Files
xTestFrameworkClient/src/app/views/v-page/v-page.component.ts
T

1002 lines
23 KiB
TypeScript

import {
Input,
Inject,
Output,
NgZone,
Renderer2,
Component,
ViewChild,
ElementRef,
TemplateRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XColor,
nameof,
XLocale,
notValue,
formatDate,
XResourceIDs,
XPickerColumn,
XStandardType,
XOneOrManyType,
XBehaviourEvent,
XColorIdentifier,
XModalButtonRole,
getDateFormatString,
XPickerColumnOption,
XLanguageDescriptor,
isNullOrEmptyString,
XBehaviourEventType,
XColorWithBrightness,
} from 'x-framework-core';
import {
XMenuSlot,
XLogoSlot,
XBackSlot,
XSideType,
XSideSlot,
XTitleSlot,
XIconNames,
XMenuState,
XContentSlot,
XPageContainer,
XPageComponent,
XPopoverOption,
XPopoverService,
XNavigatorListItem,
XBackSlotIdentifier,
XLogoSlotIdentifier,
XMenuSlotIdentifier,
XPageSizeIdentifier,
XSideSlotIdentifier,
XSideTypeIdentifier,
XTitleSlotIdentifier,
XContentSlotIdentifier,
XNavigatorStartupState,
} from 'x-framework-components';
import {
XUserProfileDto,
isDefaultUserProfileDto,
} from 'x-framework-identity-sdk';
import { map } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs';
import { MenuController } from '@ionic/angular';
import { X_CONFIG } from 'src/app/config/x.config';
import { NavigationExtras } from '@angular/router';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/overlay';
import {
VBasePageAction,
VBasePageActionModel,
} from '../v-page-presenter/v-page-presenter.typings';
import { XAppState } from './../typings/x.state.enum';
import { XManagerService } from 'x-framework-services';
import { isNullOrUndefined, hasChild } from 'x-framework-core';
import { getNavPages, Pages } from 'src/app/config/page.config';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { VUserPresentType } from '../account/v-user/v-user.typings';
import { SharedService } from 'src/app/shared/services/shared.service';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-page',
templateUrl: './v-page.component.html',
styleUrls: ['./v-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VPageComponent extends XPageComponent {
//
//#region Props ...
//
isMobileUi$ = this.managerService.isMobileUi$;
isNotMobileUi$ = this.managerService.isNotMobileUi$;
isAuthenticated$ = this.sharedService.isAuthenticated$;
//
readonly IconNames = Object.assign({}, XIconNames);
readonly AppResourceIDs = Object.assign({}, AppResourceIDs);
readonly ColorNames = Object.assign({}, XColorWithBrightness);
readonly UserPresentTypes = Object.assign({}, VUserPresentType);
readonly NavigatorStartupStates = Object.assign({}, XNavigatorStartupState);
//
//#region CaptureMode ...
@Input()
enableCaptureMode: XStandardType<boolean> = false;
@Input()
downloadCapturedImage: XStandardType<boolean> = true;
@Input()
captureTopOffset: XStandardType<number> = 0;
@Input()
captureBottomOffset: XStandardType<number> = 0;
@Output()
imageCaptured = new EventEmitter<string | Blob>();
//#endregion
//
//#region Page Props ...
//
//#region Toolbar Content ...
@Input()
showToolbarContent = true;
private TOOLBAR_CONTENT_TEMPLATE: TemplateRef<any>;
/**
* the toolbar content ...
*/
@ViewChild('toolbarContentTemplateRef', { static: false })
set toolbarContentTemplateRef(v: TemplateRef<any>) {
//
if (isNullOrUndefined(v)) {
return;
}
//
this.TOOLBAR_CONTENT_TEMPLATE = v;
}
get toolbarContentTemplateRef() {
return this.TOOLBAR_CONTENT_TEMPLATE;
}
//#endregion
//
//#region Toolbar End Slot ...
@Input()
showToolbarEndSlot = true;
//#endregion
//
@Input()
color: XStandardType<XColorIdentifier> = 'light-shade';
@Input()
cssClass: XStandardType<XOneOrManyType<string>>;
//#endregion
//
//#region Toolbar Props ...
//
//#region Toolbar Boundary ...
//
@Input()
showToolbar: XStandardType<boolean> = true;
//
@Input()
toolbarColor: XStandardType<XColor> = XColor.Primary;
//#endregion
//
//#region Toolbar ProgressBar ...
@Input()
toolbarProgressBarColor: XStandardType<XColor> = XColor.Warning;
@Input()
toolbarShowProgressBar: XStandardType<boolean> = false;
//#endregion
//
//#region Toolbar Title and SubTitle ...
//
// Toolbar Title ...
@Input()
toolbarTitleSlot: XStandardType<XTitleSlotIdentifier> = XTitleSlot.End;
@Input()
toolbarShowTitle: XStandardType<boolean> = true;
@Input()
toolbarTitle: XStandardType<string> = super.resourceProvider(
this.ResourceIDs.app_name,
);
//#endregion
//
//#region Toolbar Logo ...
@Input()
toolbarLogoSlot: XStandardType<XLogoSlotIdentifier> = XLogoSlot.End;
@Input()
toolbarShowLogo: XStandardType<boolean> = true;
@Input()
toolbarLogoUrl: XStandardType<string> = './assets/image/logo.png';
//#endregion
//
//#region Toolbar Back ...
@Input()
toolbarBackSlot: XStandardType<XBackSlotIdentifier> = XBackSlot.Start;
@Input()
toolbarShowBack: XStandardType<boolean> = false;
@Input()
toolbarHasBack: XStandardType<boolean> = true;
@Input()
toolbarDefaultHref: string;
//#endregion
//
//#region Toolbar Menu ...
@Input()
toolbarMenuSlot: XStandardType<XMenuSlotIdentifier> = XMenuSlot.Start;
//
@Input()
menuState: XStandardType<XMenuState>;
//
@Input()
toolbarShowMenu: XStandardType<boolean> = true;
//
@Input()
toolbarHasMenu: XStandardType<boolean> = true;
@Input()
toolbarAutoHideMenu: XStandardType<boolean> = true;
//#endregion
//
//#region Toolbar Templates ...
@Input()
toolbarContentSlot: XStandardType<XContentSlotIdentifier> =
XContentSlot.Start;
//#endregion
//#endregion
//
//#region Scrolling Handlers ...
@Input()
provideScrollEvents: XStandardType<boolean> = true;
@Input()
scrollBehaviour: XOneOrManyType<string> = ['x-fab'];
@Input()
scrollStartClasses: XOneOrManyType<string> = 'start-scroll';
@Input()
scrollEndClasses: XOneOrManyType<string> = 'end-scroll';
@Input()
scrollEventActionProvider: Subject<XBehaviourEventType> =
new Subject<XBehaviourEventType>();
//#endregion
//
//#region Footer Props ...
//
@Input()
footerColor: XStandardType<XColorIdentifier> = XColor.Primary;
//#endregion
//
//#region Side Props ...
//
@Input()
toggleMenuWhen: XStandardType<XPageSizeIdentifier> = 'md';
@Input()
hasSide: XStandardType<boolean> = true;
@Input()
sideSlot: XStandardType<XSideSlotIdentifier> = XSideSlot.Start;
@Input()
sideType: XStandardType<XSideTypeIdentifier> = XSideType.Overlay;
@Input()
sideColor: XStandardType<XColorIdentifier> = XColor.Primary;
//#endregion
//
navPages: XNavigatorListItem[] = [];
//#endregion
//
//#region Constructor ...
public constructor(
public zone: NgZone,
@Inject(X_CONFIG)
public config: XConfig,
public element: ElementRef,
public renderer: Renderer2,
public ruler: ViewportRuler,
public sharedService: SharedService,
public menuController: MenuController,
public managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: XPageContainer,
) {
super(
config,
managerService,
element,
renderer,
ruler,
zone,
changeDetector,
menuController,
propertyProvider,
);
//
this.isMobileUi$ = this.managerService.isMobileUi$;
this.isNotMobileUi$ = notValue(this.isMobileUi$);
}
//#endregion
//
//#region LifeCycle ...
onInit() {
super.onInit();
//
this.menuState = this.isMobileUi$.pipe(
map((isMobile) => (isMobile ? XMenuState.WillClose : XMenuState.Opened)),
);
}
async afterViewInit() {
super.afterViewInit();
//
const showToolbar = await this.getValueAsync(this.showToolbar);
if (showToolbar) {
this.detectChanges();
}
//
this.managerService.dispatchResizeEvent();
}
afterViewChecked() {
super.afterViewChecked();
//
this.changeDetector.detectChanges();
}
async onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
const isHasSideChanged = changeKeys.includes(
nameof<VPageComponent>('hasSide'),
);
if (isHasSideChanged) {
await this.handleHasSideEffect();
}
//
const isToolbarHasMenuChanged = changeKeys.includes(
nameof<VPageComponent>('toolbarHasMenu'),
);
const isToolbarShowMenuChanged = changeKeys.includes(
nameof<VPageComponent>('toolbarShowMenu'),
);
if (isToolbarHasMenuChanged || isToolbarShowMenuChanged) {
this.detectChanges();
}
//
let isChanged = changeKeys.includes(
nameof<VPageComponent>('actionProvider'),
);
if (isChanged) {
this.subscribeActionProvider();
}
//
isChanged = changeKeys.includes(
nameof<VPageComponent>('toolbarShowProgressBar'),
);
if (isChanged) {
//
this.detectChanges();
this.actionProvider.next({ action: VBasePageAction.RefreshContainer });
}
}
//#endregion
//
//#region Register Handlers ...
registerViewHandlers() {
super.registerViewHandlers();
//
//#region User Handlers ...
this.prepareNavPages(null).then(() => {
this.detectChanges();
});
//
// tslint:disable-next-line: deprecation
this.applyTakeUntilWrapper(this.sharedService.user$).subscribe(
async (user) => {
//
if (isNullOrUndefined(user) || isDefaultUserProfileDto(user)) {
return;
}
//
await this.prepareNavPages(user);
},
);
//#endregion
//
//#region Register ManagerService Loading Handler ...
// tslint:disable-next-line: deprecation
this.managerService.isLoading$.subscribe((loading) => {
this.toolbarShowProgressBar = loading;
});
//#endregion
//
//#region Register Scrolled Event Listener ...
this.applyTakeUntilWrapper(this.scrolledEvent.asObservable()).subscribe(
async (event) => {
await this.onScrolledEvent(event);
},
);
//#endregion
}
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region Handlers ...
//#endregion
//
//#region UI Providers ...
canShowUser(user: XUserProfileDto) {
//
const result = !isDefaultUserProfileDto(user);
return result;
}
//#endregion
//
//#region UI Handlers ...
async handleNavigatorItemSelected(event: XNavigatorListItem) {
//
if (!event) {
return;
}
//
// Check for Logout ...
if (event.id && event.id.includes('logout')) {
await this.handleLogout();
}
}
async handleMoreButtonClicked(event: any) {
//
const onlyIcon = false;
const isLoggedIn = await this.sharedService.authService.isLoggedIn();
const currentRoute = decodeURI(
decodeURIComponent(
this.managerService.getFullUrl(this.managerService.getRoute()),
),
);
//
const options: XPopoverOption[] = [];
//
// Check Landing Page ...
const landingPageUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.Landing),
);
const isInLandingPage = currentRoute == landingPageUrl;
if (!isInLandingPage) {
//
options.push({
id: 'go_landing_page',
icon: XIconNames.locate,
title: AppResourceIDs.landing_page,
color: XColor.Tertiary,
slot: 'start',
onlyIcon,
handler: async () => {
await this.managerService.navigateByPageReplace(Pages.Landing);
},
});
}
//
// Landing Page ...
// const landingPageUrl = this.managerService.getFullUrl(
// this.managerService.getPageRoute(Pages.Landing)
// );
// const isInLandingPage = currentRoute === landingPageUrl;
// if (!isInLandingPage) {
// //
// if (!isNullOrUndefined(this.config.defaultLandingPage)) {
// options.push({
// id: 'go_landing',
// icon: XIconNames.refresh,
// title: AppResourceIDs.landing_page_title,
// color: XColor.Tertiary,
// slot: 'start',
// onlyIcon,
// handler: async () => {
// await this.managerService.navigateByPageReplace(Pages.Landing);
// },
// });
// }
// }
//
// Content Explorer Page ...
// const contentExplorerPageUrl = this.managerService.getFullUrl(
// this.managerService.getPageRoute(Pages.ContentExplorer)
// );
// const isInContentExplorerPage = currentRoute === contentExplorerPageUrl;
// if (!isInContentExplorerPage) {
// //
// options.push({
// id: 'go_content_explorer',
// icon: XIconNames.posts,
// title: AppResourceIDs.content_explorer_page_title,
// color: XColor.Tertiary,
// slot: 'start',
// onlyIcon,
// handler: async () => {
// await this.managerService.navigateByPageReplace(
// Pages.ContentExplorer
// );
// },
// });
// }
//
// Dashboard Page ...
if (isLoggedIn) {
//
const startupUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.Startup),
);
const landingUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.Landing),
);
const registerUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.Register),
);
const notAuthorizedUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.NotAuthorized),
);
const errorUrl = this.managerService.getFullUrl(
this.managerService.getPageRoute(Pages.Error),
);
const isInOpenPages =
currentRoute === errorUrl ||
currentRoute === startupUrl ||
currentRoute === landingUrl ||
currentRoute === registerUrl ||
currentRoute === notAuthorizedUrl;
//
if (isInOpenPages) {
options.push({
id: 'go_dashboard',
icon: XIconNames.home,
title: AppResourceIDs.management_panel,
color: XColor.Tertiary,
slot: 'start',
onlyIcon,
handler: async () => {
await this.managerService.navigateByPageReplace(Pages.Home);
},
});
}
}
//
// Change Language ...
options.push({
id: 'change_language',
icon: XIconNames.language,
title: XResourceIDs.language,
color: XColor.Tertiary,
slot: 'start',
onlyIcon,
handler: async () => {
await this.handleChangeLocale();
},
});
//
// CaptureMode ...
// if (isLoggedIn) {
// const isCaptureModeEnabled = await this.getValueAsync(
// this.enableCaptureMode
// );
// if (isCaptureModeEnabled) {
// //
// // Capture Mode ...
// options.push({
// id: 'disable_capture_mode',
// icon: XIconNames.crop,
// title: XResourceIDs.disable_capture_mode,
// color: XColor.Danger,
// slot: 'start',
// onlyIcon,
// handler: async () => {
// //
// this.enableCaptureMode = false;
// this.detectChanges();
// },
// });
// } else {
// //
// // Capture Mode ...
// options.push({
// id: 'enable_capture_mode',
// icon: XIconNames.crop,
// title: XResourceIDs.enable_capture_mode,
// color: XColor.Tertiary,
// slot: 'start',
// onlyIcon,
// handler: async () => {
// //
// this.enableCaptureMode = true;
// this.detectChanges();
// },
// });
// }
// }
//
// Theme Manager ...
// if (isLoggedIn) {
// options.push({
// id: 'theme_manager',
// icon: XIconNames.color_palette,
// title: AppResourceIDs.theme_manager_page_title,
// color: XColor.Tertiary,
// slot: 'start',
// onlyIcon,
// handler: async () => {
// await this.managerService.navigateByPageReplace(Pages.ThemeManager);
// },
// });
// }
//
// Login/Logout/Dashboard ...
if (isLoggedIn) {
//
// Logout ...
options.push({
id: 'logout',
icon: XIconNames.logout,
title: AppResourceIDs.logout,
color: XColor.Danger,
slot: 'start',
onlyIcon,
handler: async () => {
await this.handleLogout();
},
});
} else {
//
options.push({
id: 'login',
icon: XIconNames.login,
title: AppResourceIDs.login,
color: XColor.Tertiary,
slot: 'start',
onlyIcon,
handler: async () => {
await this.managerService.navigateByPageReplace(Pages.Login);
},
});
}
//
// Present Context Menu ...
await this.popoverService.presentContextMenu(options, event);
}
async handleImageCaptured(image: string | Blob) {
//
if (image) {
this.imageCaptured.emit(image);
}
//
this.enableCaptureMode = false;
this.detectChanges();
}
async handleUserSelected(user: XUserProfileDto) {
//
const navigationExtras: NavigationExtras = {
state: {},
};
navigationExtras.state[XAppState.UserProfile] = user;
//
await this.managerService.navigateByPageReplace(
Pages.ProfileView,
navigationExtras,
);
}
//#endregion
//
//#region Provided Actions ...
async presentPageLockDialog() {
return new Promise<boolean>(async (res) => {
await this.managerService.dialogService.presentActionModal({
header: XResourceIDs.locked_msg,
buttons: [],
keyboardClose: true,
backdropDismiss: true,
hasNo: true,
noRole: XModalButtonRole.Destructive,
noHandler: () => {
res(false);
},
hasYes: true,
yesRole: XModalButtonRole.Selected,
yesHandler: () => {
res(true);
},
});
});
}
applyDefaultDate(value: Date) {
//
let result = '';
//
if (!isNullOrUndefined(value)) {
result = formatDate(
value,
this.managerService.currentLocale,
getDateFormatString(this.managerService.currentLocale),
true,
);
}
//
return result;
}
/**
* do LockBreak action ...
*
* @param action a callback function ...
*/
async doLockBreakAction(action: (payload?: any) => void) {
//
// Check is Locked or not ...
if (this.lock) {
//
// if Locked, Present a Dialog for Lock Break ...
await this.managerService.dialogService.presentAreYouSureYesNoDialog(
async () => {
//
// if User Confirmed Lock Break, do Action ...
action();
},
);
} else {
//
// do Action if there isn't any Lock ...
action();
}
}
/**
* do an Action which required to confirmed by user ...
*
* @param action
*/
async doConfirmedAction(action: (payload?: any) => void) {
await this.managerService.dialogService.presentAreYouSureYesNoDialog(
async () => {
//
// if User Confirmed Lock Break, do Action ...
action();
},
);
}
//#endregion
//
//#region Protected ...
//#endregion
//
//#region Overrides ...
//#endregion
//
//#region Abstraction ...
/**
* Calls When an Scroll Event Happens ...
*
* @param event
*/
async onScrolledEvent(event: XBehaviourEvent) {}
//#endregion
//
//#region Action Provider ...
@Input()
public actionProvider: Subject<VBasePageActionModel> =
new Subject<VBasePageActionModel>();
private ACTION_PROVIDER_SUBSCRIPTION: Subscription = undefined;
public unsubscribeActionProvider() {
//
if (isNullOrUndefined(this.ACTION_PROVIDER_SUBSCRIPTION)) {
return;
}
//
this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.ACTION_PROVIDER_SUBSCRIPTION = undefined;
}
public subscribeActionProvider() {
//
this.unsubscribeActionProvider();
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
.asObservable()
.subscribe((res) => {
//
if (isNullOrUndefined(res) || isNullOrEmptyString(res.action)) {
return;
}
//
if (
res.action == VBasePageAction.Refresh ||
res.action == VBasePageAction.RefreshContainer
) {
this.detectChanges();
}
});
}
//#endregion
//
//#region Private ...
private async handleHasSideEffect() {
//
const hasSide = await this.getValueAsync(this.hasSide);
if (!hasSide) {
//
this.toolbarHasMenu = false;
this.toolbarShowMenu = false;
this.toggleMenuWhen = '';
}
}
private async handleChangeLocale() {
//
const currentLocale = this.managerService.currentLocale;
const availableLanguages = this.config.availableLanguages.map(
(ai: XLanguageDescriptor) => {
return {
name: ai.name,
locale: ai.locale,
};
},
);
if (!hasChild(availableLanguages)) {
return;
}
//
const localOptions: XPickerColumnOption[] = availableLanguages.map((al) => {
//
const op: XPickerColumnOption = {
text: al.name,
value: al.locale,
disabled: al.locale === currentLocale,
};
//
return op;
});
const localColumn: XPickerColumn = {
name: 'locales',
options: localOptions,
};
//
await this.managerService.dialogService.presentPicker({
buttons: [
{
text: XResourceIDs.ok,
role: XModalButtonRole.Selected,
cssClass: ['btn-selected'],
handler: async (value) => {
//
const selectedLocale: XLocale = value?.locales?.value;
if (!selectedLocale) {
return;
}
//
await this.managerService.settingsService.changeLocale(
selectedLocale,
true,
);
},
},
{
text: XResourceIDs.cancel,
role: XModalButtonRole.Cancel,
cssClass: ['btn-cancel'],
handler: () => {},
},
],
columns: [localColumn],
});
}
private async handleLogout() {
//
await this.managerService.dialogService.presentAreYouSureYesNoDialog(
async () => {
await this.managerService.navigateByPageReplace(Pages.Logout);
},
);
}
private async prepareNavPages(user: XUserProfileDto) {
//
this.navPages = [];
//
if (!user) {
this.navPages = [...(await getNavPages(null))];
this.detectChanges();
return;
}
//
const roleInfo = await this.getValueAsync(this.sharedService.userRoleInfo$);
if (!roleInfo) {
this.navPages = [];
this.detectChanges();
return;
}
//
this.navPages = [...(await getNavPages(roleInfo))];
//
this.detectChanges();
}
//#endregion
}