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 = false; @Input() downloadCapturedImage: XStandardType = true; @Input() captureTopOffset: XStandardType = 0; @Input() captureBottomOffset: XStandardType = 0; @Output() imageCaptured = new EventEmitter(); //#endregion // //#region Page Props ... // //#region Toolbar Content ... @Input() showToolbarContent = true; private TOOLBAR_CONTENT_TEMPLATE: TemplateRef; /** * the toolbar content ... */ @ViewChild('toolbarContentTemplateRef', { static: false }) set toolbarContentTemplateRef(v: TemplateRef) { // 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 = 'light-shade'; @Input() cssClass: XStandardType>; //#endregion // //#region Toolbar Props ... // //#region Toolbar Boundary ... // @Input() showToolbar: XStandardType = true; // @Input() toolbarColor: XStandardType = XColor.Primary; //#endregion // //#region Toolbar ProgressBar ... @Input() toolbarProgressBarColor: XStandardType = XColor.Warning; @Input() toolbarShowProgressBar: XStandardType = false; //#endregion // //#region Toolbar Title and SubTitle ... // // Toolbar Title ... @Input() toolbarTitleSlot: XStandardType = XTitleSlot.End; @Input() toolbarShowTitle: XStandardType = true; @Input() toolbarTitle: XStandardType = super.resourceProvider( this.ResourceIDs.app_name, ); //#endregion // //#region Toolbar Logo ... @Input() toolbarLogoSlot: XStandardType = XLogoSlot.End; @Input() toolbarShowLogo: XStandardType = true; @Input() toolbarLogoUrl: XStandardType = './assets/image/logo.png'; //#endregion // //#region Toolbar Back ... @Input() toolbarBackSlot: XStandardType = XBackSlot.Start; @Input() toolbarShowBack: XStandardType = false; @Input() toolbarHasBack: XStandardType = true; @Input() toolbarDefaultHref: string; //#endregion // //#region Toolbar Menu ... @Input() toolbarMenuSlot: XStandardType = XMenuSlot.Start; // @Input() menuState: XStandardType; // @Input() toolbarShowMenu: XStandardType = true; // @Input() toolbarHasMenu: XStandardType = true; @Input() toolbarAutoHideMenu: XStandardType = true; //#endregion // //#region Toolbar Templates ... @Input() toolbarContentSlot: XStandardType = XContentSlot.Start; //#endregion //#endregion // //#region Scrolling Handlers ... @Input() provideScrollEvents: XStandardType = true; @Input() scrollBehaviour: XOneOrManyType = ['x-fab']; @Input() scrollStartClasses: XOneOrManyType = 'start-scroll'; @Input() scrollEndClasses: XOneOrManyType = 'end-scroll'; @Input() scrollEventActionProvider: Subject = new Subject(); //#endregion // //#region Footer Props ... // @Input() footerColor: XStandardType = XColor.Primary; //#endregion // //#region Side Props ... // @Input() toggleMenuWhen: XStandardType = 'md'; @Input() hasSide: XStandardType = true; @Input() sideSlot: XStandardType = XSideSlot.Start; @Input() sideType: XStandardType = XSideType.Overlay; @Input() sideColor: XStandardType = 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('hasSide'), ); if (isHasSideChanged) { await this.handleHasSideEffect(); } // const isToolbarHasMenuChanged = changeKeys.includes( nameof('toolbarHasMenu'), ); const isToolbarShowMenuChanged = changeKeys.includes( nameof('toolbarShowMenu'), ); if (isToolbarHasMenuChanged || isToolbarShowMenuChanged) { this.detectChanges(); } // let isChanged = changeKeys.includes( nameof('actionProvider'), ); if (isChanged) { this.subscribeActionProvider(); } // isChanged = changeKeys.includes( nameof('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(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 = new Subject(); 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 }