src/app/core/store/page/page.state.ts
General page global state
NgxsImmutableDataRepository
Properties |
|
Methods |
Accessors |
constructor(globalConfig: GlobalConfigState<GlobalConfig>, reg: RegistrationState)
|
||||||||||||
Defined in src/app/core/store/page/page.state.ts:84
|
||||||||||||
Creates an instance of page state.
Parameters :
|
cancelRegistration |
cancelRegistration()
|
Defined in src/app/core/store/page/page.state.ts:122
|
Returns :
void
|
clearHasChanges |
clearHasChanges()
|
Decorators :
@DataAction()
|
Defined in src/app/core/store/page/page.state.ts:205
|
Returns :
void
|
isOrcidValid |
isOrcidValid()
|
Defined in src/app/core/store/page/page.state.ts:243
|
Checks if current orcid value is in the valid format
Returns :
boolean
true if orcid valid or blank |
ngxsOnInit |
ngxsOnInit()
|
Defined in src/app/core/store/page/page.state.ts:101
|
Initializes this service.
Returns :
void
|
registrationStarted |
registrationStarted()
|
Decorators :
@DataAction()
|
Defined in src/app/core/store/page/page.state.ts:184
|
Sets registrationStarted to true (when initial registration modal is closed)
Returns :
void
|
setHasChanges |
setHasChanges()
|
Decorators :
@DataAction()
|
Defined in src/app/core/store/page/page.state.ts:193
|
Returns :
void
|
setOrcidId | ||||||||
setOrcidId(id?: string)
|
||||||||
Decorators :
@DataAction()
|
||||||||
Defined in src/app/core/store/page/page.state.ts:167
|
||||||||
Saves ORCID id as URI Sets orcidValid to true if blank, otherwise set to true if valid
Parameters :
Returns :
void
|
setUseCancelRegistrationCallback | ||||||
setUseCancelRegistrationCallback(use: boolean)
|
||||||
Decorators :
@DataAction()
|
||||||
Defined in src/app/core/store/page/page.state.ts:139
|
||||||
Parameters :
Returns :
void
|
setUserName | ||||||||
setUserName(name: Pick<Person | "firstName" | "middleName" | "lastName">)
|
||||||||
Decorators :
@DataAction()
|
||||||||
Defined in src/app/core/store/page/page.state.ts:149
|
||||||||
Sets the name of the active user.
Parameters :
Returns :
void
|
uriToOrcid | ||||||||
uriToOrcid(uri?: string)
|
||||||||
Defined in src/app/core/store/page/page.state.ts:253
|
||||||||
Converts orcid URI to a regular orcid value
Parameters :
Returns :
string
orcid id |
Readonly orcidValid$ |
Default value : this.state$.pipe(map((x) => x?.orcidValid))
|
Defined in src/app/core/store/page/page.state.ts:66
|
Readonly organOptions$ |
Default value : this.state$.pipe(map((x) => x?.organOptions))
|
Defined in src/app/core/store/page/page.state.ts:65
|
Readonly registrationCallbackSet$ |
Default value : this.state$.pipe(map((x) => x?.registrationCallbackSet))
|
Defined in src/app/core/store/page/page.state.ts:64
|
Readonly registrationStarted$ |
Default value : this.state$.pipe(pluckUnique('registrationStarted'))
|
Defined in src/app/core/store/page/page.state.ts:62
|
registrationStarted observable |
Readonly useCancelRegistrationCallback$ |
Default value : this.state$.pipe(map((x) => x?.useCancelRegistrationCallback))
|
Defined in src/app/core/store/page/page.state.ts:63
|
Readonly user$ |
Default value : this.state$.pipe(map((x) => x?.user))
|
Defined in src/app/core/store/page/page.state.ts:60
|
Active user observable |
skipConfirmation$ |
getskipConfirmation$()
|
Defined in src/app/core/store/page/page.state.ts:69
|
globalSkipConfirmation$ |
getglobalSkipConfirmation$()
|
Defined in src/app/core/store/page/page.state.ts:74
|
hasChanges$ |
gethasChanges$()
|
Defined in src/app/core/store/page/page.state.ts:82
|
import { Computed, DataAction, StateRepository } from '@angular-ru/ngxs/decorators';
import { NgxsImmutableDataRepository } from '@angular-ru/ngxs/repositories';
import { Injectable } from '@angular/core';
import { State } from '@ngxs/store';
import { iif, patch } from '@ngxs/store/operators';
import { GlobalConfigState, OrganInfo } from 'ccf-shared';
import { pluckUnique } from 'ccf-shared/rxjs-ext/operators';
import { Observable, combineLatest } from 'rxjs';
import { distinctUntilChanged, filter, map, tap, withLatestFrom } from 'rxjs/operators';
import { environment } from '../../../../environments/environment';
import { GlobalConfig } from '../../services/config/config';
import { RegistrationState } from '../registration/registration.state';
/** A record with information about a single person */
export interface Person {
firstName: string;
middleName?: string;
lastName: string;
orcidId?: string;
}
/** Page state model */
export interface PageStateModel {
/** Active user */
user: Person;
/** Whether or not the initial registration modal has been closed */
registrationStarted: boolean;
useCancelRegistrationCallback: boolean;
registrationCallbackSet: boolean;
skipConfirmation: boolean;
hasChanges: boolean;
organOptions?: OrganInfo[];
orcidValid: boolean;
}
/**
* General page global state
*/
@StateRepository()
@State<PageStateModel>({
name: 'page',
defaults: {
user: {
firstName: '',
lastName: '',
},
registrationStarted: false,
useCancelRegistrationCallback: false,
registrationCallbackSet: false,
skipConfirmation: true,
hasChanges: false,
organOptions: [],
orcidValid: false,
},
})
@Injectable()
export class PageState extends NgxsImmutableDataRepository<PageStateModel> {
/** Active user observable */
readonly user$ = this.state$.pipe(map((x) => x?.user));
/** registrationStarted observable */
readonly registrationStarted$ = this.state$.pipe(pluckUnique('registrationStarted'));
readonly useCancelRegistrationCallback$ = this.state$.pipe(map((x) => x?.useCancelRegistrationCallback));
readonly registrationCallbackSet$ = this.state$.pipe(map((x) => x?.registrationCallbackSet));
readonly organOptions$ = this.state$.pipe(map((x) => x?.organOptions));
readonly orcidValid$ = this.state$.pipe(map((x) => x?.orcidValid));
@Computed()
get skipConfirmation$(): Observable<boolean> {
return this.state$.pipe(pluckUnique('skipConfirmation'));
}
@Computed()
get globalSkipConfirmation$(): Observable<boolean> {
return this.globalConfig.getOption('skipUnsavedChangesConfirmation').pipe(
map((value) => value ?? environment.skipUnsavedChangesConfirmation),
distinctUntilChanged(),
);
}
@Computed()
get hasChanges$(): Observable<boolean> {
return this.state$.pipe(pluckUnique('hasChanges'));
}
/**
* Creates an instance of page state.
*
* @param globalConfig The global configuration
*/
constructor(
private readonly globalConfig: GlobalConfigState<GlobalConfig>,
private readonly reg: RegistrationState,
) {
super();
}
/**
* Initializes this service.
*/
override ngxsOnInit(): void {
super.ngxsOnInit();
combineLatest([this.reg.state$, this.globalConfig.config$])
.pipe(
tap(([reg, config]) => {
this.setState(
patch({
registrationCallbackSet: reg.useRegistrationCallback ? !!config.register : false,
useCancelRegistrationCallback: !!config.cancelRegistration,
user: iif(!!config.user, config.user ?? { firstName: '', lastName: '' }),
registrationStarted: config.user ?? reg.initialRegistration ? true : undefined,
}),
);
}),
)
.subscribe();
this.initSkipConfirmationListeners();
}
cancelRegistration(): void {
const {
globalConfig: {
snapshot: { cancelRegistration: cancelRegistrationCallback },
},
snapshot: { useCancelRegistrationCallback, skipConfirmation },
} = this;
if (useCancelRegistrationCallback) {
// eslint-disable-next-line no-alert
if (skipConfirmation || confirm('Changes you made may not be saved.')) {
cancelRegistrationCallback?.();
}
}
}
@DataAction()
setUseCancelRegistrationCallback(use: boolean): void {
this.ctx.patchState({ useCancelRegistrationCallback: use });
}
/**
* Sets the name of the active user.
*
* @param name The first and last name
*/
@DataAction()
setUserName(name: Pick<Person, 'firstName' | 'middleName' | 'lastName'>): void {
this.ctx.setState(
patch({
user: patch({
firstName: name.firstName,
lastName: name.lastName,
middleName: name.middleName !== '' ? name.middleName : undefined,
}),
}),
);
}
/**
* Saves ORCID id as URI
* Sets orcidValid to true if blank, otherwise set to true if valid
* @param id ORCID id
*/
@DataAction()
setOrcidId(id?: string): void {
this.ctx.setState(
patch({
user: patch({
orcidId: id ? this.orcidToUri(id) : undefined,
}),
}),
);
this.ctx.patchState({
orcidValid: id ? this.isOrcidValid() : true,
});
}
/**
* Sets registrationStarted to true (when initial registration modal is closed)
*/
@DataAction()
registrationStarted(): void {
this.ctx.setState(
patch({
registrationStarted: true,
}),
);
}
@DataAction()
setHasChanges(): void {
const {
snapshot: { registrationStarted, hasChanges },
} = this;
if (registrationStarted && !hasChanges) {
this.ctx.patchState({
hasChanges: true,
});
}
}
@DataAction()
clearHasChanges(): void {
this.ctx.patchState({
hasChanges: false,
});
}
private initSkipConfirmationListeners(): void {
const updateSkipConfirmation = (skipConfirmation: boolean) => this.patchState({ skipConfirmation });
this.globalSkipConfirmation$.pipe(filter((s) => s)).subscribe(updateSkipConfirmation);
this.hasChanges$
.pipe(
withLatestFrom(this.globalSkipConfirmation$),
map(([hasChanges, skipConfirmation]) => skipConfirmation || !hasChanges),
distinctUntilChanged(),
)
.subscribe(updateSkipConfirmation);
const beforeUnloadListener = (event: BeforeUnloadEvent) => {
event.preventDefault();
event.returnValue = 'Changes you made may not be saved.';
return event.returnValue;
};
this.skipConfirmation$.subscribe((skipConfirmation) => {
if (skipConfirmation) {
removeEventListener('beforeunload', beforeUnloadListener);
} else {
addEventListener('beforeunload', beforeUnloadListener);
}
});
}
/**
* Checks if current orcid value is in the valid format
* @returns true if orcid valid or blank
*/
isOrcidValid(): boolean {
const orcId = this.uriToOrcid(this.snapshot.user.orcidId);
return !!(!orcId || orcId.match('^[a-zA-Z0-9]{4}(-[a-zA-Z0-9]{4}){3}$'));
}
/**
* Converts orcid URI to a regular orcid value
* @param uri orcid uri
* @returns orcid id
*/
uriToOrcid(uri?: string): string {
return uri ? uri.split('/').slice(-1)[0] : '';
}
/**
* Converts orcid to URI
* @param id orcid id
* @returns orcid URI
*/
private orcidToUri(id: string): string {
const idWithHyphens = id
.replace(/-/g, '')
.replace(/(.{1,4})/g, '$1-')
.slice(0, -1);
return 'https://orcid.org/' + idWithHyphens;
}
}