Files
xSaherelmWorkspace/Documents/GeneratedContext/xDashboard-xClient-20260810_005046.txt
2026-08-11 22:10:12 +03:30

57059 lines
1.4 MiB
Plaintext

### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-device\types\index.d.ts
// Type definitions for Apache Cordova Device plugin
// Project: https://github.com/apache/cordova-plugin-device
// Definitions by: Microsoft Open Technologies Inc <http://msopentech.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
//
// Copyright (c) Microsoft Open Technologies Inc
// Licensed under the MIT license
/**
* This plugin defines a global device object, which describes the device's hardware and software.
* Although the object is in the global scope, it is not available until after the deviceready event.
*/
interface Device {
/** Get the version of Cordova running on the device. */
cordova: string;
/** Indicates that Cordova initialize successfully. */
available: boolean;
/**
* The device.model returns the name of the device's model or product. The value is set
* by the device manufacturer and may be different across versions of the same product.
*/
model: string;
/** Get the device's operating system name. */
platform: string;
/** Get the device's Universally Unique Identifier (UUID). */
uuid: string;
/** Get the operating system version. */
version: string;
/** Get the device's manufacturer. */
manufacturer: string;
/** Whether the device is running on a simulator. */
isVirtual: boolean;
/** Get the device hardware serial number. */
serial: string;}
declare var device: Device;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-splashscreen\src\wp\ResolutionHelper.cs
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Microsoft.Phone.Info;
using System;
using System.Windows;
namespace WPCordovaClassLib.Cordova.Commands
{
public enum Resolutions { WVGA, WXGA, HD };
public static class ResolutionHelper
{
public static Resolutions CurrentResolution
{
get
{
switch (Application.Current.Host.Content.ScaleFactor)
{
case 100: return Resolutions.WVGA;
case 160: return Resolutions.WXGA;
case 150: return Resolutions.HD;
}
throw new InvalidOperationException("Unknown resolution");
}
}
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-splashscreen\src\wp\SplashScreen.cs
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Info;
using System.Windows.Controls.Primitives;
using System.Diagnostics;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System.Windows.Threading;
namespace WPCordovaClassLib.Cordova.Commands
{
/// <summary>
/// Listens for changes to the state of the battery on the device.
/// Currently only the "isPlugged" parameter available via native APIs.
/// </summary>
public class SplashScreen : BaseCommand
{
private Popup popup;
// Time until we dismiss the splashscreen
private int prefDelay = 3000;
// Whether we hide it by default
private bool prefAutoHide = true;
// Path to image to use
private string prefImagePath = "SplashScreenImage.jpg";
// static because autodismiss is only ever applied once, at app launch
// subsequent page loads should not cause the SplashScreen to be shown.
private static bool WasShown = false;
public SplashScreen()
{
LoadConfigPrefs();
Image SplashScreen = new Image()
{
Height = Application.Current.Host.Content.ActualHeight,
Width = Application.Current.Host.Content.ActualWidth,
Stretch = Stretch.Fill
};
var imageResource = GetSplashScreenImageResource();
if (imageResource != null)
{
BitmapImage splash_image = new BitmapImage();
splash_image.SetSource(imageResource.Stream);
SplashScreen.Source = splash_image;
}
// Instansiate the popup and set the Child property of Popup to SplashScreen
popup = new Popup() { IsOpen = false,
Child = SplashScreen,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Center
};
}
public override void OnInit()
{
// we only want to autoload on the first page load.
// but OnInit is called for every page load.
if (!SplashScreen.WasShown)
{
SplashScreen.WasShown = true;
show();
}
}
private void LoadConfigPrefs()
{
StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative));
if (streamInfo != null)
{
using (StreamReader sr = new StreamReader(streamInfo.Stream))
{
//This will Read Keys Collection for the xml file
XDocument configFile = XDocument.Parse(sr.ReadToEnd());
string configAutoHide = configFile.Descendants()
.Where(x => x.Name.LocalName == "preference")
.Where(x => (string)x.Attribute("name") == "AutoHideSplashScreen")
.Select(x => (string)x.Attribute("value"))
.FirstOrDefault();
bool bVal;
prefAutoHide = bool.TryParse(configAutoHide, out bVal) ? bVal : prefAutoHide;
string configDelay = configFile.Descendants()
.Where(x => x.Name.LocalName == "preference")
.Where(x => (string)x.Attribute("name") == "SplashScreenDelay")
.Select(x => (string)x.Attribute("value"))
.FirstOrDefault();
int nVal;
prefDelay = int.TryParse(configDelay, out nVal) ? nVal : prefDelay;
string configImage = configFile.Descendants()
.Where(x => x.Name.LocalName == "preference")
.Where(x => (string)x.Attribute("name") == "SplashScreen")
.Select(x => (string)x.Attribute("value"))
.FirstOrDefault();
if (!String.IsNullOrEmpty(configImage))
{
prefImagePath = configImage;
}
}
}
}
private StreamResourceInfo GetSplashScreenImageResource()
{
// Get the base filename for the splash screen images
string imageName = System.IO.Path.GetFileNameWithoutExtension(prefImagePath);
Uri imageUri = null;
StreamResourceInfo imageResource = null;
// First, try to get a resolution-specific splashscreen
try
{
// Determine the device's resolution
switch (ResolutionHelper.CurrentResolution)
{
case Resolutions.HD:
imageUri = new Uri(imageName + ".screen-720p.jpg", UriKind.Relative);
break;
case Resolutions.WVGA:
imageUri = new Uri(imageName + ".screen-WVGA.jpg", UriKind.Relative);
break;
case Resolutions.WXGA:
default:
imageUri = new Uri(imageName + ".screen-WXGA.jpg", UriKind.Relative);
break;
}
imageResource = Application.GetResourceStream(imageUri);
}
catch (Exception)
{
// It's OK if we didn't get a resolution-specific image
}
// Fallback to the default image name without decoration
if (imageResource == null)
{
imageUri = new Uri(prefImagePath, UriKind.Relative);
imageResource = Application.GetResourceStream(imageUri);
}
if (imageUri != null) Debug.WriteLine("INFO :: SplashScreen: using image {0}", imageUri.OriginalString);
return imageResource;
}
public void show(string options = null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (!popup.IsOpen)
{
popup.Child.Opacity = 0;
Storyboard story = new Storyboard();
DoubleAnimation animation = new DoubleAnimation()
{
From = 0.0,
To = 1.0,
Duration = new Duration(TimeSpan.FromSeconds(0.2))
};
Storyboard.SetTarget(animation, popup.Child);
Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity"));
story.Children.Add(animation);
story.Begin();
popup.IsOpen = true;
if (prefAutoHide)
{
StartAutoHideTimer();
}
}
});
}
public void hide(string options = null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (popup.IsOpen)
{
popup.Child.Opacity = 1.0;
Storyboard story = new Storyboard();
DoubleAnimation animation = new DoubleAnimation()
{
From = 1.0,
To = 0.0,
Duration = new Duration(TimeSpan.FromSeconds(0.4))
};
Storyboard.SetTarget(animation, popup.Child);
Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity"));
story.Children.Add(animation);
story.Completed += (object sender, EventArgs e) =>
{
popup.IsOpen = false;
};
story.Begin();
}
});
}
private void StartAutoHideTimer()
{
var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(prefDelay) };
timer.Tick += (object sender, EventArgs e) =>
{
hide();
timer.Stop();
};
timer.Start();
}
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-splashscreen\types\index.d.ts
// Type definitions for Apache Cordova Splashscreen plugin
// Project: https://github.com/apache/cordova-plugin-splashscreen
// Definitions by: Microsoft Open Technologies Inc <http://msopentech.com>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
//
// Copyright (c) Microsoft Open Technologies Inc
// Licensed under the MIT license.
interface Navigator {
/** This plugin displays and hides a splash screen during application launch. */
splashscreen: {
/** Dismiss the splash screen. */
hide(): void;
/** Displays the splash screen. */
show(): void;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-statusbar\src\wp\StatusBar.cs
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Microsoft.Phone.Shell;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
/*
* http://www.idev101.com/code/User_Interface/StatusBar.html
* https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/Bars.html
* https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/c/econst/UIStatusBarStyleDefault
* */
namespace WPCordovaClassLib.Cordova.Commands
{
public class StatusBar : BaseCommand
{
// returns an argb value, if the hex is only rgb, it will be full opacity
protected Color ColorFromHex(string hexString)
{
string cleanHex = hexString.Replace("#", "").Replace("0x", "");
// turn #FFF into #FFFFFF
if (cleanHex.Length == 3)
{
cleanHex = "" + cleanHex[0] + cleanHex[0] + cleanHex[1] + cleanHex[1] + cleanHex[2] + cleanHex[2];
}
// add an alpha 100% if it is missing
if (cleanHex.Length == 6)
{
cleanHex = "FF" + cleanHex;
}
int argb = Int32.Parse(cleanHex, NumberStyles.HexNumber);
Color clr = Color.FromArgb((byte)((argb & 0xff000000) >> 0x18),
(byte)((argb & 0xff0000) >> 0x10),
(byte)((argb & 0xff00) >> 8),
(byte)(argb & 0xff));
return clr;
}
public void _ready(string options)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
bool isVis = SystemTray.IsVisible;
// TODO: pass this to JS
//Debug.WriteLine("Result::" + res);
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isVis));
});
}
public void overlaysWebView(string options)
{ //exec(null, null, "StatusBar", "overlaysWebView", [doOverlay]);
// string arg = JSON.JsonHelper.Deserialize<string[]>(options)[0];
}
public void styleDefault(string options)
{ //exec(null, null, "StatusBar", "styleDefault", []);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SystemTray.ForegroundColor = Colors.Black;
});
}
public void styleLightContent(string options)
{ //exec(null, null, "StatusBar", "styleLightContent", []);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SystemTray.ForegroundColor = Colors.White;
});
}
public void styleBlackTranslucent(string options)
{ //exec(null, null, "StatusBar", "styleBlackTranslucent", []);
styleLightContent(options);
}
public void styleBlackOpaque(string options)
{ //exec(null, null, "StatusBar", "styleBlackOpaque", []);
styleLightContent(options);
}
public void backgroundColorByName(string options)
{ //exec(null, null, "StatusBar", "backgroundColorByName", [colorname]);
// this should NOT be called, js should now be using/converting color names to hex
}
public void backgroundColorByHexString(string options)
{ //exec(null, null, "StatusBar", "backgroundColorByHexString", [hexString]);
string argb = JSON.JsonHelper.Deserialize<string[]>(options)[0];
Color clr = ColorFromHex(argb);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SystemTray.Opacity = clr.A / 255.0d;
SystemTray.BackgroundColor = clr;
});
}
public void hide(string options)
{ //exec(null, null, "StatusBar", "hide", []);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SystemTray.IsVisible = false;
});
}
public void show(string options)
{ //exec(null, null, "StatusBar", "show", []);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
SystemTray.IsVisible = true;
});
}
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\plugins\cordova-plugin-statusbar\types\index.d.ts
// Type definitions for Apache Cordova StatusBar plugin
// Project: https://github.com/apache/cordova-plugin-statusbar
// Definitions by: Xinkai Chen <https://github.com/Xinkai>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/**
* Global object StatusBar.
*/
interface Window {
StatusBar: StatusBar;
}
/**
* The StatusBar object provides some functions to customize the iOS and Android StatusBar.
*/
interface StatusBar {
/**
* On iOS 7, make the statusbar overlay or not overlay the WebView.
* @param isOverlay On iOS 7, set to false to make the statusbar appear like iOS 6.
* Set the style and background color to suit using the other functions.
*/
overlaysWebView: (isOverlay: boolean) => void;
/**
* Use the default statusbar (dark text, for light backgrounds).
*/
styleDefault: () => void;
/**
* Use the lightContent statusbar (light text, for dark backgrounds).
*/
styleLightContent: () => void;
/**
* Use the blackTranslucent statusbar (light text, for dark backgrounds).
*/
styleBlackTranslucent: () => void;
/**
* Use the blackOpaque statusbar (light text, for dark backgrounds).
*/
styleBlackOpaque: () => void;
/**
* On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false,
* you can set the background color of the statusbar by color name.
* @param color Supported color names are:
* black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown
*/
backgroundColorByName: (color: string) => void;
/**
* Sets the background color of the statusbar by a hex string.
* @param color CSS shorthand properties are also supported.
* On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, you can set the background color of the statusbar by a hex string (#RRGGBB).
* On WP7 and WP8 you can also specify values as #AARRGGBB, where AA is an alpha value
*/
backgroundColorByHexString: (color: string) => void;
/**
* Hide the statusbar.
*/
hide: () => void;
/**
* Show the statusbar.
*/
show: () => void;
/**
* Read this property to see if the statusbar is visible or not.
*/
isVisible: boolean;
}
declare var StatusBar: StatusBar;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\global.d.ts
interface MediaRecorderOptions {
mimeType?: string;
audioBitsPerSecond?: number;
videoBitsPerSecond?: number;
bitsPerSecond?: number;
}
interface MediaRecorder {
readonly stream: MediaStream;
readonly mimeType: string;
readonly state: 'inactive' | 'recording' | 'paused';
readonly videoBitsPerSecond: number;
readonly audioBitsPerSecond: number;
ondataavailable: ((event: BlobEvent) => void) | null;
onerror: ((event: MediaRecorderErrorEvent) => void) | null;
onpause: (() => void) | null;
onresume: (() => void) | null;
onstart: (() => void) | null;
onstop: (() => void) | null;
start(timeslice?: number): void;
stop(): void;
pause(): void;
resume(): void;
requestData(): void;
readonly isTypeSupported: (type: string) => boolean;
}
declare var MediaRecorder: {
prototype: MediaRecorder;
new(stream: MediaStream, options?: MediaRecorderOptions): MediaRecorder;
isTypeSupported?(type: string): boolean;
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>xSaherElm</title>
<base href="/" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!-- For Clear Electron Insecure Content Error ... -->
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline' https://trustseal.e-rasaneh.ir/trustseal.js;"
/>
<link rel="manifest" href="manifest.webmanifest" />
<meta name="theme-color" content="#1976d2" />
<script type="text/javascript" src="https://trustseal.e-rasaneh.ir/trustseal.js"></script>
</head>
<body>
<!-- EResaneh -->
<div id="div_eRasanehTrustseal_85710"></div>
<script type="text/javascript">
eRasaneh_Trustseal(85710, false);
</script>
<app-root></app-root>
<noscript
>Please enable JavaScript to continue using this application.</noscript
>
</body>
</html>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\polyfills.ts
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes recent versions of Safari, Chrome (including
* Opera), Edge on the desktop, and iOS and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\typings.d.ts
interface Window {
MediaRecorder: any;
require: NodeRequire;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\app-routing.module.ts
import { NgModule } from '@angular/core';
import { HomePage } from './pages/home/home.page';
import { ErrorPage } from './pages/error/error.page';
import { AuthGuard } from 'x-framework-identity-sdk';
import { XCanDeactivateGuard } from 'x-framework-core';
import { Pages, BaseRoutes } from './config/page.config';
import { StartupPage } from './pages/startup/startup.page';
import { LandingPage } from './pages/landing/landing.page';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { NotAuthorizedPage } from './pages/not-authorized/not-authorized.page';
const routes: Routes = [
//
//#region Default ...
{
pathMatch: 'full',
path: BaseRoutes.Default,
redirectTo: Pages.Startup.baseRoute,
},
//#endregion
//
//#region Startup ...
{
component: StartupPage,
path: Pages.Startup.baseRoute,
canDeactivate: [XCanDeactivateGuard],
},
//#endregion
//
//#region Landing ...
{
component: LandingPage,
path: Pages.Landing.baseRoute,
canDeactivate: [XCanDeactivateGuard],
},
//#endregion
//
//#region Home ...
{
component: HomePage,
canActivate: [AuthGuard],
path: Pages.Home.baseRoute,
canDeactivate: [XCanDeactivateGuard],
},
//#endregion
//
//#region Webinars ...
{
path: Pages.Webinars.baseRoute,
loadChildren: () =>
import('./pages/webinars/webinars-page.module').then(
(m) => m.WebinarsPageModule
),
},
//#endregion
//
//#region Tags ...
{
path: Pages.Tags.baseRoute,
loadChildren: () =>
import('./pages/tags/tags-page.module').then(
(m) => m.TagsPageModule
),
},
//#endregion
//
//#region Files ...
{
path: Pages.Files.baseRoute,
loadChildren: () =>
import('./pages/files/files-page.module').then(
(m) => m.FilesPageModule
),
},
//#endregion
//
//#region Account ...
{
path: Pages.Account.baseRoute,
loadChildren: () =>
import('./pages/account/account.module').then((m) => m.AccountPageModule),
},
//#endregion
//
//#region Not Authorized ...
{
component: NotAuthorizedPage,
path: Pages.NotAuthorized.baseRoute,
canDeactivate: [XCanDeactivateGuard],
},
//#endregion
//
//#region Error ...
{
component: ErrorPage,
path: Pages.Error.baseRoute,
canDeactivate: [XCanDeactivateGuard],
},
//#endregion
//
//#region UnKnown Routes ...
{
pathMatch: 'full',
path: BaseRoutes.Unknown,
redirectTo: Pages.Startup.baseRoute,
},
//#endregion
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules,
scrollPositionRestoration: 'enabled',
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\app.component.html
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\app.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\app.component.ts
import { Component } from '@angular/core';
import { Platform } from '@ionic/angular';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent {
constructor(private platform: Platform) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {});
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\app.module.ts
import {
XCanDeactivateGuard,
XFrameworkCoreModule,
X_FRAMEWORK_CORE_CONFIG,
} from 'x-framework-core';
import {
UserGuard,
AgentGuard,
AdminGuard,
AdminOrAgentGuard,
XSaherElmApiSdkModule,
XPushTokenFetcherService,
X_SAHERELM_API_SDK_CONFIG,
} from 'x-saherelm-api-sdk';
import {
XManagerService,
XFrameworkServicesModule,
X_FRAMEWORK_SERVICES_CONFIG,
} from 'x-framework-services';
import {
XFrameworkComponentsModule,
X_FRAMEWORK_COMPONENTS_CONFIG,
} from 'x-framework-components';
import {
AuthGuard,
X_API_CONFIG,
NotAuthGuard,
ApiHttpInterceptorService,
XFrameworkIdentitySdkModule,
X_FRAMEWORK_IDENTITY_SDK_CONFIG,
} from 'x-framework-identity-sdk';
import {
XFrameworkPushServiceModule,
X_FRAMEWORK_PUSH_SERVICE_CONFIG,
X_FRAMEWORK_PUSH_SERVICE_TOKEN_FETCHER,
} from 'x-framework-push-service';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HomePage } from './pages/home/home.page';
import { ViewsModule } from './views/views.module';
import { XAPI_CONFIG } from './config/x-api.config';
import { ErrorPage } from './pages/error/error.page';
import { RouteReuseStrategy } from '@angular/router';
import { X_CONFIG, XCONFIG } from './config/x.config';
import { SharedModule } from './shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { environment } from '../environments/environment';
import { StartupPage } from './pages/startup/startup.page';
import { LandingPage } from './pages/landing/landing.page';
import { ServiceWorkerModule } from '@angular/service-worker';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { NotAuthorizedPage } from './pages/not-authorized/not-authorized.page';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
declarations: [
HomePage,
ErrorPage,
StartupPage,
LandingPage,
AppComponent,
NotAuthorizedPage,
],
imports: [
ViewsModule,
SharedModule,
BrowserModule,
AppRoutingModule,
XFrameworkCoreModule,
XSaherElmApiSdkModule,
IonicModule.forRoot(),
BrowserAnimationsModule,
XFrameworkServicesModule,
XFrameworkComponentsModule,
XFrameworkPushServiceModule,
XFrameworkIdentitySdkModule,
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
}),
],
providers: [
//
// Ionic Routing ...
{
provide: RouteReuseStrategy,
useClass: IonicRouteStrategy,
},
//
// Manager Service ...
XManagerService,
//
// Provide Guards ...
AuthGuard,
UserGuard,
AdminGuard,
AgentGuard,
NotAuthGuard,
AdminOrAgentGuard,
XCanDeactivateGuard,
//
//#region Configs ...
{
provide: X_FRAMEWORK_CORE_CONFIG,
useValue: XCONFIG,
},
{
provide: X_FRAMEWORK_SERVICES_CONFIG,
useValue: XCONFIG,
},
{
provide: X_FRAMEWORK_COMPONENTS_CONFIG,
useValue: XCONFIG,
},
{
provide: X_FRAMEWORK_IDENTITY_SDK_CONFIG,
useValue: XCONFIG,
},
{
provide: X_FRAMEWORK_PUSH_SERVICE_CONFIG,
useValue: XCONFIG,
},
{
provide: X_SAHERELM_API_SDK_CONFIG,
useValue: XCONFIG,
},
{
provide: X_API_CONFIG,
useValue: XAPI_CONFIG,
},
{
provide: X_CONFIG,
useValue: XCONFIG,
},
//#endregion
//
//#region Push ...
{
provide: X_FRAMEWORK_PUSH_SERVICE_TOKEN_FETCHER,
useClass: XPushTokenFetcherService,
},
//#endregion
//
//#region Register Http Interceptor ...
{
provide: HTTP_INTERCEPTORS,
useClass: ApiHttpInterceptorService,
multi: true,
},
//#endregion
],
bootstrap: [AppComponent],
})
export class AppModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\app.config.ts
import {
DefaultLocale,
AppResourceIDs,
AvailableTranslationResources,
} from './localization.config';
import {
RegistrationStep,
XFrameworkIdentitySDKConfig,
} from 'x-framework-identity-sdk';
import { Pages } from './page.config';
import { LogLevel } from '@microsoft/signalr';
import { NotificationAudioSources } from './audio.config';
import { XSaherElmApiSDKConfig } from 'x-saherelm-api-sdk';
import { XFrameworkCoreConfig, XPage } from 'x-framework-core';
import { XFrameworkServicesConfig } from 'x-framework-services';
import { XFrameworkComponentsConfig } from 'x-framework-components';
import { XFrameworkPushServiceConfig } from 'x-framework-push-service';
// tslint:disable-next-line:no-empty-interface
export interface AppConfig {
//
defaultLandingPage?: XPage;
defaultBlogPage?: XPage;
forceLogin: boolean;
//
refreshTokensDelay?: number;
//
// Content Configurations ...
seeMoreResourceId: string;
}
export type XFrameworkCoreSharedConfig = Partial<XFrameworkCoreConfig>;
export type XFrameworkServicesSharedConfig = Partial<XFrameworkServicesConfig>;
export type XFrameworkComponentsSharedConfig =
Partial<XFrameworkComponentsConfig>;
export type XFrameworkIdentitySDKSharedConfig =
Partial<XFrameworkIdentitySDKConfig>;
export type XFrameworkPushServiceSharedConfig =
Partial<XFrameworkPushServiceConfig>;
export type XSaherElmApiSDKSharedConfig = Partial<XSaherElmApiSDKConfig>;
export type XAppSharedConfig = Partial<AppConfig>;
export type XConfig = XFrameworkCoreConfig &
XFrameworkServicesConfig &
XFrameworkComponentsConfig &
XFrameworkIdentitySDKConfig &
XFrameworkPushServiceConfig &
XSaherElmApiSDKConfig &
AppConfig;
export type XSharedConfig = Partial<XConfig>;
export const xFrameworkCoreSharedConfig: XFrameworkCoreSharedConfig = {
appInstanceName: 'xMabsutClient',
appNameResource: 'app_name',
appCompanyResource: 'company',
appVersion: 'v1.1',
defaultNotificationDelay: 2000,
notificationAudioSources: NotificationAudioSources,
availableLanguages: AvailableTranslationResources,
defaultLanguage: DefaultLocale,
minAllowedImageSize: 512,
maxAllowedImageSize: 5242880,
minAllowedImageCroppableSize: 512,
allowedImageExtensions: ['.png', '.jpg', '.jpeg'],
maxProfileImageSize: 5242880,
maxProfileImagesUploadFiles: 10,
defaultCountryCode: 'IR',
defaultPageSize: 5,
pageSizes: [5, 10, 20, 40, 50, 100, 200],
defaultActionDelay: 500,
poweredValue: 'SaherElmITCenter',
secretKey: 'SaherElm@09121694056@Hadi_Khazaee_Asl@yahoo.com',
};
export const xFrameworkServicesSharedConfig: XFrameworkServicesSharedConfig = {
geoLocationConfig: {
enableHighAccuracy: true,
timeout: 5000,
},
};
export const xFrameworkComponentsSharedConfig: XFrameworkComponentsSharedConfig =
{
minAllowedFileSize: 512,
maxAllowedFileSize: 20971520,
maxAllowedSize: 966367641,
maxUploadFiles: 10,
defaultSearchDebounceTime: 500,
};
export const xFrameworkIdentitySDKSharedConfig: XFrameworkIdentitySDKSharedConfig =
{
//
// Pages,
Pages,
//
minAvailableDateConst: 100 * 365 * 24 * 60 * 60 * 1000,
maxAvailableDateConst: 18 * 365 * 24 * 60 * 60 * 1000,
//
// Registration Type ...
registrationType: [
RegistrationStep.Terms,
RegistrationStep.Info,
RegistrationStep.Avatar,
RegistrationStep.EmailConfirmation,
RegistrationStep.Finish,
],
//
debounceTime: 500,
//
nameMinLength: 3,
nameMaxLength: 20,
//
registrationOnlyWithInvitation: false,
//
userNameMinLength: 4,
userNameMaxLength: 20,
//
passwordPolicies: {
requireDigit: true,
requiredLength: 6,
requiredUniqueChars: 1,
requireLowercase: true,
requireNonAlphanumeric: true,
requireUppercase: true,
},
//
passwordMinLength: 6,
passwordMaxLength: 20,
//
verificationCodeLength: 6,
delayBetweenTwoVerificationCode: 180,
};
export const xFrameworkPushServiceSharedConfig: XFrameworkPushServiceSharedConfig =
{
//
// Push Notifications ...
hubsBaseRoute: 'hubs',
pushConnectionMaxRetry: 10,
addSupportMessageProtocol: false,
connectionLogLevel: LogLevel.None,
pushConnectionReconnectDelay: 30000,
//
// Stun Servers ...
iceServers: [
{
urls: [
'stun:stun.l.google.com:19302',
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
],
},
{
urls: ['turn:turn.saherelmhub.ir:3478'],
username: 'saherelm',
credential: 's@H@1694056',
},
],
};
export const xMabsutApiSDKSharedConfig: XSaherElmApiSDKSharedConfig = {
//
// Configure API ...
apiIdentifier: 'api',
apiVersion: 'v1.0',
//
// Push Notifications ...
pushManagerReconnectDelay: 20000,
pushServiceRoute: 'push',
pushMessageAction: 'PushMessageAsync',
};
export const xAppSharedConfig: XAppSharedConfig = {
//
defaultLandingPage: Pages.Landing,
forceLogin: false,
//
// Refresh Tokens in MilliSeconds ...
refreshTokensDelay: 1 * 15 * 1000,
//
// Contents Configurations ...
seeMoreResourceId: AppResourceIDs.see_more,
};
export const xSharedConfig: XSharedConfig = {
...xFrameworkCoreSharedConfig,
...xFrameworkServicesSharedConfig,
...xFrameworkComponentsSharedConfig,
...xFrameworkIdentitySDKSharedConfig,
...xFrameworkPushServiceSharedConfig,
...xMabsutApiSDKSharedConfig,
...xAppSharedConfig,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\audio.config.ts
import { XNotificationSound, XActionType } from 'x-framework-core';
export const NotificationAudioSources: XNotificationSound[] = [
{
type: XActionType.User,
resourcePath: './assets/audio/user_notification.wav',
},
{
type: XActionType.System,
resourcePath: './assets/audio/system_notification.wav',
},
{
type: XActionType.Capture,
resourcePath: './assets/audio/capture_notification.wav',
},
];
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\localization.config.ts
import { XLocale, XLanguageDescriptor } from 'x-framework-core';
import { TagLocalizationResources } from '../views/tags/v-tag.configs';
import { FileLocalizationsResources } from '../views/files/v-file.configs';
import { VideoLocalizationResources } from '../views/v-video/v-video.configs';
import { AccountLocalizationResources } from '../views/account/account.configs';
import { WebinarLocalizationResources } from '../views/webinar/v-webinar.configs';
import { ReferenceLocalizationResources } from '../views/v-reference/v-reference.configs';
export enum AppResourceIDs {
default_loading = 'default_loading',
english = 'english',
persian = 'persian',
actor = 'actor',
index = 'index',
title = 'title',
description = 'description',
author_id = 'author_id',
attached_on = 'attached_on',
view = 'view',
viewed_on = 'viewed_on',
check_mail = 'check_mail',
name = 'name',
type = 'type',
thumbnail = 'thumbnail',
path = 'path',
uploaded_on = 'uploaded_on',
string_resource = 'string_resource',
resource_title = 'resource_title',
add_new_terms_and_condition = 'add_new_terms_and_condition',
add_new_terms_and_condition_description = 'add_new_terms_and_condition_description',
themes = 'themes',
//
state = 'state',
enabled = 'enabled',
disabled = 'disabled',
user_id = 'user_id',
content_id = 'content_id',
bann_state = 'bann_state',
email_state = 'email_state',
phone_number_state = 'phone_number_state',
avatar_list = 'avatar_list',
logging_out = 'logging_out',
phone_number = 'phone_number',
authenticating = 'authenticating',
user_identifier = 'user_identifier',
user_identifier_description = 'user_identifier_description',
password = 'password',
new_password = 'new_password',
password_confirm = 'password_confirm',
enter_a_new_password = 'enter_a_new_password',
login = 'login',
logout = 'logout',
invite_send = 'invite_send',
change_data = 'change_data',
request_reset_password = 'request_reset_password',
request_reset_password_description = 'request_reset_password_description',
request_confirm_email = 'request_confirm_email',
request_confirm_email_description = 'request_confirm_email_description',
upload_avatar = 'upload_avatar',
confirmed = 'confirmed',
unconfirmed = 'unconfirmed',
terms_and_conditions_title = 'terms_and_conditions_title',
account_info = 'account_info',
verification_code = 'verification_code',
send_verification_code = 'send_verification_code',
registration_finished = 'registration_finished',
registration_finished_description = 'registration_finished_description',
first_name = 'first_name',
last_name = 'last_name',
user_name = 'user_name',
role = 'role',
roles = 'roles',
mobile = 'mobile',
email = 'email',
date_of_birth = 'date_of_birth',
registration_date = 'registration_date',
last_login = 'last_login',
gender = 'gender',
male = 'male',
female = 'female',
landing_page = 'landing_page',
admin_role = 'admin_role',
admin_role_description = 'admin_role_description',
responsible_manager_role = 'responsible_manager_role',
responsible_manager_role_description = 'responsible_manager_role_description',
journalist_role = 'journalist_role',
journalist_role_description = 'journalist_role_description',
chief_clerk_role = 'chief_clerk_role',
chief_clerk_role_description = 'chief_clerk_role_description',
chief_clerk_assistant_role = 'chief_clerk_assistant_role',
chief_clerk_assistant_role_description = 'chief_clerk_assistant_role_description',
actuary_role = 'actuary_role',
actuary_role_description = 'actuary_role_description',
actuary_assistant_role = 'actuary_assistant_role',
actuary_assistant_role_description = 'actuary_assistant_role_description',
reporter_role = 'reporter_role',
reporter_role_description = 'reporter_role_description',
reporter_assistant_role = 'reporter_assistant_role',
reporter_assistant_role_description = 'reporter_assistant_role_description',
agent_role = 'agent_role',
agent_role_description = 'agent_role_description',
user_role = 'user_role',
user_role_description = 'user_role_description',
friendship_state = 'friendship_state',
unknown = 'unknown',
follower = 'follower',
followers = 'followers',
following = 'following',
followings = 'followings',
friend = 'friend',
block = 'block',
unblock = 'unblock',
blocked = 'blocked',
follow = 'follow',
unfollow = 'unfollow',
unfollow_as = 'unfollow_as',
unfollow_follower = 'unfollow_follower',
unfollow_following = 'unfollow_following',
accept_request = 'accept_request',
cancel_request = 'cancel_request',
pending = 'pending',
pending_follower = 'pending_follower',
pending_following = 'pending_following',
rejected = 'rejected',
accepted = 'accepted',
min_length_error = 'min_length_error',
max_length_error = 'max_length_error',
invalid_url = 'invalid_url',
invalid_phone_number = 'invalid_phone_number',
invalid_email_address = 'invalid_email_address',
email_cant_register = 'email_cant_register',
mobile_cant_register = 'mobile_cant_register',
user_name_cant_register = 'user_name_cant_register',
email_in_use = 'email_in_use',
phone_number_in_use = 'phone_number_in_use',
passwords_not_matched = 'passwords_not_matched',
//
//#region Blockchain Views ...
theme = 'theme',
symbol = 'symbol',
chart = 'chart',
date_rage = 'date_range',
chart_type = 'chart_type',
scale_mode = 'scale_mode',
select_sheet = 'select_sheet',
display_mode = 'display_mode',
analysis_tool = 'analysis_tool',
scale_position = 'scale_position',
search_symbols = 'search_symbols',
symbol_profile = 'symbol_profile',
symbol_overview = 'symbol_overview',
values_tracking = 'values_tracking',
technical_analysis = 'technical_analysis',
cryptocurrency_market = 'cryptocurrency_market',
//#endregion
//
startup_page_title = 'startup_page_title',
startup_page_description = 'startup_page_description',
landing_page_title = 'landing_page_title',
landing_page_description = 'landing_page_description',
home_page_title = 'home_page_title',
home_page_description = 'home_page_description',
error_page_title = 'error_page_title',
error_page_description = 'error_page_description',
not_authorized_page_title = 'not_authorized_page_title',
not_authorized_page_description = 'not_authorized_page_description',
theme_manager_page_title = 'theme_manager_page_title',
theme_manager_page_description = 'theme_manager_page_description',
//
settings_page_title = 'settings_page_title',
settings_page_description = 'settings_page_description',
string_resource_page_title = 'string_resource_page_title',
string_resource_page_description = 'string_resource_page_description',
terms_and_conditions_page_title = 'terms_and_conditions_page_title',
terms_and_conditions_page_description = 'terms_and_conditions_page_description',
posts_page_title = 'posts_page_title',
posts_page_description = 'posts_page_description',
media_explorer_page_title = 'media_explorer_page_title',
media_explorert_page_description = 'media_explorert_page_description',
media_gallery_explorer_page_title = 'media_gallery_explorer_page_title',
media_gallery_explorert_page_description = 'media_gallery_explorert_page_description',
media_management_page_title = 'media_management_page_title',
media_management_page_description = 'media_management_page_description',
content_explorer_page_title = 'content_explorer_page_title',
content_explorert_page_description = 'content_explorert_page_description',
content_management_page_title = 'content_management_page_title',
content_management_page_description = 'content_management_page_description',
documents_page_title = 'documents_page_title',
documents_page_description = 'documents_page_description',
document_management_page_title = 'document_management_page_title',
document_management_page_description = 'document_management_page_description',
//
// Custom ...
at = 'at',
news = 'news',
share = 'share',
media = 'media',
medias = 'medias',
summary = 'summary',
content = 'content',
see_more = 'see_more',
contents = 'contents',
posted_by = 'posted_by',
edited_by = 'edited_by',
documents = 'documents',
posted_on = 'posted_on',
editted_on = 'editted_on',
audioinput = 'audioinput',
audiooutput = 'audiooutput',
videoinput = 'videoinput',
select_media_device = 'select_media_device',
author = 'author',
private = 'private',
public = 'public',
locales = 'locales',
created_on = 'created_on',
approver = 'approver',
approver_id = 'approver_id',
approved_on = 'approved_on',
media_gallery = 'media_gallery',
media_galleries = 'media_galleries',
upload_documents = 'upload_documents',
management_panel = 'management_panel',
connect = 'connect',
//
// Video ...
mute = 'mute',
play = 'play',
pause = 'pause',
unmute = 'unmute',
volume = 'volume',
rewind = 'rewind',
forward = 'forward',
fullscreen = 'fullscreen',
unfullscreen = 'unfullscreen',
change_volume = 'change_volume',
mic_on = 'mic_on',
mic_off = 'mic_off',
video_on = 'video_on',
call_end = 'call_end',
video_off = 'video_off',
call_request = 'call_request',
stop_record = 'stop_record',
start_record = 'start_record',
stop_share_screen = 'stop_share_screen',
start_share_screen = 'start_share_screen',
switch_to_back_camera = 'switch_to_back_camera',
switch_to_front_camera = 'switch_to_front_camera',
//
// Reference ...
reference = 'reference',
references = 'references',
provided_for = 'provided_for',
referenced_to = 'referenced_to',
//
// Account ...
account = 'account',
accounts = 'accounts',
profile = 'profile',
profiles = 'profiles',
avatar = 'avatar',
avatars = 'avatars',
register = 'register',
accept_terms = 'accept_terms',
email_confirmation = 'email_confirmation',
input_user_info = 'input_user_info',
input_user_avatar = 'input_user_avatar',
finish_registration = 'finish_registration',
account_page_title = 'account_page_title',
account_page_description = 'account_page_description',
profile_page_title = 'profile_page_title',
profile_page_description = 'profile_page_description',
login_page_title = 'login_page_title',
login_page_description = 'login_page_description',
logout_page_title = 'logout_page_title',
logout_page_description = 'logout_page_description',
register_page_title = 'register_page_title',
register_page_description = 'register_page_description',
email_confirm_page_title = 'email_confirm_page_title',
email_confirm_page_description = 'email_confirm_page_description',
phone_confirm_page_title = 'phone_confirm_page_title',
phone_confirm_page_description = 'phone_confirm_page_description',
reset_password_page_title = 'reset_password_page_title',
reset_password_page_description = 'reset_password_page_description',
change_password_page_title = 'change_password_page_title',
change_password_page_description = 'change_password_page_description',
search_profiles_page_title = 'search_profiles_page_title',
search_profiles_page_description = 'search_profiles_page_description',
//
// Tags ...
tag = 'tag',
tags = 'tags',
tags_page_title = 'tags_page_title',
tags_page_description = 'tags_page_description',
tags_management_page_title = 'tags_management_page_title',
tags_management_page_description = 'tags_management_page_description',
tags_explorer_page_title = 'tags_explorer_page_title',
tags_explorer_page_description = 'tags_explorer_page_description',
//
// Files ...
file = 'file',
files = 'files',
image = 'image',
audio = 'audio',
video = 'video',
document = 'document',
file_name = 'file_name',
thumbnail_path = 'thumbnail_path',
files_page_title = 'files_page_title',
files_page_description = 'files_page_description',
files_management_page_title = 'files_management_page_title',
files_management_page_description = 'files_management_page_description',
files_explorer_page_title = 'files_explorer_page_title',
files_explorer_page_description = 'files_explorer_page_description',
//
// Webinars ...
webinar = 'webinar',
webinars = 'webinars',
close_webinar = 'close_webinar',
webinar_owner = 'webinar_owner',
webinar_joiner = 'webinar_joiner',
new_webinar_created = 'new_webinar_created',
webinars_page_title = 'webinars_page_title',
visitor_joined_message = 'visitor_joined_message',
visitor_leaved_message = 'visitor_leaved_message',
webinar_closed_message = 'webinar_closed_message',
webinars_page_description = 'webinars_page_description',
webinars_explorer_page_title = 'webinars_explorer_page_title',
webinars_management_page_title = 'webinars_management_page_title',
webinars_explorer_page_description = 'webinars_explorer_page_description',
webinars_management_page_description = 'webinars_management_page_description',
}
export const DefaultLocale: XLocale = 'fa-IR';
export const AvailableTranslationResources: XLanguageDescriptor[] = [
{
name: 'persian',
locale: 'fa-IR',
language: 'fa',
direction: 'rtl',
resources: [
{
id: 'app_name',
value: 'ساحر علم',
},
{
id: 'company',
value: 'فن آوران ساحر علم',
},
{
id: 'id',
value: 'شناسه',
},
{
id: 'file_name',
value: 'نام فایل',
},
{
id: 'error',
value: 'خطا',
},
{
id: 'required',
value: 'الزامی است.',
},
{
id: 'loading',
value: 'بارگزاری',
},
{
id: 'username',
value: 'نام کاربری',
},
{
id: 'password',
value: 'کلمه عبور',
},
{
id: 'first_name',
value: 'نام',
},
{
id: 'last_name',
value: 'نام خانوادگی',
},
{
id: 'verified',
value: 'تایید شده',
},
{
id: 'unverified',
value: 'تایید نشده',
},
{
id: 'thumbnail',
value: 'پیش نمایش',
},
{
id: 'language',
value: 'زبان',
},
{
id: 'take_screenshot',
value: 'تصویر برداری ازمحیط',
},
{
id: 'enable_capture_mode',
value: 'فعالسازی حالت تصویر برداری',
},
{
id: 'disable_capture_mode',
value: 'غیرفعالسازی حالت تصویر برداری',
},
{
id: 'be',
value: 'باشد',
},
{
id: 'not_to_be',
value: 'نباشد',
},
{
id: 'enabled',
value: 'فعال',
},
{
id: 'disabled',
value: 'غیر فعال',
},
{
id: 'banned',
value: 'مسدود',
},
{
id: 'un_banned',
value: 'غیر مسدود',
},
{
id: 'audio',
value: 'صوت',
},
{
id: 'video',
value: 'فیلم',
},
{
id: 'cover_image',
value: 'تصویر',
},
{
id: 'image',
value: 'تصویر',
},
{
id: 'unknown',
value: 'ناشناس',
},
{
id: 'location',
value: 'موقعیت جغرافیای',
},
{
id: 'are_you_sure',
value: 'اطمینان دارید ؟',
},
{
id: 'success_msg',
value: 'عملیات با موفقیت انجام شد',
},
{
id: 'locked_msg',
value:
'برای ترک این صفحه باید فعالیت جاری را لغو کرده و یا به اتمام برسانید',
},
{
id: 'empty_content',
value: 'خالی است',
},
{
id: 'no_item',
value: 'محتوایی ثبت نشده است',
},
{
id: 'drag_drop_here',
value: 'محل رها کردن فایل',
},
{
id: 'map_dialog_title',
value: 'نمایش موقعیت مکانی',
},
{
id: 'map_dialog_select_title',
value: 'انتخاب موقعیت جغرافیایی',
},
{
id: 'color_picker_dialog_title',
value: 'انتخاب رنگ',
},
{
id: 'dismiss',
value: 'رد کردن',
},
{
id: 'accept',
value: 'می پذیرم',
},
{
id: 'ok',
value: 'بسیار خب',
},
{
id: 'save',
value: 'ذخیره',
},
{
id: 'cancel',
value: 'لغو',
},
{
id: 'retry',
value: 'سعی مجدد',
},
{
id: 'edit',
value: 'ویرایش',
},
{
id: 'reset',
value: 'باز نشاندن',
},
{
id: 'change',
value: 'تغییر',
},
{
id: 'start',
value: 'آغاز',
},
{
id: 'yes',
value: 'بله',
},
{
id: 'no',
value: 'خیر',
},
{
id: 'verify',
value: 'تایید کردن',
},
{
id: 'confirm',
value: 'تاییدیه',
},
{
id: 'crop',
value: 'برش',
},
{
id: 'clear',
value: 'پاک کردن',
},
{
id: 'set',
value: 'تنظیم',
},
{
id: 'select',
value: 'انتخاب',
},
{
id: 'send',
value: 'ارسال',
},
{
id: 'show',
value: 'نمایش دادن',
},
{
id: 'hide',
value: 'پنهان کردن',
},
{
id: 'search',
value: 'جستجو',
},
{
id: 'delete',
value: 'حذف',
},
{
id: 'clone',
value: 'نمونه گیری',
},
{
id: 'select_all',
value: 'انتخاب همه',
},
{
id: 'select_file',
value: 'انتخاب فایل(ها)',
},
{
id: 'select_all_group',
value: 'انتخاب همه موارد فیلتر شده',
},
{
id: 'deselect_all',
value: 'لغو همه انتخاب ها',
},
{
id: 'deselect_all_group',
value: 'لغو انتخاب همه موارد فیلتر شده',
},
{
id: 'inverse_selection',
value: 'انتخاب معکوس',
},
{
id: 'selected_location',
value: 'انتخاب محل',
},
{
id: 'preset_colors',
value: 'رنگ های پیش فرض',
},
{
id: 'copy_to_clipboard',
value: 'ثبت در حافظه موقت',
},
{
id: 'color',
value: 'رنگ',
},
{
id: 'colors',
value: 'رنگها',
},
{
id: 'upload',
value: 'بارگزاری',
},
{
id: 'import',
value: 'وارد کردن',
},
{
id: 'continue',
value: 'ادامه',
},
{
id: 'finish',
value: 'پایان',
},
{
id: 'enable',
value: 'فعال',
},
{
id: 'disable',
value: 'غیر فعال',
},
{
id: 'all',
value: 'همه',
},
{
id: 'new',
value: 'جدید',
},
{
id: 'add',
value: 'افزودن',
},
{
id: 'update',
value: 'بروز رسانی',
},
{
id: 'details',
value: 'جزئیات',
},
{
id: 'translation',
value: 'ترجمه',
},
{
id: 'download',
value: 'دریافت',
},
{
id: 'list',
value: 'فهرست',
},
{
id: 'options',
value: 'انتخاب ها',
},
{
id: 'descriptions',
value: 'توضیحات',
},
{
id: 'type',
value: 'نوع',
},
{
id: 'media',
value: 'رسانه',
},
{
id: 'medias',
value: 'رسانه ها',
},
{
id: 'summary',
value: 'خلاصه',
},
{
id: 'go',
value: 'رفتن',
},
{
id: 'input',
value: 'ورود',
},
{
id: 'preview',
value: 'پیش نمایش',
},
{
id: 'close',
value: 'بستن',
},
{
id: 'undo',
value: 'لغو کردن',
},
{
id: 'redo',
value: 'انجام دوباره',
},
{
id: 'refresh',
value: 'بازیابی',
},
{
id: 'submit',
value: 'ثبت',
},
{
id: 'first',
value: 'نخستین',
},
{
id: 'last',
value: 'آخرین',
},
{
id: 'previous',
value: 'قبلی',
},
{
id: 'next',
value: 'بعدی',
},
{
id: 'back',
value: 'بازگشت',
},
{
id: 'x_markdown_preview',
value: 'پیش نمایش',
},
{
id: 'x_markdown_image',
value: 'تصویر',
},
{
id: 'x_markdown_link',
value: 'پیوند',
},
{
id: 'x_markdown_ol',
value: 'فهرست ترتیبی',
},
{
id: 'x_markdown_ul',
value: 'فهرست بدون ترتیب',
},
{
id: 'x_markdown_quote',
value: 'نقل قول',
},
{
id: 'x_markdown_code',
value: 'کد منبع',
},
{
id: 'x_markdown_heading',
value: 'عنوان',
},
{
id: 'x_markdown_italic',
value: 'ایتالیک',
},
{
id: 'x_markdown_bold',
value: 'پر رنگ',
},
{
id: 'x_markdown_guide',
value: 'راهنمای نگارش',
},
{
id: 'theme',
value: 'پوسته',
},
{
id: 'theme_mode',
value: 'حالت نمایشی',
},
{
id: 'theme_dark',
value: 'تیره',
},
{
id: 'theme_light',
value: 'روشن',
},
{
id: 'theme_name',
value: 'نام پوسته',
},
{
id: 'theme_name_description',
value: 'نام پوسته را وارد کنید',
},
{
id: 'theme_clone_title',
value: 'نمونه گیری از پوسته',
},
{
id: 'input_color_title',
value: 'تعیین رنگ',
},
{
id: 'input_color_description',
value: 'مقدار رنگ را وارد کنید',
},
{
id: 'ide_mode',
value: 'نوع کد',
},
{
id: 'ide_format',
value: 'حالت دهی کد',
},
{
id: 'ex_100',
value: 'داده نا معتبر',
},
{
id: 'ex_101',
value: 'تاریخ نامعتبر',
},
{
id: 'ex_102',
value: 'درخواست اشتباه',
},
{
id: 'ex_103',
value: 'ورودی نامعتبر',
},
{
id: 'ex_104',
value: 'پیام نامعتبر',
},
{
id: 'ex_105',
value: 'نشانه نامعتبر',
},
{
id: 'ex_106',
value: 'کد اععتبارسنجی نامعتبر',
},
{
id: 'ex_107',
value: 'پست الکترونیک نامعتبر',
},
{
id: 'ex_108',
value: 'شماره تلفن همراه نامعتبر',
},
{
id: 'ex_109',
value: 'کلمه عبور نامعتبر',
},
{
id: 'ex_110',
value: 'آدرس اینترنتی نامعتبر',
},
{
id: 'ex_111',
value: 'دستگاه نامعتبر',
},
{
id: 'ex_112',
value: 'تصویر پروفایل نامعتبر',
},
{
id: 'ex_113',
value: 'فایل نامعتبر',
},
{
id: 'ex_114',
value: 'نام کاربری نامعتبر',
},
{
id: 'ex_200',
value: 'یافت نشد',
},
{
id: 'ex_201',
value: 'عدم تایید جواز',
},
{
id: 'ex_202',
value: 'سطح دسترسی نامعتبر',
},
{
id: 'ex_203',
value: 'غیر قابل نمایش',
},
{
id: 'ex_300',
value: 'حساب کاربری قفل شده است',
},
{
id: 'ex_301',
value: 'خطا در ورود',
},
{
id: 'ex_302',
value: 'کاربر قبلا ثبت نام کرده است',
},
{
id: 'ex_303',
value: 'کاربر غیرفعال است',
},
{
id: 'ex_304',
value: 'دستگاه شما قفل شده است تا : (0)',
},
{
id: 'ex_305',
value: 'امکان درخواست کد اعتبارسنجی وجود ندارد تا : (0)',
},
{
id: 'ex_306',
value: 'کلمه عبورها یکسان هستند',
},
{
id: 'ex_307',
value: 'شماره های تلفن یکسان هستند',
},
{
id: 'ex_308',
value: 'آدرس های پست الکترونیک یکسان هستند',
},
{
id: 'ex_309',
value: 'شماره تلفن قبلا ثبت شده است',
},
{
id: 'ex_310',
value: 'پست الکترونیک قبلا ثبت شده است',
},
{
id: 'ex_311',
value: 'تلفن همراه تایید نشده است',
},
{
id: 'ex_312',
value: 'پست الکترونیک تایید نشده است',
},
{
id: 'ex_313',
value: 'مطلب ممنوعه',
},
{
id: 'ex_314',
value: 'کاربر ممنوعه',
},
{
id: 'ex_400',
value: 'خطای نا شناخته',
},
{
id: 'ex_401',
value: 'مورد تکراری',
},
{
id: 'ex_402',
value: 'شکست عملیات',
},
{
id: 'ex_403',
value: 'تاریخ مصرف منقضی شده است',
},
{
id: 'ex_404',
value: 'غیر قابل اسفاده',
},
{
id: 'ex_405',
value: 'تعداد برچسب ها بیش از جد مجاز است',
},
{
id: 'ex_406',
value: 'اتمام مهلت زمانی',
},
{
id: 'ex_500',
value: 'پرونده خالی است',
},
{
id: 'ex_501',
value: 'اندازه بیش از حد مجاز',
},
{
id: 'ex_502',
value: 'گونه نامعتبر فایل',
},
{
id: 'ex_503',
value: 'تعداد رسانه ها بیش از حد مجاز است',
},
{
id: 'ex_600',
value: 'پیکربندی اشتباه',
},
{
id: 'ex_601',
value: 'خطا در پیکربندی سامانه ذخیره سازی',
},
{
id: 'ex_602',
value: 'خطا در پیکربندی سامانه ذخیره سازی',
},
{
id: 'ex_900',
value: 'تعداد فایل ها بیش از حد مجاز است',
},
{
id: 'ex_901',
value: 'مجموع اندازه فایل ها کمتر از حد مجاز است',
},
{
id: 'ex_902',
value: 'مجموع اندازه فایل ها بیش از حد مجاز است',
},
{
id: 'ex_903',
value: 'شرایط و ضوابط باید پذیرفته شوند',
},
{
id: 'ex_904',
value: 'عدم پشتیبانی از سیستم موقعیت یاب',
},
{
id: 'ex_905',
value: 'مجوز دسترسی به موقعیت خود را صادر نکرده اید',
},
{
id: 'ex_906',
value: 'دسترسی به موقعیت جغرافیایی امکان پذیر نیست',
},
{
id: 'ex_907',
value: 'عدم پشتیبانی از سیستم ذخیره سازی',
},
{
id: 'ex_908',
value: 'برای دسترسی به محتوا باید از سامانه خارج شوید',
},
{
id: 'ex_909',
value: 'زبان انتخاب شده نامعتبر است',
},
{
id: 'ex_910',
value: 'داده نامعتبر برای انتخاب کاربر',
},
{
id: 'default_loading',
value: 'در حال بارگزاری',
},
{
id: 'persian',
value: 'فارسی',
},
{
id: 'english',
value: 'انگلیسی',
},
{
id: 'actor',
value: 'بازیگر',
},
{
id: 'index',
value: 'اندیس',
},
{
id: 'title',
value: 'عنوان',
},
{
id: 'description',
value: 'توضیحات',
},
{
id: 'author_id',
value: 'شناسه ناشر',
},
{
id: 'attached_on',
value: 'تاریخ الصاق',
},
{
id: 'view',
value: 'نمایش',
},
{
id: 'viewed_on',
value: 'تاریخ دیدار',
},
{
id: 'check_mail',
value: 'صندوق پست الکترونیک خود را بررسی کنید',
},
{
id: 'name',
value: 'نام',
},
{
id: 'type',
value: 'نوع',
},
{
id: 'path',
value: 'مسیر',
},
{
id: 'uploaded_on',
value: 'زمان بارگزاری',
},
{
id: 'string_resource',
value: 'منبع متنی',
},
{
id: 'resource_title',
value: 'شناسه منبع',
},
{
id: 'add_new_terms_and_condition',
value: 'افزودن شرایط ثبت نام',
},
{
id: 'add_new_terms_and_condition_description',
value: 'افزودن ترجمه جدید از شرایط و ضوابط ثبت نام',
},
{
id: 'themes',
value: 'پوسته ها',
},
//
{
id: 'state',
value: 'وضعیت',
},
{
id: 'enabled',
value: 'فعال',
},
{
id: 'disabled',
value: 'غیر فعال',
},
{
id: 'user_id',
value: 'شناسه کاربر',
},
{
id: 'content_id',
value: 'شناسه محتوا',
},
{
id: 'bann_state',
value: 'تحریم',
},
{
id: 'email_state',
value: 'وضعیت پست الکترونیک',
},
{
id: 'phone_number_state',
value: 'وضعیت شماره تماس',
},
{
id: 'avatar_list',
value: 'فهرست تصاویر پروفایل',
},
{
id: 'logging_out',
value: 'درحال خروج از سامانه',
},
{
id: 'phone_number',
value: 'شماره تماس',
},
{
id: 'authenticating',
value: 'در حال احراز هویت',
},
{
id: 'user_identifier',
value: 'شناسه کاربر',
},
{
id: 'user_identifier_description',
value:
'شناسه کاربر می تواند نام کاربری، آدرس پست الکترونیک و یا شماره تلفن همراه کاربر باشد',
},
{
id: 'password',
value: 'کلمه عبور',
},
{
id: 'new_password',
value: 'کلمه عبور جدید',
},
{
id: 'password_confirm',
value: 'تکرار کلمه عبور',
},
{
id: 'enter_a_new_password',
value: 'کلمه عبور جدید وارد کنید',
},
{
id: 'login',
value: 'ورود',
},
{
id: 'logout',
value: 'خروج',
},
{
id: 'invite_send',
value: 'ارسال دعوتنامه',
},
{
id: 'change_data',
value: 'اصلاح داده های ورودی',
},
{
id: 'request_reset_password',
value: 'درخواست باز نشاندن کلمه عبور',
},
{
id: 'request_reset_password_description',
value: 'درخواست باز نشاندن کلمه عبور حساب کاربری',
},
{
id: 'request_confirm_email',
value: 'درخواست مجدد فعالسازی',
},
{
id: 'request_confirm_email_description',
value: 'درخواست مجدد پیوند فعالسازی حساب کاربری',
},
{
id: 'upload_avatar',
value: 'بارگزاری تصویر پروفایل',
},
{
id: 'confirmed',
value: 'تایید شده',
},
{
id: 'unconfirmed',
value: 'تایید نشده',
},
{
id: 'terms_and_conditions_title',
value: 'شرایط و ضوابط ثبت نام',
},
{
id: 'account_info',
value: 'اطلاعات حساب کاربری',
},
{
id: 'verification_code',
value: 'کد اعتبار سنجی',
},
{
id: 'send_verification_code',
value: 'ارسال کد اعتبار سنجی',
},
{
id: 'registration_finished',
value: 'پایان ثبت نام',
},
{
id: 'registration_finished_description',
value:
'در این مرحله پیوند فعال سازی حساب کاربری به پست الکترونیک شما ارسال خواهد شد.',
},
{
id: 'first_name',
value: 'نام',
},
{
id: 'last_name',
value: 'نام خانوادگی',
},
{
id: 'user_name',
value: 'نام کاربری',
},
{
id: 'role',
value: 'سطح دسترسی',
},
{
id: 'roles',
value: 'سطوح دسترسی',
},
{
id: 'mobile',
value: 'تلفن همراه',
},
{
id: 'email',
value: 'پست الکترونیک',
},
{
id: 'date_of_birth',
value: 'تاریخ تولد',
},
{
id: 'registration_date',
value: 'تاریخ عضویت',
},
{
id: 'last_login',
value: 'آخرین ورود',
},
{
id: 'gender',
value: 'جنسیت',
},
{
id: 'male',
value: 'مذکر',
},
{
id: 'female',
value: 'مونث',
},
{
id: 'landing_page',
value: 'صفحه نخست',
},
{
id: 'admin_role',
value: 'سرپرست',
},
{
id: 'admin_role_description',
value: 'ناظر و سرپرست سامانه',
},
{
id: 'responsible_manager_role',
value: 'مدیر مسئول',
},
{
id: 'responsible_manager_role_description',
value: 'مدیریت کلیه بخش های سامانه',
},
{
id: 'journalist_role',
value: 'روزنامه نگار',
},
{
id: 'journalist_role_description',
value: 'مدیریت سرویس های خبری',
},
{
id: 'chief_clerk_role',
value: 'سر دبیر',
},
{
id: 'chief_clerk_role_description',
value: 'مدیریت اخبار',
},
{
id: 'chief_clerk_assistant_role',
value: 'دستیار سر دبیر',
},
{
id: 'chief_clerk_assistant_role_description',
value: 'دستیار سردبیر',
},
{
id: 'actuary_role',
value: 'دبیر',
},
{
id: 'actuary_role_description',
value: 'هماهنگی انتشار اخبار',
},
{
id: 'actuary_assistant_role',
value: 'دستیار دبیر',
},
{
id: 'actuary_assistant_role_description',
value: 'دستیار دبیر',
},
{
id: 'reporter_role',
value: 'خبرنگار',
},
{
id: 'reporter_role_description',
value: 'فراهم کننده گزارش های خبری',
},
{
id: 'reporter_assistant_role',
value: 'دستیار خبرنگار',
},
{
id: 'reporter_assistant_role_description',
value: 'دستیار خبرنگار',
},
{
id: 'agent_role',
value: 'دستیار',
},
{
id: 'agent_role_description',
value: 'نماینده یا دستیار که به تسهیل ارائه خدمات سامانه کمک می کنند',
},
{
id: 'user_role',
value: 'کاربر',
},
{
id: 'user_role_description',
value: 'استفاده کننده از خدمات ارائه شده',
},
{
id: 'friendship_state',
value: 'وضعیت ارتباط',
},
{
id: 'unknown',
value: 'ناشناس',
},
{
id: 'follower',
value: 'دنبال کننده',
},
{
id: 'followers',
value: 'دنبال کننده ها',
},
{
id: 'following',
value: 'دنبال شده',
},
{
id: 'followings',
value: 'دنبال شده ها',
},
{
id: 'friend',
value: 'دوست',
},
{
id: 'block',
value: 'مسدود کردن',
},
{
id: 'unblock',
value: 'رفع مسدودی',
},
{
id: 'blocked',
value: 'مسدود شده',
},
{
id: 'follow',
value: 'دنبال کردن',
},
{
id: 'unfollow',
value: 'قطع ارتباط',
},
{
id: 'unfollow_as',
value: 'قطع ارتباط از نوع',
},
{
id: 'unfollow_follower',
value: 'قطع ارتباط دنبال کننده',
},
{
id: 'unfollow_following',
value: 'قطع ارتباط دنبال شونده',
},
{
id: 'cancel_request',
value: 'لغو درخواست',
},
{
id: 'accept_request',
value: 'پذیرش درخواست',
},
{
id: 'pending',
value: 'در انتظار',
},
{
id: 'pending_follower',
value: 'در انتظار تایید شما',
},
{
id: 'pending_following',
value: 'منتظر تایید ایشان',
},
{
id: 'rejected',
value: 'رد شده',
},
{
id: 'accepted',
value: 'قبول شده',
},
{
id: 'min_length_error',
value: 'کمتر از حد مجاز',
},
{
id: 'max_length_error',
value: 'بیشتر از حد مجاز',
},
{
id: 'invalid_url',
value: 'آدرس اینترنتی معتبر نیست',
},
{
id: 'invalid_phone_number',
value: 'شماره تلفن وارد شده معتبر نیست',
},
{
id: 'invalid_email_address',
value: 'آدرس پست الکترونیک نامعتبر است',
},
{
id: 'email_cant_register',
value: 'پست الکترونیک قابل استفاده نیست',
},
{
id: 'mobile_cant_register',
value: 'شماره تلفن قابل استفاده نیست',
},
{
id: 'user_name_cant_register',
value: 'نام کاربری قابل استفاده نیست',
},
{
id: 'email_in_use',
value: 'پست الکترونیک در حال استفاده است',
},
{
id: 'phone_number_in_use',
value: 'شماره تلفن همراه در حال استفاده است',
},
{
id: 'passwords_not_matched',
value: 'کلمه عبور و تکران آن یکسان نیست',
},
//
//#region Blockchain Views ...
{
id: 'symbol',
value: 'نماد',
},
{
id: 'theme',
value: 'پوسته',
},
{
id: 'chart',
value: 'نمودار',
},
{
id: 'date_range',
value: 'بازه زمانی',
},
{
id: 'chart_type',
value: 'نوع نمودار',
},
{
id: 'cryptocurrency_market',
value: 'وضعیت بازار',
},
{
id: 'analysis_tool',
value: 'ابزار تحلیل',
},
{
id: 'symbol_overview',
value: 'خلاصه وضعیت',
},
{
id: 'values_tracking',
value: 'ردیابی ارزش ها',
},
{
id: 'technical_analysis',
value: 'تحلیل تکنیکال',
},
{
id: 'select_sheet',
value: 'انتخاب برگه',
},
{
id: 'display_mode',
value: 'نحوه نمایش',
},
{
id: 'search_symbols',
value: 'جستجوی نمادها',
},
{
id: 'symbol_profile',
value: 'معرفی نمایه',
},
{
id: 'scale_mode',
value: 'نحوه افزایش رشد',
},
{
id: 'scale_position',
value: 'محل نمایش رشد',
},
//#endregion
//
{
id: 'startup_page_title',
value: 'آغازگر',
},
{
id: 'startup_page_description',
value: 'آغاز فرآیند بارگزاری سامانه',
},
{
id: 'landing_page_title',
value: 'ساحرعلم',
},
{
id: 'landing_page_description',
value: 'ارائه راه کارهای فن آورانه مبتنی بر کسب و کارها',
},
{
id: 'home_page_title',
value: 'پنل مدیریت',
},
{
id: 'home_page_description',
value: 'ابزارهای مدیریت',
},
{
id: 'error_page_title',
value: 'خطا',
},
{
id: 'error_page_description',
value: 'گزارش خطای واقع شده',
},
{
id: 'not_authorized_page_title',
value: 'عدم دسترسی',
},
{
id: 'not_authorized_page_description',
value: 'جواز دسترسی احراز نشد',
},
{
id: 'theme_manager_page_title',
value: 'مدیریت پوسته',
},
{
id: 'theme_manager_page_description',
value: 'مدیریت و دستکاری پوسته های نمایشی برنامه',
},
//
{
id: 'confirm_page_title',
value: 'تایید ثبت نام',
},
{
id: 'confirm_page_description',
value: 'تایید ثبت نام و فعالسازی حساب کاربری',
},
{
id: 'invite_page_title',
value: 'دعوتنامه',
},
{
id: 'invite_page_description',
value: 'دعوت از کاربران جهت عضویت در سیستم',
},
{
id: 'avatars_page_title',
value: 'تصاویر پروفایل',
},
{
id: 'avatars_page_description',
value: 'مدیریت تصاویر پروفایل کاربر',
},
{
id: 'users_management_page_title',
value: 'کاربران',
},
{
id: 'users_management_page_description',
value: 'ابزارهای مدیریت کاربران',
},
//
{
id: 'settings_page_title',
value: 'تنظیمات',
},
{
id: 'settings_page_description',
value: 'امکانات لازم جهت پیکربندی سامانه',
},
{
id: 'string_resource_page_title',
value: 'منابع متنی',
},
{
id: 'string_resource_page_description',
value: 'مدیریت منابع متنی مورد استفاده در سامانه',
},
{
id: 'terms_and_conditions_page_title',
value: 'شرایط و ضوابط ثبت نام',
},
{
id: 'terms_and_conditions_page_description',
value: 'مدیریت شرایط و ضوابط ثبت نام',
},
{
id: 'posts_page_title',
value: 'مطالب',
},
{
id: 'posts_page_description',
value: 'ابزارهای مدیریت مطالب',
},
{
id: 'content_explorer_page_title',
value: 'مرورگر محتوا',
},
{
id: 'content_explorert_page_description',
value: 'مرور محتواهای منتشر شده',
},
{
id: 'content_management_page_title',
value: 'مدیریت محتواها',
},
{
id: 'content_management_page_description',
value: 'مدیریت محتواهای ارسال شده',
},
{
id: 'media_explorer_page_title',
value: 'مرورگر رسانه',
},
{
id: 'media_explorert_page_description',
value: 'مرور رسانه های منتشر شده',
},
{
id: 'media_gallery_explorer_page_title',
value: 'نگارخانه',
},
{
id: 'media_gallery_explorert_page_description',
value: 'نمایش نگاره های ارسالی',
},
{
id: 'media_management_page_title',
value: 'مدیریت رسانه ها',
},
{
id: 'media_management_page_description',
value: 'مدیریت رسانه های ارسال شده',
},
{
id: 'documents_page_title',
value: 'اسناد',
},
{
id: 'documents_page_description',
value: 'ابزارهای مدیریت اسناد',
},
{
id: 'document_management_page_title',
value: 'مدیریت اسناد',
},
{
id: 'document_management_page_description',
value: 'مدیریت اسناد بارگزاری شده',
},
//
// Custom ...
{
id: 'at',
value: 'در',
},
{
id: 'posted_by',
value: 'ارسال شده توسط',
},
{
id: 'edited_by',
value: 'ویرایش شده توسط',
},
{
id: 'management_panel',
value: 'پنل مدیریت',
},
{
id: 'connect',
value: 'اتصال',
},
{
id: 'references',
value: 'مراجع',
},
{
id: 'share',
value: 'اشتراک گزاری',
},
{
id: 'media',
value: 'رسانه',
},
{
id: 'medias',
value: 'رسانه ها',
},
{
id: 'news',
value: 'اخبار',
},
{
id: 'content',
value: 'محتوا',
},
{
id: 'see_more',
value: 'ادامه مطلب »',
},
{
id: 'editted_on',
value: 'ویرایش شده در',
},
{
id: 'audioinput',
value: 'ورودی صوت',
},
{
id: 'audiooutput',
value: 'خروجی صوت',
},
{
id: 'videoinput',
value: 'ورودی تصویر',
},
{
id: 'select_media_device',
value: 'انتخاب ابزار مقصد',
},
{
id: 'author',
value: 'ناشر',
},
{
id: 'private',
value: 'خصوصی',
},
{
id: 'public',
value: 'عمومی',
},
{
id: 'locales',
value: 'منابع متنی',
},
{
id: 'created_on',
value: 'تاریخ ایجاد',
},
{
id: 'approver',
value: 'تایید کننده',
},
{
id: 'approver_id',
value: 'تایید شده توسط',
},
{
id: 'approved_on',
value: 'تایید شده در',
},
{
id: 'media_gallery',
value: 'نگاره',
},
{
id: 'media_galleries',
value: 'نگاره ها',
},
{
id: 'contents',
value: 'محتواها',
},
{
id: 'documents',
value: 'اسناد',
},
{
id: 'posted_on',
value: 'ارسال شده در',
},
{
id: 'upload_documents',
value: 'بارگزاری اسناد',
},
//
// Video ...
{ ...VideoLocalizationResources.mute.fa },
{ ...VideoLocalizationResources.play.fa },
{ ...VideoLocalizationResources.pause.fa },
{ ...VideoLocalizationResources.unmute.fa },
{ ...VideoLocalizationResources.volume.fa },
{ ...VideoLocalizationResources.rewind.fa },
{ ...VideoLocalizationResources.forward.fa },
{ ...VideoLocalizationResources.fullscreen.fa },
{ ...VideoLocalizationResources.unfullscreen.fa },
{ ...VideoLocalizationResources.change_volume.fa },
{ ...VideoLocalizationResources.mic_on.fa },
{ ...VideoLocalizationResources.mic_off.fa },
{ ...VideoLocalizationResources.video_on.fa },
{ ...VideoLocalizationResources.video_off.fa },
{ ...VideoLocalizationResources.call_end.fa },
{ ...VideoLocalizationResources.call_request.fa },
{ ...VideoLocalizationResources.stop_record.fa },
{ ...VideoLocalizationResources.start_record.fa },
{ ...VideoLocalizationResources.stop_share_screen.fa },
{ ...VideoLocalizationResources.start_share_screen.fa },
{ ...VideoLocalizationResources.switch_to_back_camera.fa },
{ ...VideoLocalizationResources.switch_to_front_camera.fa },
//
// Reference ...
{ ...ReferenceLocalizationResources.reference.fa },
{ ...ReferenceLocalizationResources.references.fa },
{ ...ReferenceLocalizationResources.provided_for.fa },
{ ...ReferenceLocalizationResources.referenced_to.fa },
//
// Account ...
{ ...AccountLocalizationResources.account.fa },
{ ...AccountLocalizationResources.accounts.fa },
{ ...AccountLocalizationResources.profile.fa },
{ ...AccountLocalizationResources.profiles.fa },
{ ...AccountLocalizationResources.avatar.fa },
{ ...AccountLocalizationResources.avatars.fa },
{ ...AccountLocalizationResources.register.fa },
{ ...AccountLocalizationResources.accept_terms.fa },
{ ...AccountLocalizationResources.email_confirmation.fa },
{ ...AccountLocalizationResources.input_user_info.fa },
{ ...AccountLocalizationResources.input_user_avatar.fa },
{ ...AccountLocalizationResources.finish_registration.fa },
{ ...AccountLocalizationResources.account_page_title.fa },
{ ...AccountLocalizationResources.account_page_description.fa },
{ ...AccountLocalizationResources.profile_page_title.fa },
{ ...AccountLocalizationResources.profile_page_description.fa },
{ ...AccountLocalizationResources.login_page_title.fa },
{ ...AccountLocalizationResources.login_page_description.fa },
{ ...AccountLocalizationResources.logout_page_title.fa },
{ ...AccountLocalizationResources.logout_page_description.fa },
{ ...AccountLocalizationResources.register_page_title.fa },
{ ...AccountLocalizationResources.register_page_description.fa },
{ ...AccountLocalizationResources.email_confirm_page_title.fa },
{ ...AccountLocalizationResources.email_confirm_page_description.fa },
{ ...AccountLocalizationResources.phone_confirm_page_title.fa },
{ ...AccountLocalizationResources.phone_confirm_page_description.fa },
{ ...AccountLocalizationResources.reset_password_page_title.fa },
{ ...AccountLocalizationResources.reset_password_page_description.fa },
{ ...AccountLocalizationResources.change_password_page_title.fa },
{ ...AccountLocalizationResources.change_password_page_description.fa },
{ ...AccountLocalizationResources.search_profiles_page_title.fa },
{ ...AccountLocalizationResources.search_profiles_page_description.fa },
//
// Tag ...
{ ...TagLocalizationResources.tag.fa },
{ ...TagLocalizationResources.tags.fa },
{ ...TagLocalizationResources.tags_page_title.fa },
{ ...TagLocalizationResources.tags_page_description.fa },
{ ...TagLocalizationResources.tags_management_page_title.fa },
{ ...TagLocalizationResources.tags_management_page_description.fa },
{ ...TagLocalizationResources.tags_explorer_page_title.fa },
{ ...TagLocalizationResources.tags_explorer_page_description.fa },
//
// File ...
{ ...FileLocalizationsResources.file.fa },
{ ...FileLocalizationsResources.files.fa },
{ ...FileLocalizationsResources.image.fa },
{ ...FileLocalizationsResources.audio.fa },
{ ...FileLocalizationsResources.video.fa },
{ ...FileLocalizationsResources.document.fa },
{ ...FileLocalizationsResources.file_name.fa },
{ ...FileLocalizationsResources.thumbnail_path.fa },
{ ...FileLocalizationsResources.files_page_title.fa },
{ ...FileLocalizationsResources.files_page_description.fa },
{ ...FileLocalizationsResources.files_management_page_title.fa },
{ ...FileLocalizationsResources.files_management_page_description.fa },
{ ...FileLocalizationsResources.files_explorer_page_title.fa },
{ ...FileLocalizationsResources.files_explorer_page_description.fa },
//
// Webinar ...
{ ...WebinarLocalizationResources.webinar.fa },
{ ...WebinarLocalizationResources.webinars.fa },
{ ...WebinarLocalizationResources.close_webinar.fa },
{ ...WebinarLocalizationResources.webinar_owner.fa },
{ ...WebinarLocalizationResources.webinar_joiner.fa },
{ ...WebinarLocalizationResources.new_webinar_created.fa },
{ ...WebinarLocalizationResources.webinars_page_title.fa },
{ ...WebinarLocalizationResources.visitor_joined_message.fa },
{ ...WebinarLocalizationResources.visitor_leaved_message.fa },
{ ...WebinarLocalizationResources.webinar_closed_message.fa },
{ ...WebinarLocalizationResources.webinars_page_description.fa },
{ ...WebinarLocalizationResources.webinars_management_page_title.fa },
{
...WebinarLocalizationResources.webinars_explorer_page_title.fa,
},
{
...WebinarLocalizationResources.webinars_explorer_page_description.fa,
},
{
...WebinarLocalizationResources.webinars_management_page_description.fa,
},
],
},
{
name: 'english',
locale: 'en-US',
language: 'en',
direction: 'ltr',
resources: [
{
id: 'app_name',
value: 'SaherElm',
},
{
id: 'company',
value: 'SaherElm IT Center',
},
{
id: 'id',
value: 'ID',
},
{
id: 'file_name',
value: 'File Name',
},
{
id: 'error',
value: 'Error',
},
{
id: 'required',
value: 'required',
},
{
id: 'loading',
value: 'loading',
},
{
id: 'username',
value: 'Username',
},
{
id: 'password',
value: 'Password',
},
{
id: 'first_name',
value: 'Firstname',
},
{
id: 'last_name',
value: 'Lastname',
},
{
id: 'verified',
value: 'Verified',
},
{
id: 'unverified',
value: 'Unverified',
},
{
id: 'thumbnail',
value: 'Thumbnail',
},
{
id: 'language',
value: 'Language',
},
{
id: 'take_screenshot',
value: 'Take Screenshot',
},
{
id: 'enable_capture_mode',
value: 'Enable Capture Mode',
},
{
id: 'disable_capture_mode',
value: 'Disable Capture Mode',
},
{
id: 'be',
value: 'Be',
},
{
id: 'not_to_be',
value: 'Not to be',
},
{
id: 'enabled',
value: 'Enabled',
},
{
id: 'disabled',
value: 'Disabled',
},
{
id: 'banned',
value: 'Banned',
},
{
id: 'un_banned',
value: 'UnBanned',
},
{
id: 'audio',
value: 'Audio',
},
{
id: 'video',
value: 'Video',
},
{
id: 'cover_image',
value: 'Cover Image',
},
{
id: 'image',
value: 'Image',
},
{
id: 'avatar',
value: 'Avatar',
},
{
id: 'unknown',
value: 'Unknown',
},
{
id: 'location',
value: 'Location',
},
{
id: 'are_you_sure',
value: 'Are You Sure ?',
},
{
id: 'success_msg',
value: 'Operation done Successfully ...',
},
{
id: 'locked_msg',
value: 'You have to finish/cancel current proccess for leaving',
},
{
id: 'empty_content',
value: 'Empty',
},
{
id: 'no_item',
value: 'there is no Content',
},
{
id: 'drag_drop_here',
value: 'Drop File Here',
},
{
id: 'map_dialog_title',
value: 'Show Location',
},
{
id: 'map_dialog_select_title',
value: 'Select Location',
},
{
id: 'color_picker_dialog_title',
value: 'Select Color',
},
{
id: 'dismiss',
value: 'dismiss',
},
{
id: 'accept',
value: 'Accept',
},
{
id: 'ok',
value: 'Ok',
},
{
id: 'save',
value: 'Save',
},
{
id: 'cancel',
value: 'Cancel',
},
{
id: 'retry',
value: 'Retry',
},
{
id: 'edit',
value: 'Edit',
},
{
id: 'reset',
value: 'Reset',
},
{
id: 'change',
value: 'Change',
},
{
id: 'start',
value: 'Start',
},
{
id: 'yes',
value: 'Yes',
},
{
id: 'no',
value: 'No',
},
{
id: 'verify',
value: 'Verify',
},
{
id: 'confirm',
value: 'Confirm',
},
{
id: 'crop',
value: 'Crop',
},
{
id: 'clear',
value: 'Clear',
},
{
id: 'set',
value: 'Set',
},
{
id: 'select',
value: 'Select',
},
{
id: 'send',
value: 'Send',
},
{
id: 'show',
value: 'Show',
},
{
id: 'hide',
value: 'Hide',
},
{
id: 'search',
value: 'Search',
},
{
id: 'delete',
value: 'Delete',
},
{
id: 'clone',
value: 'Clone',
},
{
id: 'select_all',
value: 'Select All',
},
{
id: 'select_file',
value: 'Select File(s)',
},
{
id: 'select_all_group',
value: 'Select All Filtered Items',
},
{
id: 'deselect_all',
value: 'Deselect All',
},
{
id: 'deselect_all_group',
value: 'Deselect All Filtered Items',
},
{
id: 'inverse_selection',
value: 'Inverse Selection',
},
{
id: 'selected_location',
value: 'Selected Location',
},
{
id: 'preset_colors',
value: 'Preset Colors',
},
{
id: 'copy_to_clipboard',
value: 'Copy to Clipboard',
},
{
id: 'color',
value: 'Color',
},
{
id: 'colors',
value: 'Colors',
},
{
id: 'upload',
value: 'Upload',
},
{
id: 'import',
value: 'Import',
},
{
id: 'continue',
value: 'Continue',
},
{
id: 'finish',
value: 'Finish',
},
{
id: 'enable',
value: 'Enable',
},
{
id: 'disable',
value: 'Disable',
},
{
id: 'all',
value: 'All',
},
{
id: 'new',
value: 'New',
},
{
id: 'add',
value: 'Add',
},
{
id: 'update',
value: 'Update',
},
{
id: 'details',
value: 'Details',
},
{
id: 'translation',
value: 'Translation',
},
{
id: 'download',
value: 'Download',
},
{
id: 'list',
value: 'List',
},
{
id: 'options',
value: 'Options',
},
{
id: 'descriptions',
value: 'Description',
},
{
id: 'type',
value: 'Type',
},
{
id: 'media',
value: 'Media',
},
{
id: 'medias',
value: 'Medias',
},
{
id: 'summary',
value: 'ُSummary',
},
{
id: 'go',
value: 'Go',
},
{
id: 'input',
value: 'Input',
},
{
id: 'preview',
value: 'Preview',
},
{
id: 'close',
value: 'Close',
},
{
id: 'undo',
value: 'Undo',
},
{
id: 'redo',
value: 'Redo',
},
{
id: 'refresh',
value: 'Refresh',
},
{
id: 'submit',
value: 'Submit',
},
{
id: 'first',
value: 'First',
},
{
id: 'last',
value: 'Last',
},
{
id: 'previous',
value: 'Previous',
},
{
id: 'next',
value: 'Next',
},
{
id: 'back',
value: 'Back',
},
{
id: 'x_markdown_preview',
value: 'Preview',
},
{
id: 'x_markdown_image',
value: 'Image',
},
{
id: 'x_markdown_link',
value: 'Link',
},
{
id: 'x_markdown_ol',
value: 'Ordered List',
},
{
id: 'x_markdown_ul',
value: 'Unordered List',
},
{
id: 'x_markdown_quote',
value: 'Quote',
},
{
id: 'x_markdown_code',
value: 'Code',
},
{
id: 'x_markdown_heading',
value: 'Heading',
},
{
id: 'x_markdown_italic',
value: 'Italic',
},
{
id: 'x_markdown_bold',
value: 'Bold',
},
{
id: 'x_markdown_guide',
value: 'Markdown Guide',
},
{
id: 'theme',
value: 'Theme',
},
{
id: 'theme_mode',
value: 'Theme Mode',
},
{
id: 'theme_dark',
value: 'Dark',
},
{
id: 'theme_light',
value: 'Light',
},
{
id: 'theme_name',
value: 'Theme Name',
},
{
id: 'theme_name_description',
value: 'Enter Theme name',
},
{
id: 'theme_clone_title',
value: 'Theme Cloning',
},
{
id: 'input_color_title',
value: 'Color Value',
},
{
id: 'input_color_description',
value: 'input color value',
},
{
id: 'ide_mode',
value: 'Code Style',
},
{
id: 'ide_format',
value: 'Code Frormatting',
},
{
id: 'ex_100',
value: 'Invalid Data ...',
},
{
id: 'ex_101',
value: 'Invalid Date ...',
},
{
id: 'ex_102',
value: 'Bad Request ...',
},
{
id: 'ex_103',
value: 'Invalid Args ...',
},
{
id: 'ex_104',
value: 'Invalid Message ...',
},
{
id: 'ex_105',
value: 'Invalid Token ...',
},
{
id: 'ex_106',
value: 'Invalid Verification Code ...',
},
{
id: 'ex_107',
value: 'Invalid Email Address ...',
},
{
id: 'ex_108',
value: 'Invalid Mobile Number ...',
},
{
id: 'ex_109',
value: 'Invalid Password ...',
},
{
id: 'ex_110',
value: 'Invalid URL ...',
},
{
id: 'ex_111',
value: 'Invalid Device ...',
},
{
id: 'ex_112',
value: 'Invalid Profile Image ...',
},
{
id: 'ex_113',
value: 'Invalid File ...',
},
{
id: 'ex_114',
value: 'Invalid Username ...',
},
{
id: 'ex_200',
value: 'Not Found ...',
},
{
id: 'ex_201',
value: 'Not Allowed ...',
},
{
id: 'ex_202',
value: 'Not Authorized ...',
},
{
id: 'ex_203',
value: 'Not Visible ...',
},
{
id: 'ex_300',
value: 'Account Locked out ...',
},
{
id: 'ex_301',
value: 'Login Failed ...',
},
{
id: 'ex_302',
value: 'User Registered Before ...',
},
{
id: 'ex_303',
value: 'User Disabled ...',
},
{
id: 'ex_304',
value: 'Device Banned Until:(0)',
},
{
id: 'ex_305',
value: `You can not Request Verification code Until:(0)`,
},
{
id: 'ex_306',
value: 'Passwords Same ...',
},
{
id: 'ex_307',
value: 'Mobiles Same ...',
},
{
id: 'ex_308',
value: 'Emails Same ...',
},
{
id: 'ex_309',
value: 'Mobile In Used ...',
},
{
id: 'ex_310',
value: 'Email In Used ...',
},
{
id: 'ex_311',
value: 'Mobile Not Confirmed ...',
},
{
id: 'ex_312',
value: 'Email Not Confirmed ...',
},
{
id: 'ex_313',
value: 'Post Banned ...',
},
{
id: 'ex_314',
value: 'User Banned ...',
},
{
id: 'ex_400',
value: 'Unknown Error ...',
},
{
id: 'ex_401',
value: 'Duplicate ...',
},
{
id: 'ex_402',
value: 'Action Failed ...',
},
{
id: 'ex_403',
value: 'Expired Date ...',
},
{
id: 'ex_404',
value: 'Unavailable ...',
},
{
id: 'ex_405',
value: 'Max Allowed Tag Exceeded ...',
},
{
id: 'ex_406',
value: 'Timeout ...',
},
{
id: 'ex_500',
value: 'Empty File ...',
},
{
id: 'ex_501',
value: 'max File size exceeded ...',
},
{
id: 'ex_502',
value: 'Unsupported File Type ...',
},
{
id: 'ex_503',
value: 'Max Allowed Media Exceeded ...',
},
{
id: 'ex_600',
value: 'Invalid Configuration ...',
},
{
id: 'ex_601',
value: 'Storage Service Initiallization Failed ...',
},
{
id: 'ex_602',
value: 'Message Service Initialization Failed ...',
},
{
id: 'ex_900',
value: 'Max Allowed Files Exceeded ...',
},
{
id: 'ex_901',
value: 'Min Files Size Exceeded ...',
},
{
id: 'ex_902',
value: 'Max Files Size Exceeded ...',
},
{
id: 'ex_903',
value: 'Must Accept Terms And Conditions ...',
},
{
id: 'ex_904',
value: 'Browser does not support Location Services ...',
},
{
id: 'ex_905',
value: 'You have rejected access to your Location ...',
},
{
id: 'ex_906',
value: 'Unable to determine your Location ...',
},
{
id: 'ex_907',
value: 'Browser does not support Storage Services ...',
},
{
id: 'ex_908',
value: 'Must Logout to access content ...',
},
{
id: 'ex_909',
value: 'Invalid Locale ...',
},
{
id: 'ex_910',
value: 'Invalid User Selector ...',
},
{
id: 'default_loading',
value: 'Loading',
},
{
id: 'persian',
value: 'Persian',
},
{
id: 'english',
value: 'English',
},
{
id: 'actor',
value: 'Actor',
},
{
id: 'index',
value: 'Index',
},
{
id: 'title',
value: 'Title',
},
{
id: 'description',
value: 'Description',
},
{
id: 'author_id',
value: 'Author Id',
},
{
id: 'attached_on',
value: 'Attached On',
},
{
id: 'view',
value: 'View',
},
{
id: 'viewed_on',
value: 'Viewed On',
},
{
id: 'check_mail',
value: 'Check your mailbox ...',
},
{
id: 'name',
value: 'Name',
},
{
id: 'type',
value: 'Type',
},
{
id: 'path',
value: 'Path',
},
{
id: 'uploaded_on',
value: 'Uploaded On',
},
{
id: 'string_resource',
value: 'String Resource',
},
{
id: 'resource_title',
value: 'Ù‹Resource ID',
},
{
id: 'add_new_terms_and_condition',
value: 'Add New Terms and Conditions',
},
{
id: 'add_new_terms_and_condition_description',
value: 'Add New Terms and Conditions Translation',
},
{
id: 'theme',
value: 'Themes',
},
//
{
id: 'state',
value: 'State',
},
{
id: 'enabled',
value: 'Enable',
},
{
id: 'disabled',
value: 'Disable',
},
{
id: 'user_id',
value: 'User Id',
},
{
id: 'content_id',
value: 'Content Id',
},
{
id: 'bann_state',
value: 'Bann State',
},
{
id: 'email_state',
value: 'Email State',
},
{
id: 'phone_number_state',
value: 'Phone NUmber State',
},
{
id: 'avatar',
value: 'Avatar',
},
{
id: 'avatar_list',
value: 'Avatars List',
},
{
id: 'logging_out',
value: 'Logging Out ...',
},
{
id: 'phone_number',
value: 'Phone Number',
},
{
id: 'authenticating',
value: 'Authenticating',
},
{
id: 'user_identifier',
value: 'User ID',
},
{
id: 'user_identifier_description',
value: 'User ID can be UserName, Email Address or Mobile Number',
},
{
id: 'password',
value: 'Password',
},
{
id: 'new_password',
value: 'New Password',
},
{
id: 'password_confirm',
value: 'Password Confirm',
},
{
id: 'enter_a_new_password',
value: 'Enter a new Password',
},
{
id: 'login',
value: 'Login',
},
{
id: 'logout',
value: 'Logout',
},
{
id: 'invite_send',
value: 'Send Invitation',
},
{
id: 'change_data',
value: 'Change Input Data',
},
{
id: 'request_reset_password',
value: 'Request Reset Password',
},
{
id: 'request_reset_password_description',
value: 'Request Reset Forgotten Password',
},
{
id: 'request_confirm_email',
value: 'Request again',
},
{
id: 'request_confirm_email_description',
value: 'Request again Email Verification Link',
},
{
id: 'upload_avatar',
value: 'Upload Avatar',
},
{
id: 'confirmed',
value: 'Confirmed',
},
{
id: 'unconfirmed',
value: 'UnConfirmed',
},
{
id: 'terms_and_conditions_title',
value: 'Registration Terms and Conditions',
},
{
id: 'account_info',
value: 'Account Info',
},
{
id: 'verification_code',
value: 'Verification Code',
},
{
id: 'send_verification_code',
value: 'Send Verification Code',
},
{
id: 'registration_finished',
value: 'Registeration Finished',
},
{
id: 'registration_finished_description',
value:
'After this yuo get an actiovation link on your email, check and follow it ...',
},
{
id: 'first_name',
value: 'First Name',
},
{
id: 'last_name',
value: 'Last Name',
},
{
id: 'user_name',
value: 'Username',
},
{
id: 'role',
value: 'Role',
},
{
id: 'roles',
value: 'Roles',
},
{
id: 'mobile',
value: 'Mobile',
},
{
id: 'email',
value: 'Email',
},
{
id: 'date_of_birth',
value: 'Date Of Birth',
},
{
id: 'registration_date',
value: 'Registration Date',
},
{
id: 'last_login',
value: 'Last Login',
},
{
id: 'gender',
value: 'Gender',
},
{
id: 'male',
value: 'Male',
},
{
id: 'female',
value: 'Female',
},
{
id: 'landing_page',
value: 'Ù‘Landing Page',
},
{
id: 'admin_role',
value: 'Admin',
},
{
id: 'admin_role_description',
value: 'Maintainer and Controller',
},
{
id: 'responsible_manager_role',
value: 'Responsible Manager',
},
{
id: 'responsible_manager_role_description',
value: 'Responsible Manager Descriptions',
},
{
id: 'journalist_role',
value: 'Journalist',
},
{
id: 'journalist_role_description',
value: 'Journalist Descriptions',
},
{
id: 'chief_clerk_role',
value: 'Chief Clerk',
},
{
id: 'chief_clerk_role_description',
value: 'Chief Clerk Descriptions',
},
{
id: 'chief_clerk_assistant_role',
value: 'Chief Clerk Assistant',
},
{
id: 'chief_clerk_assistant_role_description',
value: 'Chief Clerk Assistant Descriptions',
},
{
id: 'actuary_role',
value: 'Actuary',
},
{
id: 'actuary_role_description',
value: 'Actuary Descriptions',
},
{
id: 'actuary_assistant_role',
value: 'Actuary Assistant',
},
{
id: 'actuary_assistant_role_description',
value: 'Actuary Assistant Descriptions',
},
{
id: 'reporter_role',
value: 'Reporter',
},
{
id: 'reporter_role_description',
value: 'Reporter Descriptions',
},
{
id: 'reporter_assistant_role',
value: 'Reporter Assistant',
},
{
id: 'reporter_assistant_role_description',
value: 'Reporter Assistant Descriptions',
},
{
id: 'agent_role',
value: 'Agent',
},
{
id: 'agent_role_description',
value: 'Service Provider Agent',
},
{
id: 'user_role',
value: 'User',
},
{
id: 'user_role_description',
value: 'consumer of provided Services',
},
{
id: 'friendship_state',
value: 'Friendship State',
},
{
id: 'unknown',
value: 'Unknown',
},
{
id: 'follower',
value: 'Follower',
},
{
id: 'followers',
value: 'Followers',
},
{
id: 'following',
value: 'Following',
},
{
id: 'followings',
value: 'Followings',
},
{
id: 'friend',
value: 'Friend',
},
{
id: 'block',
value: 'Block',
},
{
id: 'unblock',
value: 'Unblock',
},
{
id: 'blocked',
value: 'Blocked',
},
{
id: 'follow',
value: 'Follow',
},
{
id: 'unfollow',
value: 'Unfollow',
},
{
id: 'unfollow_as',
value: 'Unfollow Friendshp',
},
{
id: 'unfollow_follower',
value: 'Unfollow Follower',
},
{
id: 'unfollow_following',
value: 'Unfollow Following',
},
{
id: 'cancel_request',
value: 'Cancel Request',
},
{
id: 'accept_request',
value: 'Accept Request',
},
{
id: 'pending',
value: 'Pending',
},
{
id: 'pending_follower',
value: 'waiting for your response',
},
{
id: 'pending_following',
value: 'wait for response',
},
{
id: 'rejected',
value: 'Rejected',
},
{
id: 'accepted',
value: 'Accepted',
},
{
id: 'min_length_error',
value: 'less than minimum required',
},
{
id: 'max_length_error',
value: 'more than maximum required',
},
{
id: 'invalid_url',
value: 'Invalid URL',
},
{
id: 'invalid_phone_number',
value: 'Invalid Phone Number',
},
{
id: 'invalid_email_address',
value: 'Invalid Email Address',
},
{
id: 'email_cant_register',
value: 'Email not available ...',
},
{
id: 'mobile_cant_register',
value: 'Mobile not available ...',
},
{
id: 'user_name_cant_register',
value: 'Username not available ...',
},
{
id: 'email_in_use',
value: 'Email Address in Use',
},
{
id: 'phone_number_in_use',
value: 'Phone Number in Use',
},
{
id: 'passwords_not_matched',
value: 'Passwords not matched',
},
//
//#region Blockchain Views ...
{
id: 'symbol',
value: 'Symbol',
},
{
id: 'theme',
value: 'ُTheme',
},
{
id: 'chart',
value: 'Chart',
},
{
id: 'date_range',
value: 'Date Range',
},
{
id: 'chart_type',
value: 'Chart Type',
},
{
id: 'cryptocurrency_market',
value: 'Market State',
},
{
id: 'analysis_tool',
value: 'Analysis Tool',
},
{
id: 'symbol_overview',
value: 'Symbol Overview',
},
{
id: 'values_tracking',
value: 'Values Tracking',
},
{
id: 'technical_analysis',
value: 'Technical Analysis',
},
{
id: 'select_sheet',
value: 'Select Sheet',
},
{
id: 'display_mode',
value: 'Display Mode',
},
{
id: 'search_symbols',
value: 'Search Symbols',
},
{
id: 'symbol_profile',
value: 'Symbol Profile',
},
{
id: 'scale_mode',
value: 'Scale Mode',
},
{
id: 'scale_position',
value: 'Scale Position',
},
//#endregion
//
{
id: 'startup_page_title',
value: 'Startup',
},
{
id: 'startup_page_description',
value: 'Start Loading Application',
},
{
id: 'landing_page_title',
value: 'First Page',
},
{
id: 'landing_page_description',
value: 'First Page was shown',
},
{
id: 'home_page_title',
value: 'Management Panel',
},
{
id: 'home_page_description',
value: 'Management Tools',
},
{
id: 'error_page_title',
value: 'Error',
},
{
id: 'error_page_description',
value: 'Repport Details of happens Error',
},
{
id: 'not_authorized_page_title',
value: 'Unauthorized Access',
},
{
id: 'not_authorized_page_description',
value: 'required Permissions not found',
},
{
id: 'theme_manager_page_title',
value: 'Theme Manager',
},
{
id: 'theme_manager_page_description',
value: 'add/remove and edit Application Themes',
},
//
{
id: 'confirm_page_title',
value: 'Confirm Registration',
},
{
id: 'confirm_page_description',
value: 'Confirm Registration and Enable User Account',
},
{
id: 'invite_page_title',
value: 'Invite Users',
},
{
id: 'invite_page_description',
value: 'Send Invitation Email and Invite Users to Join',
},
{
id: 'avatars_page_title',
value: 'Profile Avatars',
},
{
id: 'avatars_page_description',
value: 'Manage User Profile Avatars',
},
{
id: 'users_management_page_title',
value: 'Users',
},
{
id: 'users_management_page_description',
value: 'Users Management Tools',
},
//
{
id: 'settings_page_title',
value: 'Settings',
},
{
id: 'settings_page_description',
value: 'Tools for Configuring System',
},
{
id: 'string_resource_page_title',
value: 'String Resources',
},
{
id: 'string_resource_page_description',
value: 'String Resources Management',
},
{
id: 'terms_and_conditions_page_title',
value: 'Terms and Conditions',
},
{
id: 'terms_and_conditions_page_description',
value: 'Terms and Conditions Management',
},
{
id: 'posts_page_title',
value: 'Posts',
},
{
id: 'posts_page_description',
value: 'Tools for Posts',
},
{
id: 'content_explorer_page_title',
value: 'Contents Explorer',
},
{
id: 'content_explorert_page_description',
value: 'Explore Published Contents',
},
{
id: 'content_management_page_title',
value: 'Content Management',
},
{
id: 'content_management_page_description',
value: 'Content Management Tools',
},
{
id: 'media_explorer_page_title',
value: 'Media Explorer',
},
{
id: 'media_explorert_page_description',
value: 'Explore Published Medias',
},
{
id: 'media_gallery_explorer_page_title',
value: 'Media Galleries',
},
{
id: 'media_gallery_explorert_page_description',
value: 'Review Posted Galleries',
},
{
id: 'media_management_page_title',
value: 'Media Management',
},
{
id: 'media_management_page_description',
value: 'Media Management Tools',
},
{
id: 'documents_page_title',
value: 'Docments',
},
{
id: 'documents_page_description',
value: 'Tools for Documents',
},
{
id: 'document_management_page_title',
value: 'Document Management',
},
{
id: 'document_management_page_description',
value: 'Document Management Tools',
},
//
// Custom ...
{
id: 'at',
value: 'at',
},
{
id: 'posted_by',
value: 'posted by',
},
{
id: 'edited_by',
value: 'edited by',
},
{
id: 'management_panel',
value: 'Management Panel',
},
{
id: 'connect',
value: 'Connect',
},
{
id: 'references',
value: 'References',
},
{
id: 'news',
value: 'News',
},
{
id: 'share',
value: 'Share',
},
{
id: 'media',
value: 'Media',
},
{
id: 'medias',
value: 'Medias',
},
{
id: 'content',
value: 'Content',
},
{
id: 'see_more',
value: '« see more',
},
{
id: 'editted_on',
value: 'Editted on',
},
{
id: 'audioinput',
value: 'Audio Input',
},
{
id: 'audiooutput',
value: 'Audio Output',
},
{
id: 'videoinput',
value: 'Video Input',
},
{
id: 'select_media_device',
value: 'Select Media Device',
},
{
id: 'author',
value: 'Author',
},
{
id: 'private',
value: 'Private',
},
{
id: 'public',
value: 'Public',
},
{
id: 'locales',
value: 'Locales',
},
{
id: 'created_on',
value: 'Created On',
},
{
id: 'approver',
value: 'Approver',
},
{
id: 'approver_id',
value: 'Approved by',
},
{
id: 'approved_on',
value: 'Approved on',
},
{
id: 'media_gallery',
value: 'Media Gallery',
},
{
id: 'media_galleries',
value: 'Media Galleries',
},
{
id: 'contents',
value: 'Contents',
},
{
id: 'posted_on',
value: 'Posted On',
},
{
id: 'documents',
value: 'Documents',
},
{
id: 'upload_documents',
value: 'Upload Documents',
},
//
// Video ...
{ ...VideoLocalizationResources.mute.en },
{ ...VideoLocalizationResources.play.en },
{ ...VideoLocalizationResources.pause.en },
{ ...VideoLocalizationResources.unmute.en },
{ ...VideoLocalizationResources.volume.en },
{ ...VideoLocalizationResources.rewind.en },
{ ...VideoLocalizationResources.forward.en },
{ ...VideoLocalizationResources.fullscreen.en },
{ ...VideoLocalizationResources.unfullscreen.en },
{ ...VideoLocalizationResources.change_volume.en },
{ ...VideoLocalizationResources.mic_on.en },
{ ...VideoLocalizationResources.mic_off.en },
{ ...VideoLocalizationResources.video_on.en },
{ ...VideoLocalizationResources.video_off.en },
{ ...VideoLocalizationResources.call_end.en },
{ ...VideoLocalizationResources.call_request.en },
{ ...VideoLocalizationResources.start_record.en },
{ ...VideoLocalizationResources.start_record.en },
{ ...VideoLocalizationResources.stop_share_screen.en },
{ ...VideoLocalizationResources.start_share_screen.en },
{ ...VideoLocalizationResources.switch_to_back_camera.en },
{ ...VideoLocalizationResources.switch_to_front_camera.en },
//
// Reference ...
{ ...ReferenceLocalizationResources.reference.en },
{ ...ReferenceLocalizationResources.references.en },
{ ...ReferenceLocalizationResources.provided_for.en },
{ ...ReferenceLocalizationResources.referenced_to.en },
//
// Account ...
{ ...AccountLocalizationResources.account.en },
{ ...AccountLocalizationResources.accounts.en },
{ ...AccountLocalizationResources.profile.en },
{ ...AccountLocalizationResources.profiles.en },
{ ...AccountLocalizationResources.avatar.en },
{ ...AccountLocalizationResources.avatars.en },
{ ...AccountLocalizationResources.register.en },
{ ...AccountLocalizationResources.accept_terms.en },
{ ...AccountLocalizationResources.email_confirmation.en },
{ ...AccountLocalizationResources.input_user_info.en },
{ ...AccountLocalizationResources.input_user_avatar.en },
{ ...AccountLocalizationResources.finish_registration.en },
{ ...AccountLocalizationResources.account_page_title.en },
{ ...AccountLocalizationResources.account_page_description.en },
{ ...AccountLocalizationResources.profile_page_title.en },
{ ...AccountLocalizationResources.profile_page_description.en },
{ ...AccountLocalizationResources.login_page_title.en },
{ ...AccountLocalizationResources.login_page_description.en },
{ ...AccountLocalizationResources.logout_page_title.en },
{ ...AccountLocalizationResources.logout_page_description.en },
{ ...AccountLocalizationResources.register_page_title.en },
{ ...AccountLocalizationResources.register_page_description.en },
{ ...AccountLocalizationResources.email_confirm_page_title.en },
{ ...AccountLocalizationResources.email_confirm_page_description.en },
{ ...AccountLocalizationResources.phone_confirm_page_title.en },
{ ...AccountLocalizationResources.phone_confirm_page_description.en },
{ ...AccountLocalizationResources.reset_password_page_title.en },
{ ...AccountLocalizationResources.reset_password_page_description.en },
{ ...AccountLocalizationResources.change_password_page_title.en },
{ ...AccountLocalizationResources.change_password_page_description.en },
{ ...AccountLocalizationResources.search_profiles_page_title.en },
{ ...AccountLocalizationResources.search_profiles_page_description.en },
//
// Tag ...
{ ...TagLocalizationResources.tag.en },
{ ...TagLocalizationResources.tags.en },
{ ...TagLocalizationResources.tags_page_title.en },
{ ...TagLocalizationResources.tags_page_description.en },
{ ...TagLocalizationResources.tags_management_page_title.en },
{ ...TagLocalizationResources.tags_management_page_description.en },
{ ...TagLocalizationResources.tags_explorer_page_title.en },
{ ...TagLocalizationResources.tags_explorer_page_description.en },
//
// File ...
{ ...FileLocalizationsResources.file.en },
{ ...FileLocalizationsResources.files.en },
{ ...FileLocalizationsResources.image.en },
{ ...FileLocalizationsResources.audio.en },
{ ...FileLocalizationsResources.video.en },
{ ...FileLocalizationsResources.document.en },
{ ...FileLocalizationsResources.file_name.en },
{ ...FileLocalizationsResources.thumbnail_path.en },
{ ...FileLocalizationsResources.files_page_title.en },
{ ...FileLocalizationsResources.files_page_description.en },
{ ...FileLocalizationsResources.files_management_page_title.en },
{ ...FileLocalizationsResources.files_management_page_description.en },
{ ...FileLocalizationsResources.files_explorer_page_title.en },
{ ...FileLocalizationsResources.files_explorer_page_description.en },
//
// Webinar ...
{ ...WebinarLocalizationResources.webinar.en },
{ ...WebinarLocalizationResources.webinars.en },
{ ...WebinarLocalizationResources.close_webinar.en },
{ ...WebinarLocalizationResources.webinar_owner.en },
{ ...WebinarLocalizationResources.webinar_joiner.en },
{ ...WebinarLocalizationResources.new_webinar_created.en },
{ ...WebinarLocalizationResources.webinars_page_title.en },
{ ...WebinarLocalizationResources.visitor_joined_message.en },
{ ...WebinarLocalizationResources.visitor_leaved_message.en },
{ ...WebinarLocalizationResources.webinar_closed_message.en },
{ ...WebinarLocalizationResources.webinars_page_description.en },
{ ...WebinarLocalizationResources.webinars_management_page_title.en },
{
...WebinarLocalizationResources.webinars_explorer_page_title.en,
},
{
...WebinarLocalizationResources.webinars_explorer_page_description.en,
},
{
...WebinarLocalizationResources.webinars_management_page_description.en,
},
],
},
];
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\page.config.ts
import {
XUserRoleInfo,
XAvailableRoles,
getTopRoleByRoleInfo,
} from 'x-saherelm-api-sdk';
import {
XIconNames,
XNavigatorListItem,
toNavigatiorListItem,
} from 'x-framework-components';
import { AppResourceIDs } from './localization.config';
import { TagRoutes, Tags } from '../views/tags/v-tag.configs';
import { toArray, XPage, XResourceIDs } from 'x-framework-core';
import { FileRoutes, Files } from '../views/files/v-file.configs';
import { XSimplePage } from '../views/typings/simple.page.typings';
import { Account, AccountRoutes } from '../views/account/account.configs';
import { WebinarRoutes, Webinars } from '../views/webinar/v-webinar.configs';
//
//#region Route Config ...
export enum BaseRoutes {
Default = '',
Unknown = '**',
}
export enum StartupRoutes {
Default = 'startup',
}
export enum LandingRoutes {
Default = '#',
}
export enum NotAuthorizedRoutes {
Default = 'not-authorized',
}
export enum ErrorRoutes {
Default = 'error',
}
export enum HomeRoutes {
Default = 'home',
}
export enum AppRoutes {
Default = BaseRoutes.Default,
Startup = StartupRoutes.Default,
Landing = LandingRoutes.Default,
Home = HomeRoutes.Default,
Webinars = WebinarRoutes.Default,
Tags = TagRoutes.Default,
Files = FileRoutes.Default,
Account = AccountRoutes.Default,
NotAuthorized = NotAuthorizedRoutes.Default,
Error = ErrorRoutes.Default,
Unknown = BaseRoutes.Unknown,
}
//#endregion
//
//#region Page Configs ...
export enum PageName {
//
//#region Startup ...
Startup = AppRoutes.Startup,
//#endregion
//
//#region Landing ...
Landing = AppRoutes.Landing,
//#endregion
//
//#region Home ...
Home = AppRoutes.Home,
//#endregion
//
//#region Webinar ...
Webinars = AppRoutes.Webinars,
WebinarsExplorer = WebinarRoutes.WebinarsExplorer,
WebinarsManagement = WebinarRoutes.WebinarsManagement,
//#endregion
//
//#region Tag ...
Tags = AppRoutes.Tags,
TagsExplorer = TagRoutes.TagsExplorer,
TagsManagement = TagRoutes.TagsManagement,
//#endregion
//
//#region File ...
Files = AppRoutes.Files,
FilesExplorer = FileRoutes.FilesExplorer,
FilesManagement = FileRoutes.FilesManagement,
//#endregion
//
//#region Account ...
Account = AppRoutes.Account,
Login = AccountRoutes.Login,
Logout = AccountRoutes.Logout,
Profile = AccountRoutes.Profile,
Register = AccountRoutes.Register,
EmailConfirm = AccountRoutes.EmailConfirm,
PhoneConfirm = AccountRoutes.PhoneConfirm,
ResetPassword = AccountRoutes.ResetPassword,
ChangePassword = AccountRoutes.ChangePassword,
SearchProfiles = AccountRoutes.SearchProfiles,
//#endregion
//
//#region NotAuthorized ...
NotAuthorized = AppRoutes.NotAuthorized,
//#endregion
//
//#region Error ...
Error = AppRoutes.Error,
//#endregion
}
export type PageNames = keyof typeof PageName;
export type PageNameIdentifier = PageName | PageNames | string;
export type PageIndexType = {
[key in PageNames]?: XPage | XSimplePage;
};
export const PageIndex: PageIndexType = {
//
//#region Startup ...
Startup: {
id: `${PageName.Startup}`,
name: `${PageName.Startup}`,
title: AppResourceIDs.startup_page_title,
description: AppResourceIDs.startup_page_description,
baseRoute: `${AppRoutes.Startup}`,
route: ['/', `${AppRoutes.Startup}`],
icon: XIconNames.friendship_unknown,
},
//#endregion
//
//#region Landing ...
Landing: {
id: `${PageName.Landing}`,
name: `${PageName.Landing}`,
title: XResourceIDs.app_name,
description: XResourceIDs.company,
baseRoute: `${AppRoutes.Landing}`,
route: ['/', `${AppRoutes.Landing}`],
icon: XIconNames.posts,
},
//#endregion
//
//#region Home ...
Home: {
icon: XIconNames.home,
id: `${PageName.Home}`,
name: `${PageName.Home}`,
baseRoute: `${AppRoutes.Home}`,
route: ['/', `${AppRoutes.Home}`],
title: AppResourceIDs.home_page_title,
description: AppResourceIDs.home_page_description,
},
//#endregion
//
// Webinars ...
Webinars: {
...Webinars,
},
//
// Tags ...
Tags: {
...Tags,
},
//
// Files ...
Files: {
...Files,
},
//
// Account ...
Account: {
...Account,
},
//
//#region Error Page ...
Error: {
id: `${PageName.Error}`,
name: `${PageName.Error}`,
title: AppResourceIDs.error_page_title,
description: AppResourceIDs.error_page_description,
baseRoute: `${AppRoutes.Error}`,
route: ['/', `${AppRoutes.Error}`],
icon: XIconNames.error,
},
//#endregion
//
//#region Not Authorized Page ...
NotAuthorized: {
id: `${PageName.NotAuthorized}`,
name: `${PageName.NotAuthorized}`,
title: AppResourceIDs.not_authorized_page_title,
description: AppResourceIDs.not_authorized_page_description,
baseRoute: `${AppRoutes.NotAuthorized}`,
route: ['/', `${AppRoutes.NotAuthorized}`],
icon: XIconNames.friendship_block,
},
//#endregion
};
//
// a Const of Available Pages ...
const accountChilds = toArray(PageIndex.Account.childs);
export const Pages: any = {
//
//#region Startup ...
Startup: PageIndex.Startup,
//#endregion
//
//#region Landing ...
Landing: PageIndex.Landing,
//#endregion
//
//#region Home ...
Home: PageIndex.Home,
//#endregion
//
// Webinars ...
Webinars: PageIndex.Webinars,
WebinarsExplorer: Webinars.childs[1],
WebinarsManagement: Webinars.childs[0],
//
// Tags ...
Tags: PageIndex.Tags,
TagsExplorer: Tags.childs[1],
TagsManagement: Tags.childs[0],
//
// Files ...
Files: PageIndex.Files,
FilesExplorer: Files.childs[1],
FilesManagement: Files.childs[0],
//
//#region Account ...
Account: PageIndex.Account,
Login: Account.childs[0],
Logout: Account.childs[1],
Profile: Account.childs[2],
Register: Account.childs[3],
EmailConfirm: Account.childs[4],
PhoneConfirm: Account.childs[5],
ResetPassword: Account.childs[6],
ChangePassword: Account.childs[7],
SearchProfiles: Account.childs[8],
//#endregion
//
//#region Error ...
Error: PageIndex.Error,
//#endregion
//
//#region Notuthorized ...
NotAuthorized: PageIndex.NotAuthorized,
//#endregion
};
//#endregion
//
//#region Navigator Pages ...
//
//#region Navigation Items ...
//
// Admin Naigator Pages ...
const AdminNavPageItems: XNavigatorListItem[] = [
//
// Home ...
{
...toNavigatiorListItem(PageIndex.Home),
},
//
// Webinars ...
{
...toNavigatiorListItem(PageIndex.Webinars),
},
//
// Tags ...
{
...toNavigatiorListItem(PageIndex.Tags),
},
//
// Files ...
{
...toNavigatiorListItem(PageIndex.Files),
},
//
// Account ...
{
...toNavigatiorListItem(PageIndex.Account),
childs: [
//
// Profile ...
{ ...toNavigatiorListItem(Pages.Profile) },
//
// Change Password ...
{ ...toNavigatiorListItem(Pages.ChangePassword) },
//
// Search Profiles ...
{ ...toNavigatiorListItem(Pages.SearchProfiles) },
],
href: '',
},
//
// Logout ...
{
...toNavigatiorListItem(Pages.Logout),
href: '',
},
];
//
// Agent Navigator Pages ...
const AgentNavPageItems: XNavigatorListItem[] = [
//
// Home ...
{
...toNavigatiorListItem(PageIndex.Home),
},
//
// Webinars ...
{
...toNavigatiorListItem(PageIndex.Webinars),
},
//
// Account ...
{
...toNavigatiorListItem(PageIndex.Account),
childs: [
//
// Profile ...
{ ...toNavigatiorListItem(Pages.Profile) },
//
// Change Password ...
{ ...toNavigatiorListItem(Pages.ChangePassword) },
//
// Search Profiles ...
{ ...toNavigatiorListItem(Pages.SearchProfiles) },
],
href: '',
},
//
// Logout ...
{
...toNavigatiorListItem(Pages.Logout),
href: '',
},
];
//
// Users Navigator Pages ...
const UserNavPageItems: XNavigatorListItem[] = [
//
// Home ...
{
...toNavigatiorListItem(PageIndex.Home),
},
//
// Webinars ...
{
...toNavigatiorListItem(PageIndex.Webinars),
childs: [
//
// Webinars Explorer ...
{ ...toNavigatiorListItem(Pages.WebinarsExplorer) },
],
},
//
// Account ...
{
...toNavigatiorListItem(PageIndex.Account),
childs: [
//
// Profile ...
{ ...toNavigatiorListItem(Pages.Profile) },
//
// Change Password ...
{ ...toNavigatiorListItem(Pages.ChangePassword) },
//
// Search Profiles ...
{ ...toNavigatiorListItem(Pages.SearchProfiles) },
],
},
//
// Logout ...
{
...toNavigatiorListItem(Pages.Logout),
href: '',
},
];
//
// Guest ...
const GuestNavPageItems: XNavigatorListItem[] = [
//
// Register ...
{
...toNavigatiorListItem(Pages.Register),
// href: '',
},
//
// Login ...
{
...toNavigatiorListItem(Pages.Login),
// href: '',
},
];
//#endregion
//
//#region Navigator Pages Provider ...
export async function getNavPages(
roleInfo: XUserRoleInfo,
): Promise<XNavigatorListItem[]> {
//
if (!roleInfo) {
//
// Guest Users ...
return GuestNavPageItems;
}
//
// TODO: Fix these ...
const topRole = getTopRoleByRoleInfo(roleInfo);
switch (topRole) {
//
case XAvailableRoles.Admin:
return AdminNavPageItems;
//
case XAvailableRoles.Agent:
return AgentNavPageItems;
//
// TODO: Fix this ...
case XAvailableRoles.User:
return UserNavPageItems;
//
default:
return GuestNavPageItems;
}
}
//#endregion
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\x-api.config.ts
import { XCONFIG } from './x.config';
import { XApiConfiguration } from 'x-framework-identity-sdk';
/**
* Api Configuration
*/
export const XAPI_CONFIG = new XApiConfiguration({
//
baseApiPath: XCONFIG.baseUrl,
apiVersion: XCONFIG.apiVersion,
poweredByValue: XCONFIG.poweredValue,
revisionSecretKey: XCONFIG.secretKey,
});
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\config\x.config.ts
import { XConfig } from './app.config';
import { InjectionToken } from '@angular/core';
import { environment } from '../../environments/environment';
export const XCONFIG: XConfig = JSON.parse(
JSON.stringify(environment.configurations)
);
export const X_CONFIG = new InjectionToken<XConfig>('x_config');
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\account.module.ts
import { NgModule } from '@angular/core';
import { LoginPage } from './login/login.page';
import { RouterModule } from '@angular/router';
import { AdminGuard } from 'x-saherelm-api-sdk';
import { LogoutPage } from './logout/logout.page';
import { ProfilePage } from './profile/profile.page';
import { XCanDeactivateGuard } from 'x-framework-core';
import { ViewsModule } from 'src/app/views/public-api';
import { RegisterPage } from './register/register.page';
import { BaseRoutes } from 'src/app/config/page.config';
import { AuthGuard, NotAuthGuard } from 'x-framework-identity-sdk';
import { AccountRoutes } from 'src/app/views/account/account.configs';
import { EmailConfirmPage } from './email-confirm/email-confirm.page';
import { PhoneConfirmPage } from './phone-confirm/phone-confirm.page';
import { ResetPasswordPage } from './reset-password/reset-password.page';
import { ChangePasswordPage } from './change-password/change-password.page';
import { SearchProfilesPage } from './search-profiles/search-profiles.page';
@NgModule({
declarations: [
LoginPage,
LogoutPage,
ProfilePage,
RegisterPage,
PhoneConfirmPage,
EmailConfirmPage,
ResetPasswordPage,
ChangePasswordPage,
SearchProfilesPage,
],
imports: [
ViewsModule,
RouterModule.forChild([
{
path: BaseRoutes.Default,
redirectTo: AccountRoutes.Profile,
pathMatch: 'full',
},
//
// Login ...
{
path: AccountRoutes.Login,
component: LoginPage,
canActivate: [NotAuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Logout ...
{
path: AccountRoutes.Logout,
component: LogoutPage,
canActivate: [AuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Profile ...
{
path: AccountRoutes.Profile,
component: ProfilePage,
canActivate: [AuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Register ...
{
path: AccountRoutes.Register,
component: RegisterPage,
canActivate: [NotAuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Email Confirm ...
{
path: AccountRoutes.EmailConfirm,
component: EmailConfirmPage,
canActivate: [NotAuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Phone Confirm ...
{
path: AccountRoutes.PhoneConfirm,
component: PhoneConfirmPage,
canActivate: [NotAuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Reset Password ...
{
path: AccountRoutes.ResetPassword,
component: ResetPasswordPage,
canActivate: [NotAuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Change Password ...
{
path: AccountRoutes.ChangePassword,
component: ChangePasswordPage,
canActivate: [AuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
//
// Search Profiles ...
{
path: AccountRoutes.SearchProfiles,
component: SearchProfilesPage,
canActivate: [AuthGuard],
canDeactivate: [XCanDeactivateGuard],
},
{
path: BaseRoutes.Unknown,
pathMatch: 'full',
redirectTo: AccountRoutes.Default,
},
]),
],
exports: [
//
ViewsModule,
RouterModule,
//
LoginPage,
LogoutPage,
ProfilePage,
RegisterPage,
PhoneConfirmPage,
EmailConfirmPage,
ResetPasswordPage,
ChangePasswordPage,
SearchProfilesPage,
],
})
export class AccountPageModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\account.typings.ts
export const LoginTasks = {
Login: 'login',
RequestResetPassword: 'request_reset_password',
ResendEmailVerificationLink: 'resend_email_verification_link',
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\change-password\change-password.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Change Password ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\change-password\change-password.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\change-password\change-password.page.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-change-password',
templateUrl: './change-password.page.html',
styleUrls: ['./change-password.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChangePasswordPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.change_password_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\email-confirm\email-confirm.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Email Confirm ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\email-confirm\email-confirm.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\email-confirm\email-confirm.page.ts
import { XSideType } from 'x-framework-components';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-email-confirm',
templateUrl: './email-confirm.page.html',
styleUrls: ['./email-confirm.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EmailConfirmPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.email_confirm_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\login\login.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<v-login
[model]="model"
[loading]="loading"
(login)="handleLogin()"
[uiDisabled]="loading || uiDisabled"
(lockNotifier)="handleLockStateChanged($event)"
(changeNotifier)="handleModelStateChange($event)"
></v-login>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\login\login.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\login\login.page.ts
import {
XTask,
XParam,
getTaskName,
isNullOrUndefined,
} from 'x-framework-core';
import { filter } from 'rxjs/operators';
import { LoginTasks } from '../account.typings';
import { Pages } from 'src/app/config/page.config';
import { XSideType } from 'x-framework-components';
import { XLoginRequestDto } from 'x-framework-identity-sdk';
import { VModelState } from 'src/app/views/v-dto/v-dto.typings';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line:component-class-suffix
export class LoginPage extends VPageComponent {
//
//#region Props ...
//
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.login_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//
showEmailConfirm = false;
showResetPassword = false;
//
model: XLoginRequestDto = {
userSelectBy: '',
password: '',
};
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Register Handlers ...
//#endregion
//
//#region Task Handlers ...
async onTaskStart(name: string, task: XTask<any>) {
super.onTaskStart(name, task);
//
// Prevent Moving Forward ...
if (!this.isPageTask(name)) {
return;
}
//
// Set Loading State ...
await this.setLoadingState(true);
//
// Handle Flags for Confirm Email and Reset Password ...
// TODO: Complete this ...
//
if (name === LoginTasks.ResendEmailVerificationLink) {
this.showEmailConfirm = false;
}
//
if (name === LoginTasks.RequestResetPassword) {
this.showResetPassword = false;
}
//
// Apply Changes ...
this.detectChanges();
}
async onTaskFinished(name: string, task: XTask<any>) {
super.onTaskFinished(name, task);
//
// Prevent Moving Forward ...
if (!this.isPageTask(name)) {
return;
}
//
// Set Loading State ...
await this.setLoadingState(false);
//
// Handle User Authenticated ...
if (name === LoginTasks.Login) {
this.handleUserAuthenticated();
return;
}
//
// If Email Verification is Passed ...
if (
name === LoginTasks.ResendEmailVerificationLink ||
name === LoginTasks.RequestResetPassword
) {
//
await this.managerService.notificationService.presentSuccessNotification({
message: this.resourceProvider(this.AppResourceIDs.check_mail),
dissmissable: true,
});
}
}
async onTaskFailed(name: string, task: XTask<any>) {
super.onTaskFailed(name, task);
//
// Prevent Moving Forward ...
if (!this.isPageTask(name)) {
return;
}
//
// Set Loading State ...
await this.setLoadingState(false);
//
// Retrieve Exception ...
const exception = task.error;
await this.handleErrorState(exception, true);
//
// Handle Email Verification Error ...
if (task.name === LoginTasks.ResendEmailVerificationLink) {
//
this.uiDisabled = true;
this.showEmailConfirm = true;
//
this.detectChanges();
return;
}
//
// Handle Request Reset Password Error ...
if (task.name === LoginTasks.RequestResetPassword) {
//
this.uiDisabled = true;
this.showResetPassword = true;
//
this.detectChanges();
return;
}
//
// Handle Other Errors ...
switch (exception.id) {
//
// Email Not Confirmed ...
case 135:
//
this.uiDisabled = true;
this.showEmailConfirm = true;
break;
//
// User Disabled ...
case 117:
//
this.uiDisabled = true;
this.showEmailConfirm = true;
break;
//
// Login Failed (Username is Ok ... Password Mistmatch ...) ...
case 106:
//
this.uiDisabled = true;
this.showResetPassword = true;
break;
}
//
// Apply Changes ...
this.detectChanges();
}
//#endregion
//
//#region UI Providers ...
//#endregion
//
//#region UI Handlers ...
/**
* Handle Login ...
*/
async handleLogin() {
//
// Prevent Moving Forward when Context is Busy ...
if (this.loading || this.uiDisabled || isNullOrUndefined(this.model)) {
return;
}
//
if (this.lock) {
this.lock = false;
}
//
// Prepare User Select By ...
const model = {
...this.model,
userSelectBy: this.model.userSelectBy.toLowerCase(),
};
//
// Prepare Task Requirements ...
const mTaskName = LoginTasks.Login;
const mTask = this.sharedService.authService.login(model);
//
// Add or Update Task to Queue ...
await this.addOrUpdateTask({
name: mTaskName,
priority: 0,
affectLoading: true,
task: mTask,
});
//
// Dispatch Task ...
await this.dispatchTask(mTaskName);
}
/**
* Update Model ...
*
* @param state
*/
async handleModelStateChange(state: VModelState<XLoginRequestDto>) {
//
// Update Model Fields ...
this.model.device = state.model.device;
this.model.language = state.model.language;
this.model.password = state.model.password;
this.model.userSelectBy = state.model.userSelectBy;
}
/**
* Handling Lock State ...
*
* @param event
* @returns
*/
handleLockStateChanged(event: { id: string; state: boolean }) {
//
if (isNullOrUndefined(event)) {
return;
}
//
this.lock = event.state;
}
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Private Actions ...
//
//#region Task Providers ...
private getTaskNames() {
return [
//
LoginTasks.Login,
LoginTasks.RequestResetPassword,
LoginTasks.ResendEmailVerificationLink,
];
}
private async isPageTask(key: string | any) {
//
const pageTasks = this.getTaskNames();
const mTaskKey = getTaskName(key);
const result = pageTasks.includes(mTaskKey);
//
return result;
}
//#endregion
//
//#region Task Handlers ...
private handleUserAuthenticated() {
//
const hasAfterLoginRedirectPage = !isNullOrUndefined(
this.config.afterLoginRedirectPage
);
//
const url = this.managerService.isContainQueryParam(XParam.ReturnUrl)
? this.managerService.getQueryParamValOfType<string>(XParam.ReturnUrl)
: hasAfterLoginRedirectPage
? this.managerService.getPageRoute(this.config.afterLoginRedirectPage)
: this.managerService.getPageRoute(Pages.Home);
//
const mSubscription = this.sharedService.state$
.asObservable()
.pipe(filter((res) => !isNullOrUndefined(res) && res.isLoggedIn === true))
// tslint:disable-next-line: deprecation
.subscribe(async () => {
//
this.runTimedOut(async () => {
//
if (this.lock) {
this.lock = false;
}
//
await this.managerService.navigateByUrlReplace(url);
});
//
mSubscription.unsubscribe();
});
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\logout\logout.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\logout\logout.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\logout\logout.page.ts
import { Pages } from 'src/app/config/page.config';
import { XSideType } from 'x-framework-components';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
const LogoutKeys = {
LoggingOutID: 'logging_out',
};
@Component({
selector: 'app-logout',
templateUrl: './logout.page.html',
styleUrls: ['./logout.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line:component-class-suffix
export class LogoutPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = false;
showToolbarContent = false;
titleRes = this.AppResourceIDs.logout_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
async afterViewInit() {
super.afterViewInit();
//
await this.handleLogoutAction();
}
//#endregion
//
//#region Private Actions ...
private async handleLogoutAction() {
//
await this.managerService.dialogService.presentLoading({
id: LogoutKeys.LoggingOutID,
message: this.resourceProvider(this.AppResourceIDs.logging_out),
});
//
this.loading = true;
//
await this.sharedService.authService.logout();
this.managerService.settingsService.restart();
//
this.loading = false;
this.managerService.dialogService.loadingController.dismiss(
LogoutKeys.LoggingOutID
);
//
await this.managerService.navigateByPageReplace(Pages.Startup);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\phone-confirm\phone-confirm.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Phone Confirm ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\phone-confirm\phone-confirm.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\phone-confirm\phone-confirm.page.ts
import { XSideType } from 'x-framework-components';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-phone-confirm',
templateUrl: './phone-confirm.page.html',
styleUrls: ['./phone-confirm.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PhoneConfirmPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.phone_confirm_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\profile\profile.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Profile ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\profile\profile.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\profile\profile.page.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-profile',
templateUrl: './profile.page.html',
styleUrls: ['./profile.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProfilePage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.profile_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\register\register.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<!-- Register -->
<v-register
[isInPage]="true"
[wrapWithCard]="true"
[cardTitle]="AppResourceIDs.register"
></v-register>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\register\register.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\register\register.page.ts
import { XSideType } from 'x-framework-components';
import { VPageComponent } from 'src/app/views/public-api';
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-register',
templateUrl: './register.page.html',
styleUrls: ['./register.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line: component-class-suffix
export class RegisterPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
titleRes = this.AppResourceIDs.register_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region Register Handlers ...
//#endregion
//
//#region Task Handlers ...
//#endregion
//
//#region UI Providers ...
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\reset-password\reset-password.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Reset Password ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\reset-password\reset-password.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\reset-password\reset-password.page.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
import { XSideType } from 'x-framework-components';
@Component({
selector: 'app-reset-password',
templateUrl: './reset-password.page.html',
styleUrls: ['./reset-password.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ResetPasswordPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.reset_password_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType = XSideType.Overlay;
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\search-profiles\search-profiles.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate> Search Profiles ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\search-profiles\search-profiles.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\account\search-profiles\search-profiles.page.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-search-profiles',
templateUrl: './search-profiles.page.html',
styleUrls: ['./search-profiles.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SearchProfilesPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
toolbarShowSubTitle = true;
showToolbarEndSlot = true;
showToolbarContent = false;
titleRes = this.AppResourceIDs.search_profiles_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\error\error.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<v-error
[isInPage]="true"
[errorTitle]="''"
[wrapWithCard]="true"
*ngIf="hasChildValue(errors) | async"
[errors]="getArrayValue(errors) | async"
[cardTitle]="AppResourceIDs.error_page_title"
>
</v-error>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\error\error.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\error\error.page.ts
import { Pages } from 'src/app/config/page.config';
import { XButtonType } from 'x-framework-components';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
import { toArray, XOneOrManyType, XParam } from 'x-framework-core';
@Component({
selector: 'app-error',
templateUrl: './error.page.html',
styleUrls: ['./error.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line: component-class-suffix
export class ErrorPage extends VPageComponent {
//
//#region Props ...
hasSide = false;
toolbarHasBack = true;
toolbarShowBack = true;
showToolbarEndSlot = false;
showToolbarContent = false;
toolbarShowSubTitle = true;
titleRes = this.AppResourceIDs.error_page_title;
toolbarSubTitle = this.resourceProvider(this.titleRes);
//
errors: XOneOrManyType<string>;
returnUrl = this.managerService.getPageRoute(Pages.Startup);
//
toolbarBackHandler = () => {
this.managerService.navigateByUrlReplace(this.returnUrl);
};
//
//#region Private/ReadOnly ...
readonly ButtonTypes = Object.assign({}, XButtonType);
//#endregion
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycle ...
parseParams(): void {
super.parseParams();
//
this.errors = [
...toArray(
this.managerService.getStateValOfType<XOneOrManyType<string>>(
XParam.Errors
)
),
];
//
const isContainsReturnUrl = this.managerService.isContainState(
XParam.ReturnUrl
);
if (isContainsReturnUrl) {
this.returnUrl = this.managerService.getStateValOfType<string>(
XParam.ReturnUrl
);
}
//
if (this.hasChild(this.errors)) {
this.detectChanges();
}
}
//#endregion
//
//#region Action Handlers ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-page.module.ts
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AuthGuard } from 'x-framework-identity-sdk';
import { XCanDeactivateGuard } from 'x-framework-core';
import { BaseRoutes } from 'src/app/config/page.config';
import { ViewsModule } from 'src/app/views/views.module';
import { FileRoutes } from 'src/app/views/files/v-file.configs';
import { FilesExplorerPage } from './files-explorer/files-explorer.page';
import { FilesManagementPage } from './files-management/files-management.page';
@NgModule({
declarations: [FilesExplorerPage, FilesManagementPage],
imports: [
ViewsModule,
RouterModule.forChild([
{
pathMatch: 'full',
path: BaseRoutes.Default,
redirectTo: FileRoutes.FilesExplorer,
},
//
// Explorer ...
{
canActivate: [AuthGuard],
component: FilesExplorerPage,
canDeactivate: [XCanDeactivateGuard],
path: FileRoutes.FilesExplorer,
},
//
// Management ...
{
canActivate: [AuthGuard],
component: FilesManagementPage,
canDeactivate: [XCanDeactivateGuard],
path: FileRoutes.FilesManagement,
},
{
pathMatch: 'full',
path: BaseRoutes.Unknown,
redirectTo: FileRoutes.Default,
},
]),
],
exports: [ViewsModule, RouterModule, FilesExplorerPage, FilesManagementPage],
})
export class FilesPageModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-base\files-base.provider.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
#pagePresenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
(xOnEscapeGlobal)="handleGlobalEscapePressed($event)"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<!-- Query Presenter -->
<v-query-presenter
[loading]="loading"
[tabsLock]="tabsLock"
[tabsColor]="tabsColor"
[showEmpty]="showEmpty"
[emptyColor]="emptyColor"
[addTabTitle]="addTabTitle"
[itemTemplate]="itemTemplate"
[emptyMessage]="emptyMessage"
[tabsCssClass]="tabsCssClass"
[tabsLoopTabs]="tabsLoopTabs"
[tabsSwipable]="tabsSwipable"
[listTabTitle]="listTabTitle"
[emptyCssClass]="emptyCssClass"
[showPaginator]="showPaginator"
[tabsAlignTabs]="tabsAlignTabs"
[showSearchBar]="showSearchBar"
[searchBarType]="searchBarType"
[searchBarValue]="searchBarValue"
[searchBarTitle]="searchBarTitle"
[searchBarColor]="searchBarColor"
[updateTabTitle]="updateTabTitle"
[paginatorColor]="paginatorColor"
[tabsShowHeader]="tabsShowHeader"
[addItemTemplate]="addItemTemplate"
[emptyHasRefresh]="emptyHasRefresh"
[detailsTabTitle]="detailsTabTitle"
[tabsHeaderColor]="tabsHeaderColor"
[showItemsAsList]="showItemsAsList"
[modelIdentifier]="modelIdentifier"
[uiDisabled]="loading || uiDisabled"
[listItemTemplate]="listItemTemplate"
[listTabCardTitle]="listTabCardTitle"
[listTabCardColor]="listTabCardColor"
[emptyRefreshIcon]="emptyRefreshIcon"
[emptyMessageColor]="emptyMessageColor"
[paginatorPosition]="paginatorPosition"
[tabsSelectedIndex]="tabsSelectedIndex"
[tabsDynamicHeight]="tabsDynamicHeight"
[tabsDisableRipple]="tabsDisableRipple"
[searchBarDebounce]="searchBarDebounce"
[searchBarCssClass]="searchBarCssClass"
[updateItemTemplate]="updateItemTemplate"
[tabsHeaderPosition]="tabsHeaderPosition"
[tabsActiveTabColor]="tabsActiveTabColor"
[searchBarSpellcheck]="searchBarSpellcheck"
[detailsItemTemplate]="detailsItemTemplate"
[wrapListTabWithCard]="wrapListTabWithCard"
[listTabCardIsInPage]="listTabCardIsInPage"
[listTabCardSubTitle]="listTabCardSubTitle"
[emptyForegroundColor]="emptyForegroundColor"
[actionProvider]="queryPresenterActionProvider"
[tabsDisablePagination]="tabsDisablePagination"
[tabsAnimationDuration]="tabsAnimationDuration"
[tabsTabIndicatorColor]="tabsTabIndicatorColor"
[tabsTabPaginatorColor]="tabsTabPaginatorColor"
[searchBarKeyboardType]="searchBarKeyboardType"
[searchBarEnterKeyHint]="searchBarEnterKeyHint"
[emptyRefreshButtonColor]="emptyRefreshButtonColor"
[searchBarForegroundColor]="searchBarForegroundColor"
[searchBarShowClearButton]="searchBarShowClearButton"
[searchBarClearButtonIcon]="searchBarClearButtonIcon"
[searchBarCancelButtonIcon]="searchBarCancelButtonIcon"
[searchBarSearchButtonIcon]="searchBarSearchButtonIcon"
[searchBarSearchInputColor]="searchBarSearchInputColor"
[searchBarShowCancelButton]="searchBarShowCancelButton"
[searchBarCancelButtonTitle]="searchBarCancelButtonTitle"
[emptyForegroundMessageColor]="emptyForegroundMessageColor"
[searchBarClearButtonIconColor]="searchBarClearButtonIconColor"
[searchBarCancelButtonIconColor]="searchBarCancelButtonIconColor"
[searchBarSearchButtonIconColor]="searchBarSearchButtonIconColor"
[emptyForegroundRefreshButtonColor]="emptyForegroundRefreshButtonColor"
[searchBarForegroundSearchInputColor]="searchBarForegroundSearchInputColor"
>
</v-query-presenter>
</ng-template>
<!-- -->
<!-- Item Template -->
<!-- -->
<ng-template #itemTemplate let-item="item">
<v-file
[model]="item"
class="v-item"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[cardTitle]="getModelCardTitle(item)"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
>
<div actions *ngIf="(notNullOrUndefinedValue(item) | async)">
<ng-container *ngIf="itemsActions.has(item.id)">
<!-- Slotting Actions -->
<x-slotter [hasCenterSlot]="false" [layout]="SlotLayouts.HORIZONTAL">
<!-- START -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.START, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
<!-- END -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.END, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
</x-slotter>
</ng-container>
</div>
</v-file>
</ng-template>
<!-- Item Action Presenter -->
<ng-template #itemActionTemplate let-action="action" let-data="data">
<!-- Validate -->
<ng-container
*ngIf="(notNullOrUndefinedValue(data) | async) && (notNullOrUndefinedValue(action) | async)"
>
<!-- Present Action -->
<x-button
[loading]="loading"
[icon]="action.icon"
[title]="action.title"
[color]="action.color"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[iconColor]="ColorNames.Transparent"
(clicked)="handleItemAction(action.id, data)"
[uiDisabled]="loading || uiDisabled || action.disabled"
>
</x-button>
</ng-container>
</ng-template>
<!-- -->
<!-- List Item Template -->
<!-- -->
<ng-template #listItemTemplate let-item="item">
<v-file
class="v-list-item"
[model]="item.data"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[presentType]="FilePresentTypes.AsItem"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
></v-file>
</ng-template>
<!-- Owner Name in Lists Template -->
<ng-template
#listOwnerRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
{{ getOwnerFullName(value) }}
</ng-template>
<!-- -->
<!-- Add Item Template -->
<!-- -->
<ng-template #addItemTemplate>
<v-file
[model]="addModel"
[loading]="loading"
class="v-input-item"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.upload"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="addItemActionProvider"
[presentType]="FilePresentTypes.AsFileUpload"
*ngIf="notNullOrUndefinedValue(addModel) | async"
(lockNotifier)="handleAddViewLockNotifier($event)"
(fileUploadActionFired)="handleFileUploadActionFired($event)"
(fileUploadFilesChanged)="handleFileUploadFilesChanged($event)"
>
</v-file>
</ng-template>
<!-- -->
<!-- Update Item Template -->
<!-- -->
<ng-template #updateItemTemplate>
<v-file
[loading]="loading"
class="v-input-item"
[model]="updateModel"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.update"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsForm"
[actionProvider]="updateItemActionProvider"
*ngIf="notNullOrUndefinedValue(updateModel) | async"
(lockNotifier)="handleUpdateViewLockNotifier($event)"
(dtoModelActionFired)="handleUpdateViewActionFired($event)"
[providedFieldDescriptors]="updateModelViewFieldDescriptors"
>
</v-file>
</ng-template>
<!-- -->
<!-- Details Item Template -->
<!-- -->
<ng-template #detailsItemTemplate>
<v-file
[loading]="loading"
class="v-detail-item"
[model]="detailsModel"
[showValidationErrors]="false"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="detailsItemActionProvider"
[cardTitle]="getModelCardTitle(detailsModel)"
*ngIf="notNullOrUndefinedValue(detailsModel) | async"
[providedFieldDescriptors]="detailsModelViewFieldDescriptors"
>
</v-file>
</ng-template>
<!-- Detail File Name Template -->
<ng-template
#fileNameRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<v-media
[src]="value"
style="--thumbnail-size: 128px;"
[presentType]="MediaPresentTypes.AsThumbnail"
></v-media>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-base\files-base.provider.page.scss
.v-item,
.v-list-item,
.v-input-item,
.v-detail-item {
width: 100%;
}
.v-media-thumbnail-present {
margin: {
left: auto;
right: auto;
}
}
:host ::ng-deep .v-media-thumbnail-present {
margin: {
left: auto !important;
right: auto !important;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-base\files-base.provider.page.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XRef,
nameof,
XFileType,
isFunction,
getFieldNames,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
getComponentSelector,
} from 'x-framework-core';
import {
XFileDto,
XPersonDto,
XReferenceDto,
defaultFileDto,
fileEntityToDto,
} from 'x-saherelm-api-sdk';
import {
XListItem,
XSlotName,
XButtonType,
XListItemOptionSlot,
XActionBarActionFiredModel,
defaultActionBarUploadItem,
} from 'x-framework-components';
import { Observable, Subject } from 'rxjs';
import {
VBaseDtoAction,
VModelFieldTypes,
VBaseDtoActionModel,
VBaseDtoModelAction,
VBaseDtoPresentType,
VModelFieldDescriptor,
VBaseDtoModelFiredActionModel,
} from 'src/app/views/v-dto/v-dto.typings';
import {
defaultEditFabButton,
defaultOptionsFabItem,
defaultRemoveFabButton,
} from 'src/app/views/constants/fab.defaults';
import {
toSimpleFabItems,
XSimpleFabButton,
} from 'src/app/views/typings/simple.fab.typings';
import {
defaultEditSlideOption,
defaultRemoveSlideOption,
defaultDetailsSlideOption,
} from 'src/app/views/constants/slide.options.defaults';
import {
processFilledItems,
VQueryPresenterTabs,
VQueryPresenterAction,
VQueryPresenterActionModel,
VQueryPresenterActionIdentifier,
VQueryFilledItemsProcessingResult,
} from 'src/app/views/v-query-presenter/v-query-presenter.typings';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { sendAction } from 'src/app/views/tools/action-provider.tools';
import { getFileTypeResourceID } from 'src/app/views/files/v-file.tools';
import { getPersonFullName } from 'src/app/views/v-person/v-person.tools';
import { PersonPresentType } from 'src/app/views/v-person/v-person.typings';
import { VFileComponent } from 'src/app/views/files/v-file/v-file.component';
import { VFilePresentType } from 'src/app/views/files/v-file/v-file.typings';
import { VDtoCommonProperties } from 'src/app/views/typings/dto.common.typings';
import { VMediaPresentType } from 'src/app/views/files/v-media/v-media.typings';
import { VPagePresenter } from 'src/app/views/v-page-presenter/v-page-presenter.component';
import { getQuerySelectorOfSpecifiedAttribute } from 'src/app/views/typings/common.typings';
import { XSimpleListItemSlideOption } from 'src/app/views/typings/simple.list-item.typings';
import { VQueryPresenterBasePage } from 'src/app/views/v-query-presenter/v-query-presenter.base.page';
@Component({
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class FilesBaseProviderPage extends VQueryPresenterBasePage {
//
//#region Props ...
toolbarShowTitle = true;
toolbarShowSubTitle = true;
titleRes = this.ResourceIDs.app_name;
toolbarSubTitle = this.resourceProvider(
this.AppResourceIDs.files_management_page_title,
);
toolbarTitle = this.resourceProvider(this.titleRes);
//
@ViewChild('pagePresenter')
pagePresenter!: VPagePresenter;
//
@ViewChild('fileNameRef')
fileNameRef!: TemplateRef<any>;
//
readonly ButtonTypes = Object.assign(XButtonType, {});
readonly ModelFieldNames = getFieldNames(defaultFileDto);
readonly ModelFieldTypes = Object.assign(VModelFieldTypes);
readonly FilePresentTypes = Object.assign(VFilePresentType, {});
readonly MediaPresentTypes = Object.assign(VMediaPresentType, {});
readonly PersonPresentTypes = Object.assign(PersonPresentType, {});
readonly BasePresentTypes = Object.assign(VBaseDtoPresentType, {});
//
// Prepare Commonly Used Properties for Dto Actions ...
// we can Override this for other Purposes ...
commonViewsProperties: VDtoCommonProperties = {
isInPage: false,
wrapWithCard: false,
labelColor: undefined,
valueColor: undefined,
fieldFormButtonPosition: undefined,
fieldFormIcon: undefined,
fieldFormIconColor: undefined,
fieldFormApplyIcon: undefined,
fieldFormApplyIconColor: undefined,
fieldFormCancelIcon: undefined,
fieldFormCancelIconColor: undefined,
removeCardMargin: undefined,
cardForegroundColor: undefined,
cardCssClass: undefined,
cardColor: undefined,
cardActionProvider: undefined,
formAppearance: undefined,
formActionProvider: undefined,
formRegularColor: undefined,
formAccentColor: undefined,
formHintColor: undefined,
formActiveColor: undefined,
formValidColor: undefined,
formInvalidColor: undefined,
formPendingColor: undefined,
formFillColor: undefined,
formDisabledColor: undefined,
formDisabledFillColor: undefined,
showFormCancelButton: undefined,
formCancelButtonResource: undefined,
formCancelButtonColor: undefined,
formResetButtonResource: undefined,
formResetButtonColor: undefined,
formActionButtonAddResource: undefined,
formActionButtonUpdateResource: undefined,
formActionButtonColor: undefined,
};
//
//#region Query Presenter Props ...
modelIdentifier: string = nameof<XFileDto>('id');
listTabCardTitle: XResourceIdentifier = this.AppResourceIDs.files;
/**
* Presenter Component Action Provider ...
*/
queryPresenterActionProvider = new Subject<VQueryPresenterActionModel>();
//#endregion
//#endregion
/**
* Specified How to get Specific Item using Model Identifier ...
*/
abstract getModelTask(identifier: string): Observable<any>;
/**
* Specified How to Query Items ...
*/
abstract queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>>;
/**
* Param Recieved Action ...
*/
paramAction: string = undefined;
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
await super.afterViewInit();
//
this.applyTakeUntilWrapper(
this.managerService.settingsService.isMobile$,
).subscribe((res) => {
//
if (!isNullOrUndefined(this.activeTab)) {
//
switch (this.activeTab) {
//
case VQueryPresenterTabs.List:
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.Refresh,
});
break;
}
}
});
//
this.handleAttachTemplates();
await this.handleConnectPushService();
}
//#endregion
//
//#region Providers ...
//#endregion
//
//#region List ...
/**
* Provides List Item Template ...
*/
@ViewChild('itemTemplate')
itemTemplate!: TemplateRef<any>;
/**
* Provides List Item Template ...
*/
@ViewChild('listItemTemplate')
listItemTemplate!: TemplateRef<any>;
//
//#region Owner Custom Template for List Item View ...
private LIST_OWNER_REF: TemplateRef<any>;
@ViewChild('listOwnerRef')
set listOwnerRef(v: TemplateRef<any>) {
//
this.LIST_OWNER_REF = v;
this.applyListOwnerRef();
}
get listOwnerRef() {
return this.LIST_OWNER_REF;
}
//#endregion
/**
* Query Object for Fetching Data ...
*/
query: XQueryDto = {
page: 1,
filter: '',
pageSize: this.config.defaultPageSize,
sortBy: nameof<XFileDto>('uploadedOn'),
};
/**
* a Flag for Detect Has Query result or not ...
*/
hasQueryResultError: boolean = false;
/**
* Fetching Data Result Model ...
*/
dtoQueryResult: XQueryResultDto<XFileDto> = undefined;
/**
* Specified Action Provider ...
*/
listItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specified View Properties ...
*/
listItemViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
};
/**
* Specified Field Descriptors for List Items ...
*/
listItemViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.type,
label: this.ResourceIDs.type,
key: this.ModelFieldNames.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(
this.resourceProvider(getFileTypeResourceID(value)),
);
},
} as VModelFieldDescriptor<XFileType>,
//
// FileName ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.fileName,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.fileName,
label: this.AppResourceIDs.file_name,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.name,
key: this.ModelFieldNames.name,
label: this.AppResourceIDs.name,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// UploadedOn ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
type: this.ModelFieldTypes.Date,
value: defaultFileDto.uploadedOn,
key: this.ModelFieldNames.uploadedOn,
label: this.AppResourceIDs.uploaded_on,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
} as VModelFieldDescriptor<Date>,
//
// Path ...
{
hide: false,
hideLabel: true,
allowFieldForm: false,
hasCustomTemplate: true,
value: defaultFileDto.path,
key: this.ModelFieldNames.path,
label: this.AppResourceIDs.path,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
hasCustomTemplate: true,
showContentBelowLabel: true,
value: defaultFileDto.owner,
key: this.ModelFieldNames.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
} as VModelFieldDescriptor<XPersonDto>,
// //
// // References ...
// {
// hide: false,
// hideLabel: false,
// allowFieldForm: false,
// value: defaultFileDto.references,
// type: this.ModelFieldTypes.Array,
// key: this.ModelFieldNames.references,
// label: this.AppResourceIDs.references,
// hasCustomTemplate: true,
// showContentBelowLabel: true,
// pushArrayChildsOnceToTemplate: true,
// } as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
/**
* Get File Owner Full Name ...
*
* @param model
* @returns
*/
getOwnerFullName(model: XPersonDto) {
return getPersonFullName(model);
}
/**
* Hold each Items Provided Actions based on Identifier ...
*/
itemsActions = new Map<string, XRef<Array<XSimpleListItemSlideOption>>>();
/**
* Prepare Item Actions based on Slot ...
*/
getSlottedItemActions(slot: XSlotName, model: XFileDto) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
if (
isNullOrUndefined(model) ||
isNullOrEmptyString(slot) ||
isNullOrEmptyString(model.id) ||
isNullOrUndefined(this.itemsActions) ||
!this.itemsActions.has(model.id)
) {
return result;
}
//
let _slot: XListItemOptionSlot =
slot === XSlotName.START || slot === XSlotName.TOP ? 'start' : 'end';
//
result = this.itemsActions
.get(model.id)
.value.filter((a) => a.slot === _slot);
//
return result;
}
/**
* Handle Non As List Item's Action ...
*
* @param id
* @param data
*/
async handleItemAction(id: string, data: XFileDto) {
//
// Validate ...
if (
this.loading ||
this.uiDisabled ||
isNullOrEmptyString(id) ||
isNullOrUndefined(data)
) {
return;
}
//
await this.doItemActions({ id, data });
}
/**
* Apply List Owner Template to Descriptors ...
*
* @returns
*/
applyListOwnerRef() {
//
if (isNullOrUndefined(this.LIST_OWNER_REF)) {
return;
}
//
this.listItemViewFieldDescriptors = this.listItemViewFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.owner
? this.LIST_OWNER_REF
: fd.template,
};
},
);
}
/**
* Handle Retrieve Data based on XQuery ...
*
* @param query
* @returns
*/
async handleGetQueryResult(
query: XQueryDto,
): Promise<XQueryResultDto<XFileDto>> {
//
let result: XQueryResultDto<XFileDto> = undefined;
//
// Start Loading ...
this.hasQueryResultError = false;
await this.setLoadingState(true);
//
// Try to Prepare Result ...
try {
//
// Retrieve File Dto Result ...
this.dtoQueryResult = await this.getValueAsync(
this.queryModelTask(query),
);
//
// Prepare Result ...
result = {
...result,
page: this.dtoQueryResult.page,
pageSize: this.dtoQueryResult.pageSize,
totalItems: this.dtoQueryResult.totalItems,
totalPages: this.dtoQueryResult.totalPages,
totalFilteredItems: this.dtoQueryResult.totalFilteredItems,
totalFilteredPages: this.dtoQueryResult.totalFilteredPages,
};
//
// Converts XFileDto to XFileDto ...
result.items = this.dtoQueryResult.items.map((i) => i);
//
// Remove Scrolling Info ...
if (
!isNullOrUndefined(this.query) &&
this.query.filter !== query.filter
) {
//
this.removeScrollingInfo();
this.infinityScrollDisabled = true;
}
//
this.query = query;
} catch {
//
this.hasQueryResultError = true;
await this.handleCustomError('QueryFilesError');
return result;
}
//
// Stop Loading ...
await this.setLoadingState(false);
//
return result;
}
/**
* Get Specified Model Based on Identifier ...
*
* @param identifier
* @param applyState
* @returns
*/
async handleGetSpecifiedModel(identifier: string, applyState = false) {
//
if (isNullOrEmptyString(identifier)) {
return;
}
//
let response: any = undefined;
try {
//
if (applyState) {
await this.setLoadingState(true);
}
//
response = await this.getValueAsync(this.getModelTask(identifier));
//
} catch {
//
await this.handleCustomError('GetFileError');
return;
}
//
if (applyState) {
await this.setLoadingState(false);
}
//
return response;
}
/**
* Provide List Actions for Specific Model ...
*
* @param model
* @returns
*/
async prepareListActions(model: XFileDto) {
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
//
return this.provideItemOptions(userId, isAdmin, model);
}
/**
* Validate List Items ...
*
* @param items
* @returns
*/
async validateAndPrepareListItems(
items: Array<XListItem<XFileDto>>,
): Promise<Array<XListItem<XFileDto>>> {
//
let result: Array<XListItem<XFileDto>> = [];
//
// Preparing Slide Options ...
result = await Promise.all([
...items.map(async (i) => {
return {
...i,
slideOptions: [...(await this.prepareListActions(i.data))],
};
}),
]);
//
return result;
}
/**
* Prepare Result for Non As List Presenting Items ...
*
* @param current
* @param recieved
*/
async requestFillingItems(
exists: Array<XFileDto>,
recieved: Array<XFileDto>,
): Promise<VQueryFilledItemsProcessingResult<XFileDto>> {
//
let result: VQueryFilledItemsProcessingResult<XFileDto> =
processFilledItems(exists, recieved, 'id');
//
// Filling Actions ...
if (!isNullOrUndefined(result)) {
//
// News Items Actions ...
if (!isNullOrUndefined(result.news) && this.hasChild(result.news)) {
//
result.news.forEach(async (i) => {
//
const actions = await this.prepareListActions(i);
this.itemsActions.set(i.id, { value: actions });
});
}
//
// Changed Items Actions ...
if (!isNullOrUndefined(result.changed) && this.hasChild(result.changed)) {
//
result.changed.forEach(async (i) => {
//
if (this.itemsActions.has(i.identifier)) {
//
const actions = await this.prepareListActions(i.data);
this.itemsActions.set(i.identifier, { value: actions });
}
});
}
}
//
return result;
}
/**
* When Non as List Items Filled, this method call for Change Detection ...
*
* @param ids
*/
async handleRefreshViews(ids: Array<string>): Promise<void> {
await super.handleRefreshViews(ids);
//
if (isNullOrUndefined(ids) || !this.hasChild(ids)) {
return;
}
//
const selector = getComponentSelector(VFileComponent);
ids.forEach((id) => {
//
const iSelector = `${selector}[${nameof<XFileDto>(
'id',
)}="${id}"][${nameof<VFileComponent>('presentType')}="${
VBaseDtoPresentType.AsView
}"]`;
this.waitForElement(iSelector).then((el) => {
this.listItemActionProvider.next({ action: VBaseDtoAction.Refresh });
});
});
}
//#endregion
//
//#region Add ...
/**
* Provides Template for Adding Items ...
*/
@ViewChild('addItemTemplate')
addItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Add ...
*/
addModel: XFileDto = {
...defaultFileDto,
};
/**
* Specified Action Provider ...
*/
addItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Add ...
*/
addModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Add Model Fields ...
*/
addModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.type,
label: this.ResourceIDs.type,
key: this.ModelFieldNames.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(
this.resourceProvider(getFileTypeResourceID(value)),
);
},
} as VModelFieldDescriptor<XFileType>,
//
// FileName ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.fileName,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.fileName,
label: this.AppResourceIDs.file_name,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.name,
key: this.ModelFieldNames.name,
label: this.AppResourceIDs.name,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// UploadedOn ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
type: this.ModelFieldTypes.Date,
value: defaultFileDto.uploadedOn,
key: this.ModelFieldNames.uploadedOn,
label: this.AppResourceIDs.uploaded_on,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
} as VModelFieldDescriptor<Date>,
//
// Path ...
{
hide: false,
hideLabel: true,
allowFieldForm: false,
hasCustomTemplate: true,
value: defaultFileDto.path,
key: this.ModelFieldNames.path,
label: this.AppResourceIDs.path,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
hasCustomTemplate: true,
showContentBelowLabel: true,
value: defaultFileDto.owner,
key: this.ModelFieldNames.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
} as VModelFieldDescriptor<XPersonDto>,
// //
// // References ...
// {
// hide: false,
// hideLabel: false,
// allowFieldForm: false,
// value: defaultFileDto.references,
// type: this.ModelFieldTypes.Array,
// key: this.ModelFieldNames.references,
// label: this.AppResourceIDs.references,
// hasCustomTemplate: true,
// showContentBelowLabel: true,
// pushArrayChildsOnceToTemplate: true,
// } as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleAddViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Handle Actiond Fired form FileUpload ...
*
* @param event
* @returns
*/
async handleFileUploadActionFired(event: XActionBarActionFiredModel) {
//
// Validate ...
if (isNullOrUndefined(event) || isNullOrEmptyString(event.action)) {
return;
}
//
// Upload ...
if (event.action === defaultActionBarUploadItem.id) {
//
await this.handleAddModel({ ...defaultFileDto });
}
}
/**
* Holding File(s) for Upload ...
*/
filesForUpload: Array<File> = [];
handleFileUploadFilesChanged(event: Array<File>) {
//
// Update Files For Upload List ...
if (isNullOrUndefined(event)) {
this.filesForUpload = [];
} else {
this.filesForUpload = [...event];
}
}
/**
* Handle Cancel Add Form View ...
*
* @param event
*/
async handleAddViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List,
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<XFileDto> = {
...event.payload.model,
} as Required<XFileDto>;
await this.handleAddModel(pModel);
}
}
/**
* Do Add Model ...
*
* @param model
* @returns
*/
async handleAddModel(model: XFileDto) {
//
if (isNullOrUndefined(model) || !this.hasChild(this.filesForUpload)) {
return;
}
//
await this.setLoadingState(true);
//
let response: Array<XFileDto> = undefined;
try {
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.files.fileEntityPushService
.connectionId;
//
// Issue Task ...
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.files.fileProviderService.upload(
[...this.filesForUpload], // Model ...
connectionId, // Connection Id ...
),
);
} catch (ex) {
//
await this.handleCustomError('AddModelError');
return;
}
//
this.lock = false;
this.filesForUpload = [];
this.addModel = undefined;
this.showSuccess();
await this.setLoadingState(false);
this.sendQueryPresenterAction(VQueryPresenterAction.ResetQuery, true);
}
//#endregion
//
//#region Update ...
/**
* Provides Template for Update Items ...
*/
@ViewChild('updateItemTemplate')
updateItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Update ...
*/
updateModel: XFileDto = undefined;
/**
* Specified Action Provider ...
*/
updateItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Update ...
*/
updateModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Update Model Fields ...
*/
updateModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.type,
label: this.ResourceIDs.type,
key: this.ModelFieldNames.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(
this.resourceProvider(getFileTypeResourceID(value)),
);
},
} as VModelFieldDescriptor<XFileType>,
//
// FileName ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.fileName,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.fileName,
label: this.AppResourceIDs.file_name,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.name,
key: this.ModelFieldNames.name,
label: this.AppResourceIDs.name,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// UploadedOn ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
type: this.ModelFieldTypes.Date,
value: defaultFileDto.uploadedOn,
key: this.ModelFieldNames.uploadedOn,
label: this.AppResourceIDs.uploaded_on,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
} as VModelFieldDescriptor<Date>,
//
// Path ...
{
hide: false,
hideLabel: true,
allowFieldForm: false,
hasCustomTemplate: true,
value: defaultFileDto.path,
key: this.ModelFieldNames.path,
label: this.AppResourceIDs.path,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
hasCustomTemplate: true,
showContentBelowLabel: true,
value: defaultFileDto.owner,
key: this.ModelFieldNames.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
} as VModelFieldDescriptor<XPersonDto>,
// //
// // References ...
// {
// hide: false,
// hideLabel: false,
// allowFieldForm: false,
// value: defaultFileDto.references,
// type: this.ModelFieldTypes.Array,
// key: this.ModelFieldNames.references,
// label: this.AppResourceIDs.references,
// hasCustomTemplate: true,
// showContentBelowLabel: true,
// pushArrayChildsOnceToTemplate: true,
// } as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleUpdateViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Tags Actions Provider for Details Section ...
*
* @param t
* @returns
*/
detailsTagsActionsProvider: (t: string) => Array<XSimpleListItemSlideOption> =
(t) => {
return [];
};
/**
* Files Actions Provider for Details Section ...
*
* @param f
* @returns
*/
detailsFilesActionsProvider: (
f: XFileDto | File,
) => Array<XSimpleListItemSlideOption> = (f) => {
return [];
};
/**
* Handle Cancel Update Form View ...
*
* @param event
*/
async handleUpdateViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List,
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<XFileDto> = {
...event.payload.model,
} as Required<XFileDto>;
await this.handleUpdateModel(pModel);
}
}
/**
* Do Update Model ...
*
* @param model
* @returns
*/
async handleUpdateModel(model: XFileDto) {
//
const prevTab = this.prevTab;
//
if (isNullOrUndefined(model)) {
return;
}
//
await this.setLoadingState(true);
//
let response: XFileDto = undefined;
try {
//
// Prepare Dto Model to Add ...
const dto =
!isNullOrUndefined(this.dtoQueryResult) &&
!isNullOrUndefined(this.dtoQueryResult.items) &&
this.hasChild(this.dtoQueryResult.items)
? this.dtoQueryResult.items.find((i) => i.id === model.id)
: undefined;
if (isNullOrUndefined(dto)) {
throw 'UpdateModelError';
}
//
// Preparing Update Model ...
const dtoModel = { ...model };
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.files.fileEntityPushService
.connectionId;
//
// Filling Owner ID ...
dtoModel.ownerId = await this.getValueAsync(this.sharedService.userId$);
//
// Issue Task ...
// response = await this.getValueAsync(
// this.sharedService.saherElmAPIService.files.fileProviderService.update(
// dtoModel.id,
// dtoModel,
// connectionId
// )
// );
} catch (ex) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
if (isNullOrUndefined(response)) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
this.lock = false;
this.updateModel = undefined;
await this.setLoadingState(false);
this.showSuccess();
//
this.prevTab = prevTab;
model = { ...response };
const actions = await this.prepareListActions(model);
//
if (this.prevTab === VQueryPresenterTabs.Details) {
//
this.detailsModel = {
...model,
};
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
model,
actions,
key: nameof<XFileDto>('id'),
});
//
// Check Handle Back ...
if (
this.toolbarHasBack &&
this.toolbarShowBack &&
isFunction(this.toolbarBackHandler)
) {
await this.toolbarBackHandler();
}
}
//#endregion
//
//#region Details ...
/**
* Provides Template for Details Items ...
*/
@ViewChild('detailsItemTemplate')
detailsItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Details ...
*/
detailsModel: XFileDto = undefined;
/**
* Specified Action Provider ...
*/
detailsItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Details ...
*/
detailsModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Details Model Fields ...
*/
detailsModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.type,
label: this.ResourceIDs.type,
key: this.ModelFieldNames.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(
this.resourceProvider(getFileTypeResourceID(value)),
);
},
} as VModelFieldDescriptor<XFileType>,
//
// FileName ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.fileName,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.fileName,
label: this.AppResourceIDs.file_name,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
value: defaultFileDto.name,
key: this.ModelFieldNames.name,
label: this.AppResourceIDs.name,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// UploadedOn ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
type: this.ModelFieldTypes.Date,
value: defaultFileDto.uploadedOn,
key: this.ModelFieldNames.uploadedOn,
label: this.AppResourceIDs.uploaded_on,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
} as VModelFieldDescriptor<Date>,
//
// Path ...
{
hide: false,
hideLabel: true,
allowFieldForm: false,
hasCustomTemplate: true,
value: defaultFileDto.path,
key: this.ModelFieldNames.path,
label: this.AppResourceIDs.path,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
hasCustomTemplate: true,
showContentBelowLabel: true,
value: defaultFileDto.owner,
key: this.ModelFieldNames.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
} as VModelFieldDescriptor<XPersonDto>,
// //
// // References ...
// {
// hide: false,
// hideLabel: false,
// allowFieldForm: false,
// value: defaultFileDto.references,
// type: this.ModelFieldTypes.Array,
// key: this.ModelFieldNames.references,
// label: this.AppResourceIDs.references,
// hasCustomTemplate: true,
// showContentBelowLabel: true,
// pushArrayChildsOnceToTemplate: true,
// } as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
/**
* Provides Card Title for Specified Model ...
*
* @param model
* @returns
*/
getModelCardTitle(model: XFileDto) {
//
// Since card title's are Rescource ID's ...
return this.applyLocale(`${model.fileName} `);
}
//#endregion
//
//#region Handlers ...
/**
* Handle Errors ...
*
* @param error
*/
async handleCustomError(
error?: VQueryPresenterActionIdentifier,
): Promise<void> {
//
await this.setLoadingState(false);
//
// TODO: Implement this ...
console.log('handle Custom Error: ', error);
}
/**
* Override this for adding additional futures ...
*
* @param tab
*/
async handleStateAfterTabOpen(tab: number) {
await super.handleStateAfterTabOpen(tab);
//
let selector: string = undefined;
let fabs: XSimpleFabButton[] = [];
let model: XFileDto = undefined;
//
// Remove Edit From Fab Actions ...
let actions = await toSimpleFabItems(this.actions);
if (!isNullOrUndefined(actions) && this.hasChild(actions.childs)) {
actions = {
...actions,
childs: [
...actions.childs.filter((ch) => ch.id !== defaultEditFabButton.id),
],
};
}
this.closeActions();
this.actions = undefined;
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
const isAgent = await this.getValueAsync(this.sharedService.isAgent$);
const isAdminAgent = isAdmin || isAgent;
//
switch (tab) {
//
case VQueryPresenterTabs.List:
//
// if Show Items as List ...
if (this.showItemsAsList) {
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.listItemViewProperties },
},
});
} else {
//
// Call each Items Action Provider Separately ...
//
// Prepare Non As List Items Properties ...
const properties: VDtoCommonProperties = {
...this.listItemViewProperties,
isInPage: true,
wrapWithCard: true,
cardShowActions: true, // Since Each Items has Separate Actions ...
};
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...properties },
},
});
}
//
break;
//
case VQueryPresenterTabs.Add:
//
this.addModel = {
...defaultFileDto,
};
//
sendAction({
provider: this.addItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.addModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Update:
//
model = { ...this.updateModel };
//
sendAction({
provider: this.updateItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.updateModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Details:
//
model = { ...this.detailsModel };
//
sendAction({
provider: this.detailsItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.detailsModelViewProperties },
},
});
//
// Adding Custom Actions ...
//
let isOwner = this.detailsModel.ownerId === userId;
//
// Actions Preparing ...
actions = {
...(!isNullOrUndefined(actions) ? actions : defaultOptionsFabItem),
childs: [
...(!isNullOrUndefined(actions) && this.hasChild(actions.childs)
? actions.childs
: []),
...fabs,
],
};
//
// Issue Action if Waiting for Details ...
if (!isNullOrEmptyString(this.paramAction)) {
//
const action = this.paramAction;
this.paramAction = undefined;
//
await this.doItemActions({
id: action,
data: { ...this.detailsModel },
});
}
break;
}
//
// Here can Handle Actions Permissions and States ...
if (!isNullOrUndefined(actions)) {
//
// Only if model Exists ...
if (!isNullOrUndefined(model)) {
//
// Retrieve Requirements ...
//
const isOwner = model.ownerId === userId;
//
const canRemove = isOwner || isAdmin;
//
// Remove ...
if (
actions.id === defaultRemoveFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultRemoveFabButton.id,
)) >= 0
) {
//
if (actions.id === defaultRemoveFabButton.id) {
actions.disabled =
!canRemove || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultRemoveFabButton.id
? !canRemove ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
}
//
// Update Actions ...
this.actions = {
...actions,
};
}
}
/**
* Override this for adding additional futures ...
*
* @param from
* @param to
*/
async handleStateBeforeTabOpen(from: number, to: number) {
await super.handleStateBeforeTabOpen(from, to);
//
this.closeActions();
//
switch (to) {
//
case VQueryPresenterTabs.List:
//
this.addModel = undefined;
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Add:
//
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Update:
//
this.addModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Details:
//
if (
isNullOrUndefined(this.detailsModel) &&
!isNullOrUndefined(this.updateModel) &&
from === VQueryPresenterTabs.Update
) {
//
this.detailsModel = {
...this.updateModel,
};
}
//
this.addModel = undefined;
this.updateModel = undefined;
break;
}
}
/**
* Here We Do All Items Based Actions ...
*
* @param info
* @returns
*/
async doItemActions(info: { id: string; data: XFileDto }): Promise<void> {
//
if (isNullOrUndefined(info) || isNullOrEmptyString(info.id)) {
return;
}
//
let isKnown = false;
//
// Details ...
isKnown = info.id === defaultDetailsSlideOption.id;
if (isKnown) {
//
this.detailsModel = {
...info.data,
};
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Details,
);
}
//
// Edit ...
isKnown =
info.id === defaultEditSlideOption.id ||
info.id === defaultEditFabButton.id;
if (isKnown) {
//
if (!isNullOrUndefined(info.data)) {
//
this.updateModel = {
...info.data,
};
} else if (
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.updateModel = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(this.updateModel)) {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Update,
);
}
}
//
// Remove ...
isKnown =
info.id === defaultRemoveSlideOption.id ||
info.id === defaultRemoveFabButton.id;
if (isKnown) {
//
if (
isNullOrUndefined(info.data) &&
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
info.data = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(info.data)) {
await this.handleRemoveFile(info.data);
}
}
}
/**
* Handle Specified Param ...
*
* @param identifier
* @param acttion
*/
async handleParamAction(identifier: string, action: string): Promise<void> {
await super.handleParamAction(identifier, action);
//
if (isNullOrEmptyString(identifier) || isNullOrEmptyString(action)) {
//
this.hasParams = false;
return;
}
//
let model = await this.handleGetSpecifiedModel(identifier, true);
if (!isNullOrUndefined(model)) {
//
this.paramAction = action;
this.detailsModel = { ...model };
this.activeTab = VQueryPresenterTabs.Details;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.activeTab,
);
} else {
this.hasParams = false;
}
}
/**
* Handle Removing File ...
*
* @param model
*/
async handleRemoveFile(model: XFileDto) {
//
await this.doConfirmedAction(async () => {
//
await this.setLoadingState(true);
//
try {
//
const connectionId =
this.sharedService.saherElmAPIService.files.fileEntityPushService
.connectionId;
//
await this.getValueAsync(
this.sharedService.saherElmAPIService.files.fileProviderService.remove(
model.id,
connectionId,
),
);
} catch {
//
await this.handleCustomError('RemoveModelError');
return;
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
model,
actions: [],
key: nameof<XFileDto>('id'),
});
//
this.showSuccess();
await this.setLoadingState(false);
//
// Handle Change Tabs if in Details ...
if (
this.activeTab === VQueryPresenterTabs.List ||
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.prevTab = undefined;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List,
);
}
});
}
/**
* Handle Running toolbar Back Handler on Specified Case
* when User Pressed Escape Button ...
*
* @param event
* @returns
*/
async handleGlobalEscapePressed(event: any) {
//
if (
this.lock ||
!this.toolbarHasBack ||
!this.toolbarShowBack ||
!isFunction(this.toolbarBackHandler) ||
[VQueryPresenterTabs.Add, VQueryPresenterTabs.Update].includes(
this.activeTab,
)
) {
return;
}
//
await this.toolbarBackHandler();
}
//#endregion
//
//#region Private ...
private provideItemOptions(userId: string, isAdmin: boolean, item: XFileDto) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
// Validate ...
if (!isNullOrUndefined(item) && !isNullOrEmptyString(userId)) {
//
const isOwner = item.ownerId === userId;
//
const canRemove = isOwner || isAdmin;
//
// Details ...
result.push({
...defaultDetailsSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled,
} as XSimpleListItemSlideOption);
//
// Remove ...
result.push({
...defaultRemoveSlideOption,
slot: 'end',
disabled: this.loading || this.uiDisabled || !canRemove,
} as XSimpleListItemSlideOption);
}
//
return result;
}
private handleAttachTemplates() {
//
// List Item ...
let hasTemplate = !isNullOrUndefined(
this.listItemViewFieldDescriptors.find(
(fd) =>
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
if (!hasTemplate && !isNullOrUndefined(this.fileNameRef)) {
//
this.listItemViewFieldDescriptors = this.listItemViewFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.fileNameRef
: fd.template,
};
},
);
}
//
// Add Item ...
hasTemplate = !isNullOrUndefined(
this.addModelViewFieldDescriptors.find(
(fd) =>
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
if (!hasTemplate && !isNullOrUndefined(this.fileNameRef)) {
//
this.addModelViewFieldDescriptors = this.addModelViewFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.fileNameRef
: fd.template,
};
},
);
}
//
// Update Item ...
hasTemplate = !isNullOrUndefined(
this.updateModelViewFieldDescriptors.find(
(fd) =>
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
if (!hasTemplate && !isNullOrUndefined(this.fileNameRef)) {
//
this.updateModelViewFieldDescriptors =
this.updateModelViewFieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.fileNameRef
: fd.template,
};
});
}
//
// Details ...
hasTemplate = !isNullOrUndefined(
this.detailsModelViewFieldDescriptors.find(
(fd) =>
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
if (!hasTemplate && !isNullOrUndefined(this.fileNameRef)) {
//
this.detailsModelViewFieldDescriptors =
this.detailsModelViewFieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.path &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.fileNameRef
: fd.template,
};
});
}
}
isPushServiceConnected = false;
private async handleConnectPushService() {
//
// Preparing Connections ...
try {
//
// Check Connected Before ...
if (
this.sharedService.saherElmAPIService.files.fileEntityPushService.isConnected()
) {
this.isPushServiceConnected = true;
} else {
//
this.isPushServiceConnected = true;
await this.sharedService.saherElmAPIService.files.fileEntityPushService.connect();
}
} catch {
//
await this.handleCustomError('PushServiceConnection');
return;
}
//
// Check Connected ...
if (!this.isPushServiceConnected) {
return;
}
//
// On Added ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.files.fileEntityPushService
.onAdded$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event)) {
return;
}
});
//
// On Removed ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.files.fileEntityPushService
.onDeleted$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) && isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id,
);
const modelP = fileEntityToDto(event.model, model.owner);
if (!isNullOrUndefined(model) && !isNullOrUndefined(modelP)) {
//
const actions = await this.prepareListActions(modelP);
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
actions,
model: modelP,
key: nameof<XFileDto>('id'),
});
}
//
// Check Details or Edit Tab ...
if (
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.detailsModel) &&
this.detailsModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Details) ||
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.updateModel) &&
this.updateModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Update)
) {
//
// Force Unlock ...
this.lock = false;
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List,
);
}
});
//
// On Updated ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.files.fileEntityPushService
.onUpdated$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) || isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id,
);
if (isNullOrUndefined(model)) {
return;
}
//
let fromEntity: XFileDto = undefined;
try {
fromEntity = await this.getValueAsync(
this.sharedService.saherElmAPIService.files.fileProviderService.get(
event.model.id,
),
);
} catch {
return;
}
//
if (isNullOrUndefined(fromEntity)) {
return;
}
//
const modelP = fileEntityToDto(event.model, fromEntity.owner);
//
if (isNullOrUndefined(modelP)) {
return;
}
//
// Preparing Model List Actions ...
const actions = await this.prepareListActions(modelP);
//
// Updating Model on Query Presenter ...
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
actions,
model: { ...modelP },
key: nameof<XFileDto>('id'),
});
//
// Update Tab ...
if (
!isNullOrUndefined(this.updateModel) &&
modelP.id === this.updateModel.id &&
this.activeTab === VQueryPresenterTabs.Update
) {
//
this.updateModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VFileComponent,
this.ModelFieldNames.id,
this.updateModel.id,
);
await this.waitForElement(selector).then(async (el) => {
//
this.updateItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.updateModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
//
// Details Tab ...
if (
!isNullOrUndefined(this.detailsModel) &&
modelP.id === this.detailsModel.id &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.detailsModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VFileComponent,
this.ModelFieldNames.id,
this.detailsModel.id,
);
await this.waitForElement(selector).then(async (el) => {
//
this.detailsItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.detailsModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-explorer\files-explorer.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-explorer\files-explorer.page.scss
@import '../files-base//files-base.provider.page.scss';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-explorer\files-explorer.page.ts
import { Observable } from 'rxjs';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { FilesBaseProviderPage } from '../files-base/files-base.provider.page';
@Component({
selector: 'files-explorer',
styleUrls: ['./files-explorer.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../files-base/files-base.provider.page.html',
})
export class FilesExplorerPage extends FilesBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsExplorerPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.files.fileProviderService.get(
identifier,
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.files.fileProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-management\files-management.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-management\files-management.page.scss
@import '../files-base//files-base.provider.page.scss';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\files\files-management\files-management.page.ts
import { Observable } from 'rxjs';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { FilesBaseProviderPage } from '../files-base/files-base.provider.page';
@Component({
selector: 'files-management',
styleUrls: ['./files-management.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../files-base/files-base.provider.page.html',
})
export class FilesManagementPage extends FilesBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsManagementPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.files.fileProviderService.get(
identifier,
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.files.fileProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\home\home.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\home\home.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\home\home.page.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-components-home',
templateUrl: './home.page.html',
styleUrls: ['./home.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line:component-class-suffix
export class HomePage extends VPageComponent {
//
//#region Props ...
toolbarShowTitle = true;
toolbarShowSubTitle = true;
titleRes = this.ResourceIDs.app_name;
toolbarSubTitle = this.resourceProvider(this.AppResourceIDs.home_page_title);
toolbarTitle = this.resourceProvider(this.titleRes);
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Register Handlers ...
//#endregion
//
//#region Task Handlers ...
//#endregion
//
//#region UI Providers ...
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Abstracts ...
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\landing\landing.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\landing\landing.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\landing\landing.page.ts
import { XStandardType } from 'x-framework-core';
import { XSideType } from 'x-framework-components';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-landing',
templateUrl: './landing.page.html',
styleUrls: ['./landing.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line: component-class-suffix
export class LandingPage extends VPageComponent {
//
//#region Props ...
hasSide = true;
showFooter = true;
toolbarShowSubTitle = true;
titleRes = this.AppResourceIDs.landing_page_title;
toolbarSubTitle = this.resourceProvider(
this.AppResourceIDs.landing_page_description
);
toolbarTitle = this.resourceProvider(this.titleRes);
/**
* use this for Menu always Visible ...
*/
toggleMenuWhen = '';
sideType: XStandardType<string> = XSideType.Overlay;
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Register Handlers ...
//#endregion
//
//#region Handlers ...
//#endregion
//
//#region Task Handlers ...
//#endregion
//
//#region UI Providers ...
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\landing\landing.typing.ts
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\not-authorized\not-authorized.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\not-authorized\not-authorized.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\not-authorized\not-authorized.page.ts
import { Pages } from 'src/app/config/page.config';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
@Component({
selector: 'app-not-authorized',
templateUrl: './not-authorized.page.html',
styleUrls: ['./not-authorized.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line: component-class-suffix
export class NotAuthorizedPage extends VPageComponent {
//
//#region Page Props ...
hasSide = false;
toolbarHasBack = true;
toolbarShowBack = true;
showToolbarEndSlot = false;
showToolbarContent = false;
toolbarShowSubTitle = true;
toolbarSubTitle = this.resourceProvider(this.titleRes);
titleRes = this.AppResourceIDs.not_authorized_page_title;
//
toolbarBackHandler = () => {
//
// TODO: Fix this ...
// this.managerService.navigateByPageReplace(Pages.);
}
//#endregion
//
//#region Constructor ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\startup\startup.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
[containerBase]="this"
[contentTemplate]="contentTemplate"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\startup\startup.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\startup\startup.page.ts
import {
XPage,
XExceptionIDs,
isNullOrUndefined,
} from 'x-framework-core';
import { Pages } from 'src/app/config/page.config';
import { VPageComponent } from 'src/app/views/v-page/v-page.component';
import { Component, ChangeDetectionStrategy } from '@angular/core';
const StartupKeys = {
AuthLoadingID: 'auth_loading',
};
@Component({
selector: 'app-startup',
templateUrl: './startup.page.html',
styleUrls: ['./startup.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
// tslint:disable-next-line: component-class-suffix
export class StartupPage extends VPageComponent {
//
//#region Configuration Props ...
//
hasSide = false;
showToolbarEndSlot = false;
showToolbarContent = false;
//
hasBlogPage = !isNullOrUndefined(this.config.defaultBlogPage);
hasLandingPage = !isNullOrUndefined(this.config.defaultLandingPage);
//
private loadingModal: HTMLIonLoadingElement;
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycle ...
async onInit() {
super.onInit();
//
await this.handleStartUp();
}
//#endregion
//
//#region Private ...
private async handleStartUp() {
//
await this.presentLoading(
StartupKeys.AuthLoadingID,
this.resourceProvider(this.AppResourceIDs.default_loading)
);
//
// Retrieve Account State ...
const xAccountState = await this.sharedService.authService.currentState();
const isAuthenticated = xAccountState ? xAccountState.isLoggedIn : false;
if (!isAuthenticated) {
//
if (!this.hasBlogPage && !this.hasLandingPage) {
//
await this.dismissLoading(StartupKeys.AuthLoadingID);
this.showError(XExceptionIDs.NotAuthorized);
}
//
await this.finishAction(Pages.Login);
return;
}
//
// Check Token Expiration ...
const isExpiredToken = await this.sharedService.authService.isExpired();
if (isExpiredToken) {
//
try {
//
const mTask = this.sharedService.authService.refreshTokens();
//
const newTokens = await this.getValueAsync(mTask);
//
await this.sharedService.authService.updateUserTokens(
xAccountState,
newTokens
);
//
await this.finishAction(Pages.Home);
} catch {
//
if (!this.hasBlogPage && !this.hasLandingPage) {
await this.handleErrorState(XExceptionIDs.LoginFailed, true);
}
//
await this.sharedService.authService.logout();
await this.finishAction(Pages.Login);
}
return;
}
//
if (!this.hasBlogPage && !this.hasLandingPage) {
//
await this.finishAction(Pages.Login);
return;
}
//
await this.finishAction(Pages.Home);
return;
}
private async finishAction(dest?: XPage) {
//
await this.dismissLoading(StartupKeys.AuthLoadingID);
//
if (this.hasBlogPage && !this.hasLandingPage) {
//
await this.managerService.navigateByPageReplace(
this.config.defaultBlogPage
);
return;
}
//
if (this.hasLandingPage) {
//
await this.managerService.navigateByPageReplace(
this.config.defaultLandingPage
);
return;
}
//
if (!isNullOrUndefined(dest)) {
await this.managerService.navigateByPageReplace(dest);
}
}
private async presentLoading(id: string, message: string) {
//
if (!isNullOrUndefined(this.loadingModal) && this.loadingModal) {
await this.dismissLoading(StartupKeys.AuthLoadingID);
}
//
this.loadingModal = await this.managerService.dialogService.presentLoading({
id,
message,
});
}
private async dismissLoading(id: string) {
//
if (isNullOrUndefined(this.loadingModal)) {
return false;
}
//
const result =
await this.managerService.dialogService.loadingController.dismiss(id);
if (!!result) {
this.loadingModal = undefined;
}
//
return result;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-page.module.ts
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AuthGuard } from 'x-framework-identity-sdk';
import { XCanDeactivateGuard } from 'x-framework-core';
import { BaseRoutes } from 'src/app/config/page.config';
import { ViewsModule } from 'src/app/views/views.module';
import { TagRoutes } from 'src/app/views/tags/v-tag.configs';
import { TagsExplorerPage } from './tags-explorer/tags-explorer.page';
import { TagsManagementPage } from './tags-management/tags-management.page';
@NgModule({
declarations: [TagsExplorerPage, TagsManagementPage],
imports: [
ViewsModule,
RouterModule.forChild([
{
pathMatch: 'full',
path: BaseRoutes.Default,
redirectTo: TagRoutes.TagsExplorer,
},
//
// Tags Explorer ...
{
canActivate: [AuthGuard],
component: TagsExplorerPage,
canDeactivate: [XCanDeactivateGuard],
path: TagRoutes.TagsExplorer,
},
//
// Tags Management ...
{
canActivate: [AuthGuard],
component: TagsManagementPage,
canDeactivate: [XCanDeactivateGuard],
path: TagRoutes.TagsManagement,
},
{
pathMatch: 'full',
path: BaseRoutes.Unknown,
redirectTo: TagRoutes.Default,
},
]),
],
exports: [ViewsModule, RouterModule, TagsExplorerPage, TagsManagementPage],
})
export class TagsPageModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-base\tags-base.provider.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
#pagePresenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
(xOnEscapeGlobal)="handleGlobalEscapePressed($event)"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<!-- Query Presenter -->
<v-query-presenter
[loading]="loading"
[tabsLock]="tabsLock"
[tabsColor]="tabsColor"
[showEmpty]="showEmpty"
[emptyColor]="emptyColor"
[addTabTitle]="addTabTitle"
[itemTemplate]="itemTemplate"
[emptyMessage]="emptyMessage"
[tabsCssClass]="tabsCssClass"
[tabsLoopTabs]="tabsLoopTabs"
[tabsSwipable]="tabsSwipable"
[listTabTitle]="listTabTitle"
[emptyCssClass]="emptyCssClass"
[showPaginator]="showPaginator"
[tabsAlignTabs]="tabsAlignTabs"
[showSearchBar]="showSearchBar"
[searchBarType]="searchBarType"
[searchBarValue]="searchBarValue"
[searchBarTitle]="searchBarTitle"
[searchBarColor]="searchBarColor"
[updateTabTitle]="updateTabTitle"
[paginatorColor]="paginatorColor"
[tabsShowHeader]="tabsShowHeader"
[addItemTemplate]="addItemTemplate"
[emptyHasRefresh]="emptyHasRefresh"
[detailsTabTitle]="detailsTabTitle"
[tabsHeaderColor]="tabsHeaderColor"
[showItemsAsList]="showItemsAsList"
[modelIdentifier]="modelIdentifier"
[uiDisabled]="loading || uiDisabled"
[listItemTemplate]="listItemTemplate"
[listTabCardTitle]="listTabCardTitle"
[listTabCardColor]="listTabCardColor"
[emptyRefreshIcon]="emptyRefreshIcon"
[emptyMessageColor]="emptyMessageColor"
[paginatorPosition]="paginatorPosition"
[tabsSelectedIndex]="tabsSelectedIndex"
[tabsDynamicHeight]="tabsDynamicHeight"
[tabsDisableRipple]="tabsDisableRipple"
[searchBarDebounce]="searchBarDebounce"
[searchBarCssClass]="searchBarCssClass"
[updateItemTemplate]="updateItemTemplate"
[tabsHeaderPosition]="tabsHeaderPosition"
[tabsActiveTabColor]="tabsActiveTabColor"
[searchBarSpellcheck]="searchBarSpellcheck"
[detailsItemTemplate]="detailsItemTemplate"
[wrapListTabWithCard]="wrapListTabWithCard"
[listTabCardIsInPage]="listTabCardIsInPage"
[listTabCardSubTitle]="listTabCardSubTitle"
[emptyForegroundColor]="emptyForegroundColor"
[actionProvider]="queryPresenterActionProvider"
[tabsDisablePagination]="tabsDisablePagination"
[tabsAnimationDuration]="tabsAnimationDuration"
[tabsTabIndicatorColor]="tabsTabIndicatorColor"
[tabsTabPaginatorColor]="tabsTabPaginatorColor"
[searchBarKeyboardType]="searchBarKeyboardType"
[searchBarEnterKeyHint]="searchBarEnterKeyHint"
[emptyRefreshButtonColor]="emptyRefreshButtonColor"
[searchBarForegroundColor]="searchBarForegroundColor"
[searchBarShowClearButton]="searchBarShowClearButton"
[searchBarClearButtonIcon]="searchBarClearButtonIcon"
[searchBarCancelButtonIcon]="searchBarCancelButtonIcon"
[searchBarSearchButtonIcon]="searchBarSearchButtonIcon"
[searchBarSearchInputColor]="searchBarSearchInputColor"
[searchBarShowCancelButton]="searchBarShowCancelButton"
[searchBarCancelButtonTitle]="searchBarCancelButtonTitle"
[emptyForegroundMessageColor]="emptyForegroundMessageColor"
[searchBarClearButtonIconColor]="searchBarClearButtonIconColor"
[searchBarCancelButtonIconColor]="searchBarCancelButtonIconColor"
[searchBarSearchButtonIconColor]="searchBarSearchButtonIconColor"
[emptyForegroundRefreshButtonColor]="emptyForegroundRefreshButtonColor"
[searchBarForegroundSearchInputColor]="searchBarForegroundSearchInputColor"
>
</v-query-presenter>
</ng-template>
<!-- -->
<!-- Item Template -->
<!-- -->
<ng-template #itemTemplate let-item="item">
<v-tag
[model]="item"
class="v-item"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[cardTitle]="getModelCardTitle(item)"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
>
<div actions *ngIf="(notNullOrUndefinedValue(item) | async)">
<ng-container *ngIf="itemsActions.has(item.id.toString())">
<!-- Slotting Actions -->
<x-slotter [hasCenterSlot]="false" [layout]="SlotLayouts.HORIZONTAL">
<!-- START -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.START, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
<!-- END -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.END, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
</x-slotter>
</ng-container>
</div>
</v-tag>
</ng-template>
<!-- Item Action Presenter -->
<ng-template #itemActionTemplate let-action="action" let-data="data">
<!-- Validate -->
<ng-container
*ngIf="(notNullOrUndefinedValue(data) | async) && (notNullOrUndefinedValue(action) | async)"
>
<!-- Present Action -->
<x-button
[loading]="loading"
[icon]="action.icon"
[title]="action.title"
[color]="action.color"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[iconColor]="ColorNames.Transparent"
(clicked)="handleItemAction(action.id, data)"
[uiDisabled]="loading || uiDisabled || action.disabled"
>
</x-button>
</ng-container>
</ng-template>
<!-- -->
<!-- List Item Template -->
<!-- -->
<ng-template #listItemTemplate let-item="item">
<v-tag
class="v-list-item"
[model]="item.data"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[presentType]="TagPresentTypes.AsView"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
></v-tag>
</ng-template>
<!-- -->
<!-- Add Item Template -->
<!-- -->
<ng-template #addItemTemplate>
<v-tag
[model]="addModel"
[loading]="loading"
class="v-input-item"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.add"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsForm"
[actionProvider]="addItemActionProvider"
*ngIf="notNullOrUndefinedValue(addModel) | async"
(lockNotifier)="handleAddViewLockNotifier($event)"
(dtoModelActionFired)="handleAddViewActionFired($event)"
[providedFieldDescriptors]="addModelViewFieldDescriptors"
>
</v-tag>
</ng-template>
<!-- -->
<!-- Update Item Template -->
<!-- -->
<ng-template #updateItemTemplate>
<v-tag
[loading]="loading"
class="v-input-item"
[model]="updateModel"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.update"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsForm"
[actionProvider]="updateItemActionProvider"
*ngIf="notNullOrUndefinedValue(updateModel) | async"
(lockNotifier)="handleUpdateViewLockNotifier($event)"
(dtoModelActionFired)="handleUpdateViewActionFired($event)"
[providedFieldDescriptors]="updateModelViewFieldDescriptors"
>
</v-tag>
</ng-template>
<!-- -->
<!-- Details Item Template -->
<!-- -->
<ng-template #detailsItemTemplate>
<v-tag
[loading]="loading"
class="v-detail-item"
[model]="detailsModel"
[showValidationErrors]="false"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="detailsItemActionProvider"
[cardTitle]="getModelCardTitle(detailsModel)"
*ngIf="notNullOrUndefinedValue(detailsModel) | async"
[providedFieldDescriptors]="detailsModelViewFieldDescriptors"
>
</v-tag>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-base\tags-base.provider.page.scss
.v-item,
.v-list-item,
.v-input-item,
.v-detail-item
{
width: 100%;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-base\tags-base.provider.page.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XRef,
nameof,
isFunction,
getFieldNames,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
getComponentSelector,
} from 'x-framework-core';
import {
XListItem,
XSlotName,
XButtonType,
XListItemOptionSlot,
} from 'x-framework-components';
import {
TVDtoSections,
VBaseDtoAction,
VModelFieldTypes,
VBaseDtoPresentType,
VBaseDtoActionModel,
VBaseDtoModelAction,
VModelFieldDescriptor,
VBaseDtoModelFiredActionModel,
} from 'src/app/views/v-dto/v-dto.typings';
import { Observable, Subject } from 'rxjs';
import { Validators } from '@angular/forms';
import {
defaultEditFabButton,
defaultOptionsFabItem,
defaultRemoveFabButton,
} from 'src/app/views/constants/fab.defaults';
import {
toSimpleFabItems,
XSimpleFabButton,
} from 'src/app/views/typings/simple.fab.typings';
import {
defaultEditSlideOption,
defaultRemoveSlideOption,
defaultDetailsSlideOption,
} from 'src/app/views/constants/slide.options.defaults';
import {
processFilledItems,
VQueryPresenterTabs,
VQueryPresenterAction,
VQueryPresenterActionModel,
VQueryPresenterActionIdentifier,
VQueryFilledItemsProcessingResult,
} from 'src/app/views/v-query-presenter/v-query-presenter.typings';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { sendAction } from 'src/app/views/tools/action-provider.tools';
import { VTagComponent } from 'src/app/views/tags/v-tag/v-tag.component';
import { VTagPresentType } from 'src/app/views/tags/v-tag/v-tag.typings';
import { defaultTagDto, XReferenceDto, XTagDto } from 'x-saherelm-api-sdk';
import { PersonPresentType } from 'src/app/views/v-person/v-person.typings';
import { VDtoCommonProperties } from 'src/app/views/typings/dto.common.typings';
import { VPagePresenter } from 'src/app/views/v-page-presenter/v-page-presenter.component';
import { XSimpleListItemSlideOption } from 'src/app/views/typings/simple.list-item.typings';
import { getQuerySelectorOfSpecifiedAttribute } from 'src/app/views/typings/common.typings';
import { VQueryPresenterBasePage } from 'src/app/views/v-query-presenter/v-query-presenter.base.page';
@Component({
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class TagsBaseProviderPage extends VQueryPresenterBasePage {
//
//#region Props ...
toolbarShowTitle = true;
toolbarShowSubTitle = true;
titleRes = this.ResourceIDs.app_name;
toolbarSubTitle = this.resourceProvider(
this.AppResourceIDs.tags_management_page_title
);
toolbarTitle = this.resourceProvider(this.titleRes);
//
@ViewChild('pagePresenter')
pagePresenter!: VPagePresenter;
//
readonly ButtonTypes = Object.assign(XButtonType, {});
readonly ModelFieldNames = getFieldNames(defaultTagDto);
readonly ModelFieldTypes = Object.assign(VModelFieldTypes);
readonly TagPresentTypes = Object.assign(VTagPresentType, {});
readonly PersonPresentTypes = Object.assign(PersonPresentType, {});
readonly BasePresentTypes = Object.assign(VBaseDtoPresentType, {});
//
// Prepare Commonly Used Properties for Dto Actions ...
// we can Override this for other Purposes ...
commonViewsProperties: VDtoCommonProperties = {
isInPage: false,
wrapWithCard: false,
labelColor: undefined,
valueColor: undefined,
fieldFormButtonPosition: undefined,
fieldFormIcon: undefined,
fieldFormIconColor: undefined,
fieldFormApplyIcon: undefined,
fieldFormApplyIconColor: undefined,
fieldFormCancelIcon: undefined,
fieldFormCancelIconColor: undefined,
removeCardMargin: undefined,
cardForegroundColor: undefined,
cardCssClass: undefined,
cardColor: undefined,
cardActionProvider: undefined,
formAppearance: undefined,
formActionProvider: undefined,
formRegularColor: undefined,
formAccentColor: undefined,
formHintColor: undefined,
formActiveColor: undefined,
formValidColor: undefined,
formInvalidColor: undefined,
formPendingColor: undefined,
formFillColor: undefined,
formDisabledColor: undefined,
formDisabledFillColor: undefined,
showFormCancelButton: undefined,
formCancelButtonResource: undefined,
formCancelButtonColor: undefined,
formResetButtonResource: undefined,
formResetButtonColor: undefined,
formActionButtonAddResource: undefined,
formActionButtonUpdateResource: undefined,
formActionButtonColor: undefined,
};
//
//#region Query Presenter Props ...
modelIdentifier: string = nameof<XTagDto>('id');
listTabCardTitle: XResourceIdentifier = this.AppResourceIDs.tags;
/**
* Presenter Component Action Provider ...
*/
queryPresenterActionProvider = new Subject<VQueryPresenterActionModel>();
//#endregion
//#endregion
//
//#region Abstracts ...
/**
* Specified How to get Specific Item using Model Identifier ...
*/
abstract getModelTask(identifier: string): Observable<any>;
/**
* Specified How to Query Items ...
*/
abstract queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>>;
/**
* Param Recieved Action ...
*/
paramAction: string = undefined;
//#endregion
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
await super.afterViewInit();
//
this.applyTakeUntilWrapper(
this.managerService.settingsService.isMobile$
).subscribe((res) => {
//
if (!isNullOrUndefined(this.activeTab)) {
//
switch(this.activeTab) {
//
case VQueryPresenterTabs.List:
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.Refresh
});
break;
}
}
});
//
await this.handleConnectPushService();
}
//#endregion
//
//#region Providers ...
//#endregion
//
//#region List ...
/**
* Provides List Item Template ...
*/
@ViewChild('itemTemplate')
itemTemplate!: TemplateRef<any>;
/**
* Provides List Item Template ...
*/
@ViewChild('listItemTemplate')
listItemTemplate!: TemplateRef<any>;
/**
* Query Object for Fetching Data ...
*/
query: XQueryDto = {
page: 1,
filter: '',
pageSize: this.config.defaultPageSize,
sortBy: nameof<XTagDto>('id'),
};
/**
* a Flag for Detect Has Query result or not ...
*/
hasQueryResultError: boolean = false;
/**
* Fetching Data Result Model ...
*/
dtoQueryResult: XQueryResultDto<XTagDto> = undefined;
/**
* Specified Action Provider ...
*/
listItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specified View Properties ...
*/
listItemViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
};
/**
* Specified Field Descriptors for List Items ...
*/
listItemViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Tag ...
{
hide: false,
hideLabel: true,
allowFieldForm: false,
key: this.ModelFieldNames.tag,
value: defaultTagDto.tag,
label: this.AppResourceIDs.tag,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
];
/**
* Hold each Items Provided Actions based on Identifier ...
*/
itemsActions = new Map<string, XRef<Array<XSimpleListItemSlideOption>>>();
/**
* Prepare Item Actions based on Slot ...
*/
getSlottedItemActions(slot: XSlotName, model: XTagDto) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
if (
isNullOrUndefined(model) ||
isNullOrEmptyString(slot) ||
isNullOrUndefined(model.id) ||
isNullOrUndefined(this.itemsActions) ||
!this.itemsActions.has(model.id.toString())
) {
return result;
}
//
let _slot: XListItemOptionSlot =
slot === XSlotName.START || slot === XSlotName.TOP ? 'start' : 'end';
//
result = this.itemsActions
.get(model.id.toString())
.value.filter((a) => a.slot === _slot);
//
return result;
}
/**
* Handle Non As List Item's Action ...
*
* @param id
* @param data
*/
async handleItemAction(id: string, data: XTagDto) {
//
// Validate ...
if (
this.loading ||
this.uiDisabled ||
isNullOrEmptyString(id) ||
isNullOrUndefined(data)
) {
return;
}
//
await this.doItemActions({ id, data });
}
/**
* Handle Retrieve Data based on XQuery ...
*
* @param query
* @returns
*/
async handleGetQueryResult(
query: XQueryDto
): Promise<XQueryResultDto<XTagDto>> {
//
let result: XQueryResultDto<XTagDto> = undefined;
//
// Start Loading ...
this.hasQueryResultError = false;
await this.setLoadingState(true);
//
// Try to Prepare Result ...
try {
//
// Retrieve Webinar Dto Result ...
this.dtoQueryResult = await this.getValueAsync(
this.queryModelTask(query)
);
//
// Prepare Result ...
result = {
...result,
page: this.dtoQueryResult.page,
pageSize: this.dtoQueryResult.pageSize,
totalItems: this.dtoQueryResult.totalItems,
totalPages: this.dtoQueryResult.totalPages,
totalFilteredItems: this.dtoQueryResult.totalFilteredItems,
totalFilteredPages: this.dtoQueryResult.totalFilteredPages,
};
//
// Converts XWebinarDto to XTagDto ...
result.items = this.dtoQueryResult.items.map((i) => i);
//
// Remove Scrolling Info ...
if (
!isNullOrUndefined(this.query) &&
this.query.filter !== query.filter
) {
//
this.removeScrollingInfo();
this.infinityScrollDisabled = true;
}
//
this.query = query;
} catch {
//
this.hasQueryResultError = true;
await this.handleCustomError('QueryWebinarsError');
return result;
}
//
// Stop Loading ...
await this.setLoadingState(false);
//
return result;
}
/**
* Get Specified Model Based on Identifier ...
*
* @param identifier
* @param applyState
* @returns
*/
async handleGetSpecifiedModel(identifier: string, applyState = false) {
//
if (isNullOrEmptyString(identifier)) {
return;
}
//
let response: any = undefined;
try {
//
if (applyState) {
await this.setLoadingState(true);
}
//
response = await this.getValueAsync(this.getModelTask(identifier));
//
} catch {
//
await this.handleCustomError('GetWebinarError');
return;
}
//
if (applyState) {
await this.setLoadingState(false);
}
//
return response;
}
/**
* Provide List Actions for Specific Model ...
*
* @param model
* @returns
*/
async prepareListActions(model: XTagDto) {
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
//
return this.provideItemOptions(userId, isAdmin, model);
}
/**
* Validate List Items ...
*
* @param items
* @returns
*/
async validateAndPrepareListItems(
items: Array<XListItem<XTagDto>>
): Promise<Array<XListItem<XTagDto>>> {
//
let result: Array<XListItem<XTagDto>> = [];
//
// Preparing Slide Options ...
result = await Promise.all([
...items.map(async (i) => {
return {
...i,
slideOptions: [...(await this.prepareListActions(i.data))],
};
}),
]);
//
return result;
}
/**
* Prepare Result for Non As List Presenting Items ...
*
* @param current
* @param recieved
*/
async requestFillingItems(
exists: Array<XTagDto>,
recieved: Array<XTagDto>
): Promise<VQueryFilledItemsProcessingResult<XTagDto>> {
//
let result: VQueryFilledItemsProcessingResult<XTagDto> = processFilledItems(
exists,
recieved,
'id'
);
//
// Filling Actions ...
if (!isNullOrUndefined(result)) {
//
// News Items Actions ...
if (!isNullOrUndefined(result.news) && this.hasChild(result.news)) {
//
for (let i of result.news) {
//
const actions = await this.prepareListActions(i);
this.itemsActions.set(i.id.toString(), { value: actions });
}
}
//
// Changed Items Actions ...
if (!isNullOrUndefined(result.changed) && this.hasChild(result.changed)) {
//
for (let i of result.changed) {
//
if (this.itemsActions.has(i.identifier.toString())) {
//
const actions = await this.prepareListActions(i.data);
this.itemsActions.set(i.identifier.toString(), { value: actions });
}
}
}
}
//
return result;
}
/**
* When Non as List Items Filled, this method call for Change Detection ...
*
* @param ids
*/
async handleRefreshViews(ids: Array<string>): Promise<void> {
await super.handleRefreshViews(ids);
//
if (isNullOrUndefined(ids) || !this.hasChild(ids)) {
return;
}
//
const selector = getComponentSelector(VTagComponent);
ids.forEach((id) => {
//
const iSelector = `${selector}[${nameof<XTagDto>(
'id'
)}="${id}"][${nameof<VTagComponent>('presentType')}="${
VBaseDtoPresentType.AsView
}"]`;
this.waitForElement(iSelector).then((el) => {
this.listItemActionProvider.next({ action: VBaseDtoAction.Refresh });
});
});
}
//#endregion
//
//#region Add ...
/**
* Provides Template for Adding Items ...
*/
@ViewChild('addItemTemplate')
addItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Add ...
*/
addModel: XTagDto = {
...defaultTagDto,
};
/**
* Specified Action Provider ...
*/
addItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Add ...
*/
addModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Add Model Fields ...
*/
addModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Tag ...
{
hide: false,
hideLabel: true,
allowFieldForm: true,
key: this.ModelFieldNames.tag,
value: defaultTagDto.tag,
label: this.AppResourceIDs.tag,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
formControlConfig: {
index: 0,
propName: this.ModelFieldNames.tag,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.tag,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleAddViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Handle Cancel Add Form View ...
*
* @param event
*/
async handleAddViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<XTagDto> = {
...event.payload.model,
} as Required<XTagDto>;
await this.handleAddModel(pModel);
}
}
/**
* Do Add Model ...
*
* @param model
* @returns
*/
async handleAddModel(model: XTagDto) {
//
if (isNullOrUndefined(model)) {
return;
}
//
await this.setLoadingState(true);
//
let response: XTagDto = undefined;
try {
//
// Prepare Dto Model to Add ...
const dtoModel = { ...model };
//
// Remove ID ...
delete dtoModel.id;
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.tags.tagEntityPushService
.connectionId;
//
// Issue Task ...
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.tags.tagProviderService.add(
dtoModel, // Model ...
connectionId // Connection Id ...
)
);
} catch (ex) {
//
await this.handleCustomError('AddModelError');
return;
}
//
this.lock = false;
this.addModel = undefined;
this.showSuccess();
await this.setLoadingState(false);
this.sendQueryPresenterAction(VQueryPresenterAction.ResetQuery, true);
}
//#endregion
//
//#region Update ...
/**
* Provides Template for Update Items ...
*/
@ViewChild('updateItemTemplate')
updateItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Update ...
*/
updateModel: XTagDto = undefined;
/**
* Specified Action Provider ...
*/
updateItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Update ...
*/
updateModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Update Model Fields ...
*/
updateModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Tag ...
{
hide: false,
hideLabel: true,
allowFieldForm: true,
key: this.ModelFieldNames.tag,
value: defaultTagDto.tag,
label: this.AppResourceIDs.tag,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
formControlConfig: {
index: 0,
propName: this.ModelFieldNames.tag,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.tag,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleUpdateViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Handle Cancel Update Form View ...
*
* @param event
*/
async handleUpdateViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<XTagDto> = {
...event.payload.model,
} as Required<XTagDto>;
await this.handleUpdateModel(pModel);
}
}
/**
* Do Update Model ...
*
* @param model
* @returns
*/
async handleUpdateModel(model: XTagDto) {
//
const prevTab = this.prevTab;
//
if (isNullOrUndefined(model)) {
return;
}
//
await this.setLoadingState(true);
//
let response: XTagDto = undefined;
try {
//
// Prepare Dto Model to Add ...
const dto =
!isNullOrUndefined(this.dtoQueryResult) &&
!isNullOrUndefined(this.dtoQueryResult.items) &&
this.hasChild(this.dtoQueryResult.items)
? this.dtoQueryResult.items.find((i) => i.id === model.id)
: undefined;
if (isNullOrUndefined(dto)) {
throw 'UpdateModelError';
}
//
// Preparing Update Model ...
const dtoModel = { ...model };
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.tags.tagEntityPushService
.connectionId;
//
// Issue Task ...
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.tags.tagProviderService.update(
dtoModel.id,
dtoModel,
connectionId
)
);
} catch (ex) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
if (isNullOrUndefined(response)) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
this.lock = false;
this.updateModel = undefined;
await this.setLoadingState(false);
this.showSuccess();
//
this.prevTab = prevTab;
model = { ...response };
const actions = await this.prepareListActions(model);
//
if (this.prevTab === VQueryPresenterTabs.Details) {
//
this.detailsModel = {
...model,
};
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
model,
actions,
key: nameof<XTagDto>('id'),
});
//
// Check Handle Back ...
if (
this.toolbarHasBack &&
this.toolbarShowBack &&
isFunction(this.toolbarBackHandler)
) {
await this.toolbarBackHandler();
}
}
//#endregion
//
//#region Details ...
/**
* Provides Template for Details Items ...
*/
@ViewChild('detailsItemTemplate')
detailsItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Details ...
*/
detailsModel: XTagDto = undefined;
/**
* Specified Action Provider ...
*/
detailsItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Details ...
*/
detailsModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Details Model Fields ...
*/
detailsModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Tag ...
{
hide: false,
hideLabel: true,
allowFieldForm: true,
key: this.ModelFieldNames.tag,
value: defaultTagDto.tag,
label: this.AppResourceIDs.tag,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// References ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.references,
value: defaultTagDto.references,
label: this.AppResourceIDs.references,
type: this.ModelFieldTypes.Array,
showContentBelowLabel: true,
pushArrayChildsOnceToTemplate: true,
hasCustomTemplate: true,
} as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
/**
* Provides Card Title for Specified Model ...
*
* @param model
* @returns
*/
getModelCardTitle(model: XTagDto) {
//
// Since card title's are Rescource ID's ...
return this.applyLocale(`${model.tag} `);
}
//#endregion
//
//#region Handlers ...
/**
* Handle Errors ...
*
* @param error
*/
async handleCustomError(
error?: VQueryPresenterActionIdentifier
): Promise<void> {
//
await this.setLoadingState(false);
//
// TODO: Implement this ...
console.log('handle Custom Error: ', error);
}
/**
* Override this for adding additional futures ...
*
* @param tab
*/
async handleStateAfterTabOpen(tab: number) {
await super.handleStateAfterTabOpen(tab);
//
let selector: string = undefined;
let fabs: XSimpleFabButton[] = [];
let model: XTagDto = undefined;
//
let actions = await toSimpleFabItems(this.actions);
this.closeActions();
this.actions = undefined;
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
const isAgent = await this.getValueAsync(this.sharedService.isAgent$);
const isAdminAgent = isAdmin || isAgent;
//
switch (tab) {
//
case VQueryPresenterTabs.List:
//
if (this.showItemsAsList) {
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.listItemViewProperties },
},
});
} else {
//
// Prepare Non As List Items Properties ...
const properties: VDtoCommonProperties = {
...this.listItemViewProperties,
isInPage: true,
wrapWithCard: true,
cardShowActions: true, // Since Each Items has Separate Actions ...
};
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...properties },
},
});
}
//
break;
//
case VQueryPresenterTabs.Add:
//
this.addModel = {
...defaultTagDto,
};
//
sendAction({
provider: this.addItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.addModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Update:
//
model = { ...this.updateModel };
//
sendAction({
provider: this.updateItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.updateModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Details:
//
model = { ...this.detailsModel };
//
sendAction({
provider: this.detailsItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.detailsModelViewProperties },
},
});
//
// Adding Custom Actions ...
//
// Actions Preparing ...
actions = {
...(!isNullOrUndefined(actions) ? actions : defaultOptionsFabItem),
childs: [
...(!isNullOrUndefined(actions) && this.hasChild(actions.childs)
? actions.childs
: []),
...fabs,
],
};
//
// Issue Action if Waiting for Details ...
if (!isNullOrEmptyString(this.paramAction)) {
//
const action = this.paramAction;
this.paramAction = undefined;
//
await this.doItemActions({
id: action,
data: { ...this.detailsModel },
});
}
break;
}
//
// Here can Handle Actions Permissions and States ...
if (!isNullOrUndefined(actions)) {
//
// Only if model Exists ...
if (!isNullOrUndefined(model)) {
//
// Retrieve Requirements ...
//
const canEdit = isAdmin;
const canRemove = isAdmin;
//
// Edit ...
if (
actions.id === defaultEditFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultEditFabButton.id
)) >= 0
) {
//
if (actions.id === defaultEditFabButton.id) {
actions.disabled =
!canEdit || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultEditFabButton.id
? !canEdit ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
//
// Remove ...
if (
actions.id === defaultRemoveFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultRemoveFabButton.id
)) >= 0
) {
//
if (actions.id === defaultRemoveFabButton.id) {
actions.disabled =
!canRemove || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultRemoveFabButton.id
? !canRemove ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
}
//
// Update Actions ...
this.actions = {
...actions,
};
}
}
/**
* Override this for adding additional futures ...
*
* @param from
* @param to
*/
async handleStateBeforeTabOpen(from: number, to: number) {
await super.handleStateBeforeTabOpen(from, to);
//
this.closeActions();
//
switch (to) {
//
case VQueryPresenterTabs.List:
//
this.addModel = undefined;
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Add:
//
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Update:
//
this.addModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Details:
//
if (
isNullOrUndefined(this.detailsModel) &&
!isNullOrUndefined(this.updateModel) &&
from === VQueryPresenterTabs.Update
) {
//
this.detailsModel = {
...this.updateModel,
};
}
//
this.addModel = undefined;
this.updateModel = undefined;
break;
}
}
/**
* Here We Do All Items Based Actions ...
*
* @param info
* @returns
*/
async doItemActions(info: { id: string; data: XTagDto }): Promise<void> {
//
if (isNullOrUndefined(info) || isNullOrEmptyString(info.id)) {
return;
}
//
let isKnown = false;
//
// Details ...
isKnown = info.id === defaultDetailsSlideOption.id;
if (isKnown) {
//
this.detailsModel = {
...info.data,
};
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Details
);
}
//
// Edit ...
isKnown =
info.id === defaultEditSlideOption.id ||
info.id === defaultEditFabButton.id;
if (isKnown) {
//
if (!isNullOrUndefined(info.data)) {
//
this.updateModel = {
...info.data,
};
} else if (
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.updateModel = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(this.updateModel)) {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Update
);
}
}
//
// Remove ...
isKnown =
info.id === defaultRemoveSlideOption.id ||
info.id === defaultRemoveFabButton.id;
if (isKnown) {
//
if (
isNullOrUndefined(info.data) &&
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
info.data = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(info.data)) {
await this.handleRemoveModel(info.data);
}
}
}
/**
* Handle Specified Param ...
*
* @param identifier
* @param acttion
*/
async handleParamAction(identifier: string, action: string): Promise<void> {
await super.handleParamAction(identifier, action);
//
if (isNullOrEmptyString(identifier) || isNullOrEmptyString(action)) {
//
this.hasParams = false;
return;
}
//
let model = await this.handleGetSpecifiedModel(identifier, true);
if (!isNullOrUndefined(model)) {
//
this.paramAction = action;
this.detailsModel = { ...model };
this.activeTab = VQueryPresenterTabs.Details;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.activeTab
);
} else {
this.hasParams = false;
}
}
/**
* Handle Removing Model ...
*
* @param model
*/
async handleRemoveModel(model: XTagDto) {
//
await this.doConfirmedAction(async () => {
//
await this.setLoadingState(true);
//
try {
//
const connectionId =
this.sharedService.saherElmAPIService.tags.tagEntityPushService
.connectionId;
//
await this.getValueAsync(
this.sharedService.saherElmAPIService.tags.tagProviderService.remove(
model.id,
connectionId
)
);
} catch (err) {
//
await this.handleCustomError('RemoveModelError');
return;
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
model,
actions: [],
key: nameof<XTagDto>('id'),
});
//
this.showSuccess();
await this.setLoadingState(false);
//
// Handle Change Tabs if in Details ...
if (
this.activeTab === VQueryPresenterTabs.List ||
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.prevTab = undefined;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List
);
}
});
}
/**
* Handle Running toolbar Back Handler on Specified Case
* when User Pressed Escape Button ...
*
* @param event
* @returns
*/
async handleGlobalEscapePressed(event: any) {
//
if (
this.lock ||
!this.toolbarHasBack ||
!this.toolbarShowBack ||
!isFunction(this.toolbarBackHandler) ||
[VQueryPresenterTabs.Add, VQueryPresenterTabs.Update].includes(
this.activeTab
)
) {
return;
}
//
await this.toolbarBackHandler();
}
//#endregion
//
//#region Private ...
private provideItemOptions(userId: string, isAdmin: boolean, item: XTagDto) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
// Validate ...
if (!isNullOrUndefined(item) && !isNullOrEmptyString(userId)) {
//
const canEdit = isAdmin;
const canRemove = isAdmin;
//
// Details ...
result.push({
...defaultDetailsSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled,
} as XSimpleListItemSlideOption);
//
// Edit ...
result.push({
...defaultEditSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled || !canEdit,
} as XSimpleListItemSlideOption);
//
// Remove ...
result.push({
...defaultRemoveSlideOption,
slot: 'end',
disabled: this.loading || this.uiDisabled || !canRemove,
} as XSimpleListItemSlideOption);
}
//
return result;
}
isPushServiceConnected = false;
private async handleConnectPushService() {
//
// Preparing Connections ...
try {
//
// Check Connected Before ...
if (
this.sharedService.saherElmAPIService.tags.tagEntityPushService.isConnected()
) {
this.isPushServiceConnected = true;
} else {
//
this.isPushServiceConnected = true;
await this.sharedService.saherElmAPIService.tags.tagEntityPushService.connect();
}
} catch {
//
await this.handleCustomError('PushServiceConnection');
return;
}
//
// Check Connected ...
if (!this.isPushServiceConnected) {
return;
}
//
// On Added ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.tags.tagEntityPushService.onAdded$
).subscribe(async (event) => {
//
if (isNullOrUndefined(event)) {
return;
}
});
//
// On Removed ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.tags.tagEntityPushService.onDeleted$
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) && isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id
);
const modelP = { ...model };
if (!isNullOrUndefined(model) && !isNullOrUndefined(modelP)) {
//
const actions = await this.prepareListActions(modelP);
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
actions,
model: modelP,
key: nameof<XTagDto>('id'),
});
}
//
// Check Details or Edit Tab ...
if (
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.detailsModel) &&
this.detailsModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Details) ||
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.updateModel) &&
this.updateModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Update)
) {
//
// Force Unlock ...
this.lock = false;
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List
);
}
});
//
// On Updated ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.tags.tagEntityPushService.onUpdated$
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) || isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id
);
if (isNullOrUndefined(model)) {
return;
}
//
let modelP: XTagDto = undefined;
try {
//
modelP = await this.getValueAsync(
this.sharedService.saherElmAPIService.tags.tagProviderService.get(
event.model.id
)
);
} catch {
return;
}
//
if (isNullOrUndefined(modelP)) {
return;
}
//
// Preparing Model List Actions ...
const actions = await this.prepareListActions(modelP);
//
// Updating Model on Query Presenter ...
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
actions,
model: { ...modelP },
key: nameof<XTagDto>('id'),
});
//
// Update Tab ...
if (
!isNullOrUndefined(this.updateModel) &&
modelP.id === this.updateModel.id &&
this.activeTab === VQueryPresenterTabs.Update
) {
//
this.updateModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VTagComponent,
this.ModelFieldNames.id,
this.updateModel.id.toString()
);
await this.waitForElement(selector).then(async (el) => {
//
this.updateItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.updateModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
//
// Details Tab ...
if (
!isNullOrUndefined(this.detailsModel) &&
modelP.id === this.detailsModel.id &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.detailsModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VTagComponent,
this.ModelFieldNames.id,
this.detailsModel.id.toString()
);
await this.waitForElement(selector).then(async (el) => {
//
this.detailsItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.detailsModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-explorer\tags-explorer.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-explorer\tags-explorer.page.scss
@import '../tags-base/tags-base.provider.page.scss';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-explorer\tags-explorer.page.ts
import { Observable } from 'rxjs';
import { toNumber } from 'x-framework-core';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { TagsBaseProviderPage } from '../tags-base/tags-base.provider.page';
@Component({
selector: 'tags-explorer',
styleUrls: ['./tags-explorer.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../tags-base/tags-base.provider.page.html',
})
export class TagsExplorerPage extends TagsBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsExplorerPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.tags.tagProviderService.get(
toNumber(identifier),
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.tags.tagProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-management\tags-management.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-management\tags-management.page.scss
@import '../tags-base/tags-base.provider.page.scss';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tags\tags-management\tags-management.page.ts
import { Observable } from 'rxjs';
import { toNumber } from 'x-framework-core';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { TagsBaseProviderPage } from '../tags-base/tags-base.provider.page';
@Component({
selector: 'tags-management',
styleUrls: ['./tags-management.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../tags-base/tags-base.provider.page.html',
})
export class TagsManagementPage extends TagsBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsManagementPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.tags.tagProviderService.get(
toNumber(identifier),
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.tags.tagProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\tools\common.tools.ts
import { XLocale } from 'x-framework-core';
import { AppResourceIDs } from 'src/app/config/localization.config';
export function getLocaleResourceID(locale: XLocale) {
//
if (locale === 'en-US') {
return AppResourceIDs.english;
} else if (locale === 'fa-IR') {
return AppResourceIDs.persian;
}
//
return locale;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-page.module.ts
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AuthGuard } from 'x-framework-identity-sdk';
import { XCanDeactivateGuard } from 'x-framework-core';
import { BaseRoutes } from 'src/app/config/page.config';
import { ViewsModule } from 'src/app/views/views.module';
import { WebinarRoutes } from 'src/app/views/webinar/v-webinar.configs';
import { WebinarsExplorerPage } from './webinars-explorer/webinars-explorer.page';
import { WebinarsManagementPage } from './webinars-management/webinars-management.page';
@NgModule({
declarations: [WebinarsExplorerPage, WebinarsManagementPage],
imports: [
ViewsModule,
RouterModule.forChild([
{
pathMatch: 'full',
path: BaseRoutes.Default,
redirectTo: WebinarRoutes.WebinarsExplorer,
},
//
// Webinars Explorer ...
{
canActivate: [AuthGuard],
component: WebinarsExplorerPage,
canDeactivate: [XCanDeactivateGuard],
path: WebinarRoutes.WebinarsExplorer,
},
//
// Webinars Management ...
{
canActivate: [AuthGuard],
component: WebinarsManagementPage,
canDeactivate: [XCanDeactivateGuard],
path: WebinarRoutes.WebinarsManagement,
},
{
pathMatch: 'full',
path: BaseRoutes.Unknown,
redirectTo: WebinarRoutes.Default,
},
]),
],
exports: [
ViewsModule,
RouterModule,
WebinarsExplorerPage,
WebinarsManagementPage,
],
})
export class WebinarsPageModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-base\webinars-base.provider.page.html
<!-- -->
<!-- Page Presenter -->
<!-- -->
<v-page-presenter
#pagePresenter
[loading]="loading"
[containerBase]="this"
[uiDisabled]="uiDisabled"
[contentTemplate]="contentTemplate"
(xOnEscapeGlobal)="handleGlobalEscapePressed($event)"
></v-page-presenter>
<!-- -->
<!-- Page Content -->
<!-- -->
<ng-template #contentTemplate>
<!-- Item List -->
<v-query-presenter
[loading]="loading"
[tabsLock]="tabsLock"
[tabsColor]="tabsColor"
[showEmpty]="showEmpty"
[emptyColor]="emptyColor"
[addTabTitle]="addTabTitle"
[itemTemplate]="itemTemplate"
[emptyMessage]="emptyMessage"
[tabsCssClass]="tabsCssClass"
[tabsLoopTabs]="tabsLoopTabs"
[tabsSwipable]="tabsSwipable"
[listTabTitle]="listTabTitle"
[emptyCssClass]="emptyCssClass"
[showPaginator]="showPaginator"
[tabsAlignTabs]="tabsAlignTabs"
[showSearchBar]="showSearchBar"
[searchBarType]="searchBarType"
[searchBarValue]="searchBarValue"
[searchBarTitle]="searchBarTitle"
[searchBarColor]="searchBarColor"
[updateTabTitle]="updateTabTitle"
[paginatorColor]="paginatorColor"
[tabsShowHeader]="tabsShowHeader"
[addItemTemplate]="addItemTemplate"
[emptyHasRefresh]="emptyHasRefresh"
[detailsTabTitle]="detailsTabTitle"
[tabsHeaderColor]="tabsHeaderColor"
[showItemsAsList]="showItemsAsList"
[modelIdentifier]="modelIdentifier"
[uiDisabled]="loading || uiDisabled"
[listItemTemplate]="listItemTemplate"
[listTabCardTitle]="listTabCardTitle"
[listTabCardColor]="listTabCardColor"
[emptyRefreshIcon]="emptyRefreshIcon"
[emptyMessageColor]="emptyMessageColor"
[paginatorPosition]="paginatorPosition"
[tabsSelectedIndex]="tabsSelectedIndex"
[tabsDynamicHeight]="tabsDynamicHeight"
[tabsDisableRipple]="tabsDisableRipple"
[searchBarDebounce]="searchBarDebounce"
[searchBarCssClass]="searchBarCssClass"
[updateItemTemplate]="updateItemTemplate"
[tabsHeaderPosition]="tabsHeaderPosition"
[tabsActiveTabColor]="tabsActiveTabColor"
[searchBarSpellcheck]="searchBarSpellcheck"
[detailsItemTemplate]="detailsItemTemplate"
[wrapListTabWithCard]="wrapListTabWithCard"
[listTabCardIsInPage]="listTabCardIsInPage"
[listTabCardSubTitle]="listTabCardSubTitle"
[emptyForegroundColor]="emptyForegroundColor"
[actionProvider]="queryPresenterActionProvider"
[tabsDisablePagination]="tabsDisablePagination"
[tabsAnimationDuration]="tabsAnimationDuration"
[tabsTabIndicatorColor]="tabsTabIndicatorColor"
[tabsTabPaginatorColor]="tabsTabPaginatorColor"
[searchBarKeyboardType]="searchBarKeyboardType"
[searchBarEnterKeyHint]="searchBarEnterKeyHint"
[emptyRefreshButtonColor]="emptyRefreshButtonColor"
[searchBarForegroundColor]="searchBarForegroundColor"
[searchBarShowClearButton]="searchBarShowClearButton"
[searchBarClearButtonIcon]="searchBarClearButtonIcon"
[searchBarCancelButtonIcon]="searchBarCancelButtonIcon"
[searchBarSearchButtonIcon]="searchBarSearchButtonIcon"
[searchBarSearchInputColor]="searchBarSearchInputColor"
[searchBarShowCancelButton]="searchBarShowCancelButton"
[searchBarCancelButtonTitle]="searchBarCancelButtonTitle"
[emptyForegroundMessageColor]="emptyForegroundMessageColor"
[searchBarClearButtonIconColor]="searchBarClearButtonIconColor"
[searchBarCancelButtonIconColor]="searchBarCancelButtonIconColor"
[searchBarSearchButtonIconColor]="searchBarSearchButtonIconColor"
[emptyForegroundRefreshButtonColor]="emptyForegroundRefreshButtonColor"
[searchBarForegroundSearchInputColor]="searchBarForegroundSearchInputColor"
>
</v-query-presenter>
</ng-template>
<!-- -->
<!-- Item Template -->
<!-- -->
<ng-template #itemTemplate let-item="item">
<v-webinar-view
[model]="item"
[showTags]="false"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[cardTitle]="getModelCardTitle(item)"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
>
<div actions *ngIf="(notNullOrUndefinedValue(item) | async)">
<ng-container *ngIf="itemsActions.has(item.id)">
<!-- Slotting Actions -->
<x-slotter [hasCenterSlot]="false" [layout]="SlotLayouts.HORIZONTAL">
<!-- START -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.START, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
<!-- END -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
<ng-container
*ngFor="let action of getSlottedItemActions(SlotNames.END, item)"
>
<!-- Present Action -->
<ng-container
*ngTemplateOutlet="itemActionTemplate; context: {action: action, data: item}"
></ng-container>
</ng-container>
</x-slot>
</x-slotter>
</ng-container>
</div>
</v-webinar-view>
</ng-template>
<!-- Item Action Presenter -->
<ng-template #itemActionTemplate let-action="action" let-data="data">
<!-- Validate -->
<ng-container
*ngIf="(notNullOrUndefinedValue(data) | async) && (notNullOrUndefinedValue(action) | async)"
>
<!-- Present Action -->
<x-button
[loading]="loading"
[icon]="action.icon"
[title]="action.title"
[color]="action.color"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[iconColor]="ColorNames.Transparent"
(clicked)="handleItemAction(action.id, data)"
[uiDisabled]="loading || uiDisabled || action.disabled"
>
</x-button>
</ng-container>
</ng-template>
<!-- -->
<!-- List Item Template -->
<!-- -->
<ng-template #listItemTemplate let-item="item">
<v-webinar-view
[showTags]="false"
[model]="item.data"
[loading]="loading"
[uiDisabled]="loading || uiDisabled"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="listItemActionProvider"
*ngIf="notNullOrUndefinedValue(item) | async"
[providedFieldDescriptors]="listItemViewFieldDescriptors"
></v-webinar-view>
</ng-template>
<!-- Webinar Owner Name in Lists Template -->
<ng-template
#listOwnerRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
{{ getOwnerFullName(value) }}
</ng-template>
<!-- -->
<!-- Add Item Template -->
<!-- -->
<ng-template #addItemTemplate>
<v-webinar-view
[showTags]="true"
[showFiles]="true"
[model]="addModel"
[loading]="loading"
[showTagsLabel]="true"
[showFilesLabel]="true"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.add"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsForm"
[actionProvider]="addItemActionProvider"
*ngIf="notNullOrUndefinedValue(addModel) | async"
(lockNotifier)="handleAddViewLockNotifier($event)"
(tagsStateChanged)="handleTagsStateChanged($event)"
(filesStateChanged)="handleFilesStateChanged($event)"
(dtoModelActionFired)="handleAddViewActionFired($event)"
[providedFieldDescriptors]="addModelViewFieldDescriptors"
>
</v-webinar-view>
</ng-template>
<!-- -->
<!-- Update Item Template -->
<!-- -->
<ng-template #updateItemTemplate>
<v-webinar-view
[showTags]="true"
[showFiles]="true"
[loading]="loading"
[model]="updateModel"
[showTagsLabel]="true"
[showFilesLabel]="true"
[showValidationErrors]="true"
[cardTitle]="ResourceIDs.update"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsForm"
[actionProvider]="updateItemActionProvider"
(tagsStateChanged)="handleTagsStateChanged($event)"
(filesStateChanged)="handleFilesStateChanged($event)"
*ngIf="notNullOrUndefinedValue(updateModel) | async"
(lockNotifier)="handleUpdateViewLockNotifier($event)"
(dtoModelActionFired)="handleUpdateViewActionFired($event)"
[providedFieldDescriptors]="updateModelViewFieldDescriptors"
>
</v-webinar-view>
</ng-template>
<!-- -->
<!-- Details Item Template -->
<!-- -->
<ng-template #detailsItemTemplate>
<v-webinar-view
[showTags]="true"
[showFiles]="true"
[loading]="loading"
[showTagsLabel]="true"
[showFilesLabel]="true"
[model]="detailsModel"
[showValidationErrors]="false"
[uiDisabled]="uiDisabled || loading"
[presentType]="BasePresentTypes.AsView"
[actionProvider]="detailsItemActionProvider"
[cardTitle]="getModelCardTitle(detailsModel)"
[tagActionsProvider]="detailsTagsActionsProvider"
[fileActionsProvider]="detailsFilesActionsProvider"
*ngIf="notNullOrUndefinedValue(detailsModel) | async"
[tagsPresentType]="TagsPresenterPresertTypes.AsChips"
[filesPresentType]="FilesPresenterPresertTypes.AsThumbnails"
[providedFieldDescriptors]="detailsModelViewFieldDescriptors"
>
</v-webinar-view>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-base\webinars-base.provider.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-base\webinars-base.provider.page.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XRef,
nameof,
isFunction,
getFieldNames,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
getComponentSelector,
} from 'x-framework-core';
import {
XFileDto,
XPersonDto,
XWebinarDto,
XWebinarType,
} from 'x-saherelm-api-sdk';
import {
XListItem,
XSlotName,
XButtonType,
XFabButtonSide,
XListItemOptionSlot,
} from 'x-framework-components';
import { Observable, Subject } from 'rxjs';
import {
VBaseDtoAction,
VModelFieldTypes,
VBaseDtoActionModel,
VBaseDtoModelAction,
VBaseDtoPresentType,
VModelFieldDescriptor,
VBaseDtoModelFiredActionModel,
} from 'src/app/views/v-dto/v-dto.typings';
import { Validators } from '@angular/forms';
import {
defaultEditFabButton,
defaultCloseFabButton,
defaultOptionsFabItem,
defaultRemoveFabButton,
defaultConnectFabButton,
} from 'src/app/views/constants/fab.defaults';
import {
ToWebinarDto,
ToWebinarViewDto,
getWebinarResourceID,
getWebinarTypeValues,
} from 'src/app/views/webinar/v-webinar.tools';
import {
VWebinarViewDto,
defaultVWebinarViewDto,
} from 'src/app/views/webinar/v-webinar.typings';
import {
toSimpleFabItems,
XSimpleFabButton,
} from 'src/app/views/typings/simple.fab.typings';
import {
XSimpleFormSelectControlConfig,
XSimpleFormSelectControlOption,
} from 'src/app/views/typings/simple.form.typings';
import {
defaultEditSlideOption,
defaultCloseSlideOption,
defaultRemoveSlideOption,
defaultConnectSlideOption,
defaultDetailsSlideOption,
} from 'src/app/views/constants/slide.options.defaults';
import {
processFilledItems,
VQueryPresenterTabs,
VQueryPresenterAction,
VQueryPresenterActionModel,
VQueryPresenterActionIdentifier,
VQueryFilledItemsProcessingResult,
} from 'src/app/views/v-query-presenter/v-query-presenter.typings';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import {
VTagsPresenterModelState,
VTagsPresenterPresentType,
} from 'src/app/views/tags/v-tags-presenter/v-tags-presenter.typings';
import { sendAction } from 'src/app/views/tools/action-provider.tools';
import {
VFilesPresenterModelState,
VFilesPresenterPresentType,
} from 'src/app/views/files/v-files-presenter/v-files-presenter.typings';
import { getPersonFullName } from 'src/app/views/v-person/v-person.tools';
import { PersonPresentType } from 'src/app/views/v-person/v-person.typings';
import { VDtoCommonProperties } from 'src/app/views/typings/dto.common.typings';
import { VPagePresenter } from 'src/app/views/v-page-presenter/v-page-presenter.component';
import { getQuerySelectorOfSpecifiedAttribute } from 'src/app/views/typings/common.typings';
import { XSimpleListItemSlideOption } from 'src/app/views/typings/simple.list-item.typings';
import { VQueryPresenterBasePage } from 'src/app/views/v-query-presenter/v-query-presenter.base.page';
import { VWebinarViewComponent } from 'src/app/views/webinar/v-webinar-view/v-webinar-view.component';
import { VWebinarModalContext } from 'src/app/views/webinar/v-webinar-connect/v-webinar-connect.typings';
import { VWebinarConnectComponent } from 'src/app/views/webinar/v-webinar-connect/v-webinar-connect.component';
@Component({
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class WebinarsBaseProviderPage extends VQueryPresenterBasePage {
//
//#region Props ...
toolbarShowTitle = true;
toolbarShowSubTitle = true;
titleRes = this.ResourceIDs.app_name;
toolbarSubTitle = this.resourceProvider(
this.AppResourceIDs.webinars_management_page_title,
);
toolbarTitle = this.resourceProvider(this.titleRes);
//
@ViewChild('pagePresenter')
pagePresenter!: VPagePresenter;
//
readonly TagsPresenterPresertTypes = Object.assign(
VTagsPresenterPresentType,
{},
);
readonly FilesPresenterPresertTypes = Object.assign(
{},
VFilesPresenterPresentType,
);
readonly ButtonTypes = Object.assign(XButtonType, {});
readonly ModelFieldTypes = Object.assign(VModelFieldTypes);
readonly ModelFieldNames = getFieldNames(defaultVWebinarViewDto);
readonly PersonPresentTypes = Object.assign(PersonPresentType, {});
readonly BasePresentTypes = Object.assign(VBaseDtoPresentType, {});
//
// Prepare Commonly Used Properties for Dto Actions ...
// we can Override this for other Purposes ...
commonViewsProperties: VDtoCommonProperties = {
isInPage: false,
wrapWithCard: false,
labelColor: undefined,
valueColor: undefined,
fieldFormButtonPosition: undefined,
fieldFormIcon: undefined,
fieldFormIconColor: undefined,
fieldFormApplyIcon: undefined,
fieldFormApplyIconColor: undefined,
fieldFormCancelIcon: undefined,
fieldFormCancelIconColor: undefined,
removeCardMargin: undefined,
cardForegroundColor: undefined,
cardCssClass: undefined,
cardColor: undefined,
cardActionProvider: undefined,
formAppearance: undefined,
formActionProvider: undefined,
formRegularColor: undefined,
formAccentColor: undefined,
formHintColor: undefined,
formActiveColor: undefined,
formValidColor: undefined,
formInvalidColor: undefined,
formPendingColor: undefined,
formFillColor: undefined,
formDisabledColor: undefined,
formDisabledFillColor: undefined,
showFormCancelButton: undefined,
formCancelButtonResource: undefined,
formCancelButtonColor: undefined,
formResetButtonResource: undefined,
formResetButtonColor: undefined,
formActionButtonAddResource: undefined,
formActionButtonUpdateResource: undefined,
formActionButtonColor: undefined,
};
//
//#region Query Presenter Props ...
modelIdentifier: string = nameof<VWebinarViewDto>('id');
listTabCardTitle: XResourceIdentifier = this.AppResourceIDs.webinars;
/**
* Presenter Component Action Provider ...
*/
queryPresenterActionProvider = new Subject<VQueryPresenterActionModel>();
//#endregion
//#endregion
/**
* Specified How to get Specific Item using Model Identifier ...
*/
abstract getModelTask(identifier: string): Observable<any>;
/**
* Specified How to Query Items ...
*/
abstract queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>>;
/**
* Param Recieved Action ...
*/
paramAction: string = undefined;
//
tagsState: VTagsPresenterModelState;
filesState: VFilesPresenterModelState;
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
await super.afterViewInit();
//
this.applyTakeUntilWrapper(
this.managerService.settingsService.isMobile$,
).subscribe((res) => {
//
if (!isNullOrUndefined(this.activeTab)) {
//
switch (this.activeTab) {
//
case VQueryPresenterTabs.List:
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.Refresh,
});
break;
}
}
});
//
await this.handleConnectPushService();
}
//#endregion
//
//#region Providers ...
//#endregion
//
//#region List ...
/**
* Provides List Item Template ...
*/
@ViewChild('itemTemplate')
itemTemplate!: TemplateRef<any>;
/**
* Provides List Item Template ...
*/
@ViewChild('listItemTemplate')
listItemTemplate!: TemplateRef<any>;
//
//#region Owner Custom Template for List Item View ...
private LIST_OWNER_REF: TemplateRef<any>;
@ViewChild('listOwnerRef')
set listOwnerRef(v: TemplateRef<any>) {
//
this.LIST_OWNER_REF = v;
this.applyListOwnerRef();
}
get listOwnerRef() {
return this.LIST_OWNER_REF;
}
//#endregion
/**
* Query Object for Fetching Data ...
*/
query: XQueryDto = {
page: 1,
filter: '',
pageSize: this.config.defaultPageSize,
sortBy: nameof<XWebinarDto>('createdOn'),
};
/**
* a Flag for Detect Has Query result or not ...
*/
hasQueryResultError: boolean = false;
/**
* Fetching Data Result Model ...
*/
dtoQueryResult: XQueryResultDto<XWebinarDto> = undefined;
/**
* Specified Action Provider ...
*/
listItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specified View Properties ...
*/
listItemViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
};
/**
* Specified Field Descriptors for List Items ...
*/
listItemViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.ModelFieldNames.type,
value: defaultVWebinarViewDto.type,
label: this.AppResourceIDs.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.resourceProvider(getWebinarResourceID(value));
},
} as VModelFieldDescriptor<XWebinarType>,
//
// Title ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.ModelFieldNames.title,
value: defaultVWebinarViewDto.title,
label: this.AppResourceIDs.title,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Description ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.ModelFieldNames.description,
value: defaultVWebinarViewDto.description,
label: this.AppResourceIDs.description,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
showContentBelowLabel: false,
key: this.ModelFieldNames.owner,
value: defaultVWebinarViewDto.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
template: this.listOwnerRef,
hasCustomTemplate: true,
} as VModelFieldDescriptor<XPersonDto>,
];
/**
* Get Webinar Owner Full Name ...
*
* @param model
* @returns
*/
getOwnerFullName(model: XPersonDto) {
return getPersonFullName(model);
}
/**
* Hold each Items Provided Actions based on Identifier ...
*/
itemsActions = new Map<string, XRef<Array<XSimpleListItemSlideOption>>>();
/**
* Prepare Item Actions based on Slot ...
*/
getSlottedItemActions(slot: XSlotName, model: VWebinarViewDto) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
if (
isNullOrUndefined(model) ||
isNullOrEmptyString(slot) ||
isNullOrEmptyString(model.id) ||
isNullOrUndefined(this.itemsActions) ||
!this.itemsActions.has(model.id)
) {
return result;
}
//
let _slot: XListItemOptionSlot =
slot === XSlotName.START || slot === XSlotName.TOP ? 'start' : 'end';
//
result = this.itemsActions
.get(model.id)
.value.filter((a) => a.slot === _slot);
//
return result;
}
/**
* Handle Non As List Item's Action ...
*
* @param id
* @param data
*/
async handleItemAction(id: string, data: VWebinarViewDto) {
//
// Validate ...
if (
this.loading ||
this.uiDisabled ||
isNullOrEmptyString(id) ||
isNullOrUndefined(data)
) {
return;
}
//
await this.doItemActions({ id, data });
}
/**
* Apply List Owner Template to Descriptors ...
*
* @returns
*/
applyListOwnerRef() {
//
if (isNullOrUndefined(this.LIST_OWNER_REF)) {
return;
}
//
this.listItemViewFieldDescriptors = this.listItemViewFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.ModelFieldNames.owner
? this.LIST_OWNER_REF
: fd.template,
};
},
);
}
/**
* Handle Retrieve Data based on XQuery ...
*
* @param query
* @returns
*/
async handleGetQueryResult(
query: XQueryDto,
): Promise<XQueryResultDto<VWebinarViewDto>> {
//
let result: XQueryResultDto<VWebinarViewDto> = undefined;
//
// Start Loading ...
this.hasQueryResultError = false;
await this.setLoadingState(true);
//
// Try to Prepare Result ...
try {
//
// Retrieve Webinar Dto Result ...
this.dtoQueryResult = await this.getValueAsync(
this.queryModelTask(query),
);
//
// Prepare Result ...
result = {
...result,
page: this.dtoQueryResult.page,
pageSize: this.dtoQueryResult.pageSize,
totalItems: this.dtoQueryResult.totalItems,
totalPages: this.dtoQueryResult.totalPages,
totalFilteredItems: this.dtoQueryResult.totalFilteredItems,
totalFilteredPages: this.dtoQueryResult.totalFilteredPages,
};
//
// Converts XWebinarDto to VWebinarViewDto ...
result.items = this.dtoQueryResult.items.map((i) =>
ToWebinarViewDto(i, this.managerService.currentLocale),
);
//
// Remove Scrolling Info ...
if (
!isNullOrUndefined(this.query) &&
this.query.filter !== query.filter
) {
//
this.removeScrollingInfo();
this.infinityScrollDisabled = true;
}
//
this.query = query;
} catch {
//
this.hasQueryResultError = true;
await this.handleCustomError('QueryWebinarsError');
return result;
}
//
// Stop Loading ...
await this.setLoadingState(false);
//
return result;
}
/**
* Get Specified Model Based on Identifier ...
*
* @param identifier
* @param applyState
* @returns
*/
async handleGetSpecifiedModel(identifier: string, applyState = false) {
//
if (isNullOrEmptyString(identifier)) {
return;
}
//
let response: any = undefined;
try {
//
if (applyState) {
await this.setLoadingState(true);
}
//
response = await this.getValueAsync(this.getModelTask(identifier));
//
} catch {
//
await this.handleCustomError('GetWebinarError');
return;
}
//
if (applyState) {
await this.setLoadingState(false);
}
//
return response;
}
/**
* Provide List Actions for Specific Model ...
*
* @param model
* @returns
*/
async prepareListActions(model: VWebinarViewDto) {
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
//
return this.provideItemOptions(userId, isAdmin, model);
}
/**
* Validate List Items ...
*
* @param items
* @returns
*/
async validateAndPrepareListItems(
items: Array<XListItem<VWebinarViewDto>>,
): Promise<Array<XListItem<VWebinarViewDto>>> {
//
let result: Array<XListItem<VWebinarViewDto>> = [];
//
// Preparing Slide Options ...
result = await Promise.all([
...items.map(async (i) => {
return {
...i,
slideOptions: [...(await this.prepareListActions(i.data))],
};
}),
]);
//
return result;
}
/**
* Prepare Result for Non As List Presenting Items ...
*
* @param current
* @param recieved
*/
async requestFillingItems(
exists: Array<VWebinarViewDto>,
recieved: Array<VWebinarViewDto>,
): Promise<VQueryFilledItemsProcessingResult<VWebinarViewDto>> {
//
let result: VQueryFilledItemsProcessingResult<VWebinarViewDto> =
processFilledItems(exists, recieved, 'id');
//
// Filling Actions ...
if (!isNullOrUndefined(result)) {
//
// News Items Actions ...
if (!isNullOrUndefined(result.news) && this.hasChild(result.news)) {
//
result.news.forEach(async (i) => {
//
const actions = await this.prepareListActions(i);
this.itemsActions.set(i.id, { value: actions });
});
}
//
// Changed Items Actions ...
if (!isNullOrUndefined(result.changed) && this.hasChild(result.changed)) {
//
result.changed.forEach(async (i) => {
//
if (this.itemsActions.has(i.identifier)) {
//
const actions = await this.prepareListActions(i.data);
this.itemsActions.set(i.identifier, { value: actions });
}
});
}
}
//
return result;
}
/**
* When Non as List Items Filled, this method call for Change Detection ...
*
* @param ids
*/
async handleRefreshViews(ids: Array<string>): Promise<void> {
await super.handleRefreshViews(ids);
//
if (isNullOrUndefined(ids) || !this.hasChild(ids)) {
return;
}
//
const selector = getComponentSelector(VWebinarViewComponent);
ids.forEach((id) => {
//
const iSelector = `${selector}[${nameof<VWebinarViewDto>(
'id',
)}="${id}"][${nameof<VWebinarViewComponent>('presentType')}="${
VBaseDtoPresentType.AsView
}"]`;
this.waitForElement(iSelector).then((el) => {
this.listItemActionProvider.next({ action: VBaseDtoAction.Refresh });
});
});
}
//#endregion
//
//#region Add ...
/**
* Provides Template for Adding Items ...
*/
@ViewChild('addItemTemplate')
addItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Add ...
*/
addModel: VWebinarViewDto = {
...defaultVWebinarViewDto,
language: this.managerService.currentLocale,
};
/**
* Specified Action Provider ...
*/
addItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Add ...
*/
addModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Add Model Fields ...
*/
addModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.ModelFieldNames.id,
label: this.ResourceIDs.id,
value: defaultVWebinarViewDto.id,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Language ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.ModelFieldNames.language,
label: this.ResourceIDs.language,
value: defaultVWebinarViewDto.language,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.ModelFieldNames.ownerId,
label: this.AppResourceIDs.author_id,
value: defaultVWebinarViewDto.ownerId,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.type,
value: defaultVWebinarViewDto.type,
label: this.AppResourceIDs.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.resourceProvider(getWebinarResourceID(value));
},
formControlConfig: {
index: 0,
propName: this.ModelFieldNames.type,
type: {
type: this.FormControlTypes.Select,
config: {
multiple: false,
options: getWebinarTypeValues().map((k) => {
return {
value: k,
viewValue: this.resourceProvider(getWebinarResourceID(k)),
} as XSimpleFormSelectControlOption<XWebinarType>;
}),
} as XSimpleFormSelectControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
label: this.AppResourceIDs.type,
placeholder: this.AppResourceIDs.type,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<XWebinarType>,
//
// Title ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.title,
value: defaultVWebinarViewDto.title,
label: this.AppResourceIDs.title,
type: this.ModelFieldTypes.String,
formControlConfig: {
index: 1,
propName: this.ModelFieldNames.title,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
focusHandler: new Subject(),
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.title,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Description ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.description,
value: defaultVWebinarViewDto.description,
label: this.AppResourceIDs.description,
type: this.ModelFieldTypes.String,
formControlConfig: {
index: 2,
propName: this.ModelFieldNames.description,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.description,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleAddViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Handle Cancel Add Form View ...
*
* @param event
*/
async handleAddViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List,
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<VWebinarViewDto> = {
...event.payload.model,
} as Required<VWebinarViewDto>;
await this.handleAddModel(pModel);
}
}
/**
* Do Add Model ...
*
* @param model
* @returns
*/
async handleAddModel(
model: VWebinarViewDto,
tags?: Array<string>,
files?: Array<File>,
) {
//
if (isNullOrUndefined(model)) {
return;
}
//
await this.setLoadingState(true);
//
let response: XWebinarDto = undefined;
try {
//
// Prepare Dto Model to Add ...
const dtoModel = ToWebinarDto(model);
//
// Remove ID ...
delete dtoModel.id;
//
// Preparing Tags ...
let pTags: Array<string> = [];
if (!isNullOrUndefined(tags) && this.hasChild(tags)) {
pTags = [...tags];
}
//
// Preparing Files ...
let pFiles: Array<File> = [];
if (!isNullOrUndefined(files) && this.hasChild(files)) {
pFiles = [...files];
}
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService
.connectionId;
//
// Filling Owner ID ...
dtoModel.ownerId = await this.getValueAsync(this.sharedService.userId$);
//
// Issue Task ...
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.create(
dtoModel, // Model ...
pTags, // Tags ...
pFiles, // Files ...
connectionId, // Connection Id ...
),
);
} catch (ex) {
//
await this.handleCustomError('AddModelError');
return;
}
//
this.lock = false;
this.addModel = undefined;
this.showSuccess();
await this.setLoadingState(false);
this.sendQueryPresenterAction(VQueryPresenterAction.ResetQuery, true);
}
//#endregion
//
//#region Update ...
/**
* Provides Template for Update Items ...
*/
@ViewChild('updateItemTemplate')
updateItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Update ...
*/
updateModel: VWebinarViewDto = undefined;
/**
* Specified Action Provider ...
*/
updateItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Update ...
*/
updateModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Update Model Fields ...
*/
updateModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
label: this.ResourceIDs.id,
key: this.ModelFieldNames.id,
value: defaultVWebinarViewDto.id,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Language ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
label: this.ResourceIDs.language,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.language,
value: defaultVWebinarViewDto.language,
} as VModelFieldDescriptor<string>,
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.type,
label: this.AppResourceIDs.type,
type: this.ModelFieldTypes.Number,
value: defaultVWebinarViewDto.type,
valueProvider: (value) => {
return this.resourceProvider(getWebinarResourceID(value));
},
formControlConfig: {
index: 0,
propName: this.ModelFieldNames.type,
type: {
type: this.FormControlTypes.Select,
config: {
multiple: false,
options: getWebinarTypeValues().map((k) => {
return {
value: k,
viewValue: this.resourceProvider(getWebinarResourceID(k)),
} as XSimpleFormSelectControlOption<XWebinarType>;
}),
} as XSimpleFormSelectControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
label: this.AppResourceIDs.type,
placeholder: this.AppResourceIDs.type,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<XWebinarType>,
//
// Title ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.ModelFieldNames.title,
label: this.AppResourceIDs.title,
type: this.ModelFieldTypes.String,
value: defaultVWebinarViewDto.title,
formControlConfig: {
index: 1,
propName: this.ModelFieldNames.title,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
focusHandler: new Subject(),
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.title,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Description ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
type: this.ModelFieldTypes.String,
key: this.ModelFieldNames.description,
label: this.AppResourceIDs.description,
value: defaultVWebinarViewDto.description,
formControlConfig: {
index: 2,
propName: this.ModelFieldNames.description,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.description,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
/**
* Handle Locking Page when a Form Lock Content ...
*
* @param event
*/
handleUpdateViewLockNotifier(event: { id: string; state: boolean }) {
this.lock = event.state;
}
/**
* Tags Actions Provider for Details Section ...
*
* @param t
* @returns
*/
detailsTagsActionsProvider: (t: string) => Array<XSimpleListItemSlideOption> =
(t) => {
return [];
};
/**
* Files Actions Provider for Details Section ...
*
* @param f
* @returns
*/
detailsFilesActionsProvider: (
f: XFileDto | File,
) => Array<XSimpleListItemSlideOption> = (f) => {
return [];
};
/**
* Handle Cancel Update Form View ...
*
* @param event
*/
async handleUpdateViewActionFired(event: VBaseDtoModelFiredActionModel) {
//
// Cancel Form ...
if (event.action === VBaseDtoModelAction.Cancel) {
//
// Since Cancel only Fired when Lock Breaked, here we do not need to handle it ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List,
);
}
//
// Reset Form ...
else if (event.action === VBaseDtoModelAction.Reset) {
}
//
// Add Or Update Form ...
else if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
const pModel: Required<VWebinarViewDto> = {
...event.payload.model,
} as Required<VWebinarViewDto>;
await this.handleUpdateModel(pModel);
}
}
/**
* Do Update Model ...
*
* @param model
* @returns
*/
async handleUpdateModel(model: VWebinarViewDto) {
//
const prevTab = this.prevTab;
//
if (isNullOrUndefined(model)) {
return;
}
//
await this.setLoadingState(true);
//
let response: XWebinarDto = undefined;
try {
//
// Prepare Dto Model to Add ...
const dto =
!isNullOrUndefined(this.dtoQueryResult) &&
!isNullOrUndefined(this.dtoQueryResult.items) &&
this.hasChild(this.dtoQueryResult.items)
? this.dtoQueryResult.items.find((i) => i.id === model.id)
: undefined;
if (isNullOrUndefined(dto)) {
throw 'UpdateModelError';
}
//
// Preparing Update Model ...
const dtoModel = ToWebinarDto(model, dto);
//
// Retrieve Connection ID ...
const connectionId =
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService
.connectionId;
//
// Filling Owner ID ...
dtoModel.ownerId = await this.getValueAsync(this.sharedService.userId$);
//
// Issue Task ...
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.update(
dtoModel,
connectionId,
),
);
} catch (ex) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
if (isNullOrUndefined(response)) {
//
await this.handleCustomError('UpdateModelError');
return;
}
//
this.lock = false;
this.updateModel = undefined;
await this.setLoadingState(false);
this.showSuccess();
//
this.prevTab = prevTab;
model = ToWebinarViewDto(response, this.managerService.currentLocale);
const actions = await this.prepareListActions(model);
//
if (this.prevTab === VQueryPresenterTabs.Details) {
//
this.detailsModel = {
...model,
};
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
model,
actions,
key: nameof<XWebinarDto>('id'),
});
//
// Check Handle Back ...
if (
this.toolbarHasBack &&
this.toolbarShowBack &&
isFunction(this.toolbarBackHandler)
) {
await this.toolbarBackHandler();
}
}
//#endregion
//
//#region Details ...
/**
* Provides Template for Details Items ...
*/
@ViewChild('detailsItemTemplate')
detailsItemTemplate!: TemplateRef<any>;
/**
* a Model for Providing to Details ...
*/
detailsModel: VWebinarViewDto = undefined;
/**
* Specified Action Provider ...
*/
detailsItemActionProvider = new Subject<VBaseDtoActionModel>();
/**
* Specify View Properties for Details ...
*/
detailsModelViewProperties: VDtoCommonProperties = {
...this.commonViewsProperties,
isInPage: true,
wrapWithCard: true,
};
/**
* Describe Details Model Fields ...
*/
detailsModelViewFieldDescriptors: VModelFieldDescriptor<any>[] = [];
/**
* Provides Card Title for Specified Model ...
*
* @param model
* @returns
*/
getModelCardTitle(model: VWebinarViewDto) {
//
// Since card title's are Rescource ID's ...
return this.applyLocale(`${model.title} `);
}
//#endregion
//
//#region Handlers ...
/**
* Handle Errors ...
*
* @param error
*/
async handleCustomError(
error?: VQueryPresenterActionIdentifier,
): Promise<void> {
//
await this.setLoadingState(false);
//
// TODO: Implement this ...
console.log('handle Custom Error: ', error);
}
/**
* Override this for adding additional futures ...
*
* @param tab
*/
async handleStateAfterTabOpen(tab: number) {
await super.handleStateAfterTabOpen(tab);
//
let selector: string = undefined;
let fabs: XSimpleFabButton[] = [];
let model: VWebinarViewDto = undefined;
//
let actions = await toSimpleFabItems(this.actions);
this.closeActions();
this.actions = undefined;
//
const userId = await this.getValueAsync(this.sharedService.userId$);
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
const isAgent = await this.getValueAsync(this.sharedService.isAgent$);
const isAdminAgent = isAdmin || isAgent;
//
switch (tab) {
//
case VQueryPresenterTabs.List:
//
// if Show Items as List ...
if (this.showItemsAsList) {
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.listItemViewProperties },
},
});
} else {
//
// Call each Items Action Provider Separately ...
//
// Prepare Non As List Items Properties ...
const properties: VDtoCommonProperties = {
...this.listItemViewProperties,
isInPage: true,
wrapWithCard: true,
cardShowActions: true, // Since Each Items has Separate Actions ...
};
//
sendAction({
provider: this.listItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...properties },
},
});
}
//
break;
//
case VQueryPresenterTabs.Add:
//
this.addModel = {
...defaultVWebinarViewDto,
language: this.managerService.currentLocale,
};
//
sendAction({
provider: this.addItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.addModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Update:
//
model = { ...this.updateModel };
//
sendAction({
provider: this.updateItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.updateModelViewProperties },
},
});
break;
//
case VQueryPresenterTabs.Details:
//
model = { ...this.detailsModel };
//
sendAction({
provider: this.detailsItemActionProvider,
action: VBaseDtoAction.SetProperties,
payload: {
props: { ...this.detailsModelViewProperties },
},
});
//
// Adding Custom Actions ...
//
let isOwner = this.detailsModel.ownerId === userId;
let canConnect = isOwner || this.detailsModel.enabled;
let canClose = (isOwner || isAdmin) && this.detailsModel.enabled;
//
// Connect ...
if (canConnect) {
//
fabs = [
...fabs,
{
...defaultConnectFabButton,
index: 1,
side: XFabButtonSide.TOP,
handler: async () => {
await this.doItemActions({
id: defaultConnectFabButton.id,
data: { ...this.detailsModel },
});
},
},
];
}
//
// Close ...
if (canClose) {
//
fabs = [
...fabs,
{
...defaultCloseFabButton,
index: 1,
side: XFabButtonSide.START,
handler: async () => {
await this.doItemActions({
id: defaultCloseFabButton.id,
data: { ...this.detailsModel },
});
},
},
];
}
//
// Actions Preparing ...
actions = {
...(!isNullOrUndefined(actions) ? actions : defaultOptionsFabItem),
childs: [
...(!isNullOrUndefined(actions) && this.hasChild(actions.childs)
? actions.childs
: []),
...fabs,
],
};
//
// Issue Action if Waiting for Details ...
if (!isNullOrEmptyString(this.paramAction)) {
//
const action = this.paramAction;
this.paramAction = undefined;
//
await this.doItemActions({
id: action,
data: { ...this.detailsModel },
});
}
break;
}
//
// Here can Handle Actions Permissions and States ...
if (!isNullOrUndefined(actions)) {
//
// Only if model Exists ...
if (!isNullOrUndefined(model)) {
//
// Retrieve Requirements ...
//
const isOwner = model.ownerId === userId;
//
const canEdit = isOwner || isAdmin;
const canRemove = isOwner || isAdmin;
const canClose = (isOwner || isAdmin) && model.enabled;
const canConnect = isOwner || model.enabled;
//
// Edit ...
if (
actions.id === defaultEditFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultEditFabButton.id,
)) >= 0
) {
//
if (actions.id === defaultEditFabButton.id) {
actions.disabled =
!canEdit || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultEditFabButton.id
? !canEdit ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
//
// Remove ...
if (
actions.id === defaultRemoveFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultRemoveFabButton.id,
)) >= 0
) {
//
if (actions.id === defaultRemoveFabButton.id) {
actions.disabled =
!canRemove || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultRemoveFabButton.id
? !canRemove ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
//
// Close ...
if (
actions.id === defaultCloseFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultCloseFabButton.id,
)) >= 0
) {
//
if (actions.id === defaultCloseFabButton.id) {
actions.disabled =
!canClose || actions.disabled || this.loading || this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultCloseFabButton.id
? !canClose ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
//
// Connect ...
if (
actions.id === defaultConnectFabButton.id ||
(!isNullOrUndefined(actions.childs) &&
this.hasChild(actions.childs) &&
actions.childs.findIndex(
(a) => a.id === defaultConnectFabButton.id,
)) >= 0
) {
//
if (actions.id === defaultConnectFabButton.id) {
actions.disabled =
!canConnect ||
actions.disabled ||
this.loading ||
this.uiDisabled;
} else {
//
actions.childs = [
...actions.childs.map((ch) => {
return {
...ch,
disabled:
ch.id === defaultConnectFabButton.id
? !canConnect ||
ch.disabled ||
this.loading ||
this.uiDisabled
: ch.disabled,
};
}),
];
}
}
}
//
// Update Actions ...
this.actions = {
...actions,
};
}
}
/**
* Override this for adding additional futures ...
*
* @param from
* @param to
*/
async handleStateBeforeTabOpen(from: number, to: number) {
await super.handleStateBeforeTabOpen(from, to);
//
this.closeActions();
//
switch (to) {
//
case VQueryPresenterTabs.List:
//
this.addModel = undefined;
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Add:
//
this.updateModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Update:
//
this.addModel = undefined;
this.detailsModel = undefined;
break;
//
case VQueryPresenterTabs.Details:
//
if (
isNullOrUndefined(this.detailsModel) &&
!isNullOrUndefined(this.updateModel) &&
from === VQueryPresenterTabs.Update
) {
//
this.detailsModel = {
...this.updateModel,
};
}
//
this.addModel = undefined;
this.updateModel = undefined;
break;
}
}
/**
* Here We Do All Items Based Actions ...
*
* @param info
* @returns
*/
async doItemActions(info: {
id: string;
data: VWebinarViewDto;
}): Promise<void> {
//
if (isNullOrUndefined(info) || isNullOrEmptyString(info.id)) {
return;
}
//
let isKnown = false;
//
// Close ...
isKnown =
info.id === defaultCloseSlideOption.id ||
info.id === defaultCloseFabButton.id;
if (isKnown) {
//
if (
isNullOrUndefined(info.data) &&
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
info.data = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(info.data)) {
await this.handleCloseWebinar(info.data);
}
}
//
// Conect ...
isKnown =
info.id === defaultConnectSlideOption.id ||
info.id === defaultConnectFabButton.id;
if (isKnown) {
//
if (
isNullOrUndefined(info.data) &&
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
info.data = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(info.data)) {
await this.handleConnectWebinar(info.data);
}
}
//
// Details ...
isKnown = info.id === defaultDetailsSlideOption.id;
if (isKnown) {
//
this.detailsModel = {
...info.data,
};
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Details,
);
}
//
// Edit ...
isKnown =
info.id === defaultEditSlideOption.id ||
info.id === defaultEditFabButton.id;
if (isKnown) {
//
if (!isNullOrUndefined(info.data)) {
//
this.updateModel = {
...info.data,
};
} else if (
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.updateModel = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(this.updateModel)) {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Update,
);
}
}
//
// Remove ...
isKnown =
info.id === defaultRemoveSlideOption.id ||
info.id === defaultRemoveFabButton.id;
if (isKnown) {
//
if (
isNullOrUndefined(info.data) &&
!isNullOrUndefined(this.detailsModel) &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
info.data = {
...this.detailsModel,
};
}
//
if (!isNullOrUndefined(info.data)) {
await this.handleRemoveWebinar(info.data);
}
}
}
/**
* Handle Specified Param ...
*
* @param identifier
* @param acttion
*/
async handleParamAction(identifier: string, action: string): Promise<void> {
await super.handleParamAction(identifier, action);
//
if (isNullOrEmptyString(identifier) || isNullOrEmptyString(action)) {
//
this.hasParams = false;
return;
}
//
let model = await this.handleGetSpecifiedModel(identifier, true);
if (!isNullOrUndefined(model)) {
//
this.paramAction = action;
this.detailsModel = { ...model };
this.activeTab = VQueryPresenterTabs.Details;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.activeTab,
);
} else {
this.hasParams = false;
}
}
/**
* Close Specified Webinar if it's Open ...
*
* @param model
* @returns
*/
async handleCloseWebinar(model: VWebinarViewDto) {
//
if (isNullOrUndefined(model) || !model.enabled) {
return;
}
//
model = {
...model,
enabled: false,
};
const dto = this.dtoQueryResult.items.find((dto) => dto.id === model.id);
const dtoModel = ToWebinarDto(model, dto);
if (isNullOrUndefined(dto) || isNullOrUndefined(dtoModel)) {
return;
}
//
// Do Based on Confirm ...
await this.doConfirmedAction(async () => {
//
await this.setLoadingState(true);
//
let response: XWebinarDto = undefined;
try {
//
const connectionId =
this.sharedService.saherElmAPIService.webinars
.webinarEntityPushService.connectionId;
response = await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.update(
dtoModel,
connectionId,
),
);
} catch {
//
await this.handleCustomError('CloseWebinarError');
return;
}
//
await this.setLoadingState(false);
//
// Check if Response Provided ...
// Update Contents ...
if (!isNullOrUndefined(response)) {
//
const updateVDto = ToWebinarViewDto(
response,
this.managerService.currentLocale,
);
const actions = await this.prepareListActions(updateVDto);
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
actions,
model: updateVDto,
key: nameof<VWebinarViewDto>('id'),
});
await this.handleStateAfterTabOpen(this.activeTab);
}
});
}
/**
* Handle Connecting to Webinar ...
*
* @param model
*/
async handleConnectWebinar(model: VWebinarViewDto) {
//
// Validate ...
if (isNullOrUndefined(model) || isNullOrEmptyString(model.id)) {
return;
}
//
// Present Webinar Connect Model ...
let modal = await this.managerService.dialogService.presentModal({
autoHeight: true,
fullScreen: true,
backdropDismiss: false,
component: VWebinarConnectComponent,
componentProps: {
//
context: VWebinarModalContext.Modal,
model: { ...model },
toolbarTitle: this.AppResourceIDs.webinar,
},
});
if (!isNullOrUndefined(model)) {
this.lock = true;
}
/**
* wait for Model Result ...
*/
const data = await modal.onDidDismiss();
modal.remove();
modal.dismiss();
this.lock = false;
}
/**
* Handle Removing Webinar ...
*
* @param model
*/
async handleRemoveWebinar(model: VWebinarViewDto) {
//
await this.doConfirmedAction(async () => {
//
await this.setLoadingState(true);
//
try {
//
const connectionId =
this.sharedService.saherElmAPIService.webinars
.webinarEntityPushService.connectionId;
//
await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.remove(
model.id,
connectionId,
),
);
} catch {
//
await this.handleCustomError('RemoveModelError');
return;
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
model,
actions: [],
key: nameof<VWebinarViewDto>('id'),
});
//
this.showSuccess();
await this.setLoadingState(false);
//
// Handle Change Tabs if in Details ...
if (
this.activeTab === VQueryPresenterTabs.List ||
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.prevTab = undefined;
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List,
);
}
});
}
/**
* Handle Running toolbar Back Handler on Specified Case
* when User Pressed Escape Button ...
*
* @param event
* @returns
*/
async handleGlobalEscapePressed(event: any) {
//
if (
this.lock ||
!this.toolbarHasBack ||
!this.toolbarShowBack ||
!isFunction(this.toolbarBackHandler) ||
[VQueryPresenterTabs.Add, VQueryPresenterTabs.Update].includes(
this.activeTab,
)
) {
return;
}
//
await this.toolbarBackHandler();
}
/**
* Handle Tags State ...
*
* @param event
* @returns
*/
handleTagsStateChanged(event: VTagsPresenterModelState) {
//
if (isNullOrUndefined(event)) {
return;
}
//
this.tagsState = {
...event,
};
}
/**
* Handle Files State ...
*
* @param event
* @returns
*/
handleFilesStateChanged(event: VFilesPresenterModelState) {
//
if (isNullOrUndefined(event)) {
return;
}
//
this.filesState = {
...event,
};
}
//#endregion
//
//#region Private ...
private provideItemOptions(
userId: string,
isAdmin: boolean,
item: VWebinarViewDto,
) {
//
let result: Array<XSimpleListItemSlideOption> = [];
//
// Validate ...
if (!isNullOrUndefined(item) && !isNullOrEmptyString(userId)) {
//
const isOwner = item.ownerId === userId;
//
const canEdit = isOwner || isAdmin;
const canRemove = isOwner || isAdmin;
const canClose = (isOwner || isAdmin) && item.enabled;
//
// Close ...
if (canClose) {
//
result.push({
...defaultCloseSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled,
} as XSimpleListItemSlideOption);
}
//
// Join ...
result.push({
...defaultConnectSlideOption,
slot: 'start',
icon: this.IconNames.live_tv,
disabled:
this.loading || this.uiDisabled || (!isOwner && !item.enabled),
} as XSimpleListItemSlideOption);
//
// Details ...
result.push({
...defaultDetailsSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled,
} as XSimpleListItemSlideOption);
//
// Edit ...
result.push({
...defaultEditSlideOption,
slot: 'start',
disabled: this.loading || this.uiDisabled || !canEdit,
} as XSimpleListItemSlideOption);
//
// Remove ...
result.push({
...defaultRemoveSlideOption,
slot: 'end',
disabled: this.loading || this.uiDisabled || !canRemove,
} as XSimpleListItemSlideOption);
}
//
return result;
}
isPushServiceConnected = false;
private async handleConnectPushService() {
//
// Preparing Connections ...
try {
//
// Check Connected Before ...
if (
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService.isConnected()
) {
this.isPushServiceConnected = true;
} else {
//
this.isPushServiceConnected = true;
await this.sharedService.saherElmAPIService.webinars.webinarEntityPushService.connect();
}
} catch {
//
await this.handleCustomError('PushServiceConnection');
return;
}
//
// Check Connected ...
if (!this.isPushServiceConnected) {
return;
}
//
// On Added ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService
.onAdded$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event)) {
return;
}
//
// Only Notify when a new Open Webinar Created ...
// Since here is Management Page ...
// TODO: Use this if necessary ...
// if (this.activeTab === VQueryPresenterTabs.List) {
// //
// await this.managerService.notificationService.presentInfoNotification({
// dissmissable: true,
// message: this.resourceProvider(
// this.AppResourceIDs.new_webinar_created
// ),
// opt: {
// duration: 5000,
// buttons: [
// {
// icon: this.IconNames.refresh,
// side: 'end',
// handler: async () => {
// this.sendQueryPresenterAction(
// VQueryPresenterAction.ResetQuery
// );
// },
// } as ToastButton,
// ],
// },
// });
// }
});
//
// On Removed ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService
.onDeleted$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) && isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id,
);
const modelP = ToWebinarViewDto(model, this.managerService.currentLocale);
if (!isNullOrUndefined(model) && !isNullOrUndefined(modelP)) {
//
const actions = await this.prepareListActions(modelP);
//
this.sendQueryPresenterAction(VQueryPresenterAction.RemoveModel, {
actions,
model: modelP,
key: nameof<VWebinarViewDto>('id'),
});
}
//
// Check Details or Edit Tab ...
if (
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.detailsModel) &&
this.detailsModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Details) ||
(!isNullOrUndefined(model) &&
!isNullOrUndefined(this.updateModel) &&
this.updateModel.id === model.id &&
this.activeTab === VQueryPresenterTabs.Update)
) {
//
// Force Unlock ...
this.lock = false;
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List,
);
}
});
//
// On Updated ...
this.applyTakeUntilWrapper(
this.sharedService.saherElmAPIService.webinars.webinarEntityPushService
.onUpdated$,
).subscribe(async (event) => {
//
if (isNullOrUndefined(event) || isNullOrUndefined(this.dtoQueryResult)) {
return;
}
//
// Check if Model Exists in Current Context or not ...
let model = this.dtoQueryResult.items.find(
(i) => i.id === event.model.id,
);
if (isNullOrUndefined(model)) {
return;
}
//
let fromEntity: XWebinarDto = undefined;
try {
fromEntity = await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.get(
event.model.id,
),
);
} catch {
return;
}
//
if (isNullOrUndefined(fromEntity)) {
return;
}
//
const modelP = ToWebinarViewDto(
fromEntity,
this.managerService.currentLocale,
);
//
if (isNullOrUndefined(modelP)) {
return;
}
//
// Preparing Model List Actions ...
const actions = await this.prepareListActions(modelP);
//
// Updating Model on Query Presenter ...
this.sendQueryPresenterAction(VQueryPresenterAction.UpdateModel, {
actions,
model: { ...modelP },
key: nameof<VWebinarViewDto>('id'),
});
//
// Update Tab ...
if (
!isNullOrUndefined(this.updateModel) &&
modelP.id === this.updateModel.id &&
this.activeTab === VQueryPresenterTabs.Update
) {
//
this.updateModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VWebinarViewComponent,
this.ModelFieldNames.id,
this.updateModel.id,
);
await this.waitForElement(selector).then(async (el) => {
//
this.updateItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.updateModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
//
// Details Tab ...
if (
!isNullOrUndefined(this.detailsModel) &&
modelP.id === this.detailsModel.id &&
this.activeTab === VQueryPresenterTabs.Details
) {
//
this.detailsModel = {
...modelP,
};
//
const selector = getQuerySelectorOfSpecifiedAttribute(
VWebinarViewComponent,
this.ModelFieldNames.id,
this.detailsModel.id,
);
await this.waitForElement(selector).then(async (el) => {
//
this.detailsItemActionProvider.next({
action: VBaseDtoAction.RenewModel,
payload: { ...this.detailsModel },
});
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
});
}
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-explorer\webinars-explorer.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-explorer\webinars-explorer.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-explorer\webinars-explorer.page.ts
import { Observable } from 'rxjs';
import { concatMap } from 'rxjs/operators';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { ToWebinarViewDto } from 'src/app/views/webinar/v-webinar.tools';
import { WebinarsBaseProviderPage } from '../webinars-base/webinars-base.provider.page';
@Component({
selector: 'webinars-explorer',
styleUrls: ['./webinars-explorer.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../webinars-base/webinars-base.provider.page.html',
})
export class WebinarsExplorerPage extends WebinarsBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsExplorerPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.webinars.webinarProviderService
.get(identifier)
.pipe(
concatMap((m) =>
this.getValue(ToWebinarViewDto(m, this.managerService.currentLocale)),
),
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.webinars.webinarProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-management\webinars-management.page.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-management\webinars-management.page.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-management\webinars-management.page.ts
import { Observable } from 'rxjs';
import { concatMap } from 'rxjs/operators';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { ToWebinarViewDto } from 'src/app/views/webinar/v-webinar.tools';
import { WebinarsBaseProviderPage } from '../webinars-base/webinars-base.provider.page';
@Component({
selector: 'webinars-management',
styleUrls: ['./webinars-management.page.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: '../webinars-base/webinars-base.provider.page.html',
})
export class WebinarsManagementPage extends WebinarsBaseProviderPage {
//
//#region Props ...
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
//
this.configureAsManagementPage();
}
//#endregion
//
//#region Abstractions ...
getModelTask(identifier: string): Observable<any> {
//
return this.sharedService.saherElmAPIService.webinars.webinarProviderService
.get(identifier)
.pipe(
concatMap((m) =>
this.getValue(ToWebinarViewDto(m, this.managerService.currentLocale)),
),
);
}
queryModelTask(query: XQueryDto): Observable<XQueryResultDto<any>> {
return this.sharedService.saherElmAPIService.webinars.webinarProviderService.query(
query,
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\pages\webinars\webinars-management\webinars-management.typings.ts
import { isValueInEnum } from 'src/app/views/public-api';
export enum WebinarsManagementTask {
Create = 'Create_Webinar',
Remove = 'Remove_Webinar',
Update = 'Update_Webinar',
QueryList = 'QueryList_Webinars',
}
export function isWebinarsManagementTask(value: string, forceExactSames: boolean = true) {
return isValueInEnum(value, Object.assign(WebinarsManagementTask, {}), forceExactSames);
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\public-api.ts
//
//#region Services ...
export * from './services/shared.service';
//#endregion
//
//#region Typings ...
export * from './typings/shared.typings';
export * from './typings/x-electron.typings';
export * from './services/x-scrolling-provider.service'
//#endregion
//
//#region Tools ...
//#endregion
//
// Module ...
export * from './shared.module';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\shared.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [CommonModule],
exports: [CommonModule],
})
export class SharedModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\services\shared.service.ts
import {
XParam,
XLoggable,
toPromise,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XUserRoleInfo,
toRoleInfoByRoles,
XSaherElmAPIService,
} from 'x-saherelm-api-sdk';
import { timer } from 'rxjs';
import {
ApiValidators,
XAccountService,
XLoginResponseDto,
prepareUserProfileDtoFields,
} from 'x-framework-identity-sdk';
import { X_CONFIG } from 'src/app/config/x.config';
import { Pages } from 'src/app/config/page.config';
import { XConfig } from 'src/app/config/app.config';
import { XManagerService } from 'x-framework-services';
import { XElectronService } from './x-electron.service';
import { concatMap, filter, map } from 'rxjs/operators';
import { EventEmitter, Inject, Injectable } from '@angular/core';
import { XContentShareService } from './x-content-share-service';
import { XMediaDevicesService } from './x-media-devices.service';
import { XScrollingProviderService } from './x-scrolling-provider.service';
@Injectable({
providedIn: 'root',
})
export class SharedService extends XLoggable {
//
//#region Props ...
//
//#region User/Role/Token Helpers ...
/**
* XAccountState retriever ...
*/
state$ = this.authService.state$;
/**
* check if a user logged in or not ...
*/
isAuthenticated$ = this.state$.pipe(
map((res) => {
//
if (!res) {
return false;
}
//
return res.isLoggedIn;
}),
);
/**
* user profile retriever ...
*/
user$ = this.state$.pipe(
filter((res) => !!res),
map((state) => prepareUserProfileDtoFields(state.profile)),
);
/**
* user id retriever ...
*/
userId$ = this.user$.pipe(map((user) => user.userId));
/**
* user name retriever ...
*/
userName$ = this.user$.pipe(map((user) => user.userName));
/**
* token retriever ...
*/
token$ = this.state$.pipe(
filter((res) => !!res),
map((state) => state.accessToken),
);
/**
* retrieve role ...
*/
roles$ = this.user$.pipe(map((user) => user.roles));
/**
* retrieve userRoleInfo ...
*/
userRoleInfo$ = this.roles$.pipe(
map((roles) => {
//
const result: XUserRoleInfo = toRoleInfoByRoles(roles);
//
return result;
}),
);
//
isAdmin$ = this.userRoleInfo$.pipe(map((info) => info && !!info.isAdmin));
isAgent$ = this.userRoleInfo$.pipe(map((info) => info && !!info.isAgent));
isUser$ = this.userRoleInfo$.pipe(map((info) => info && !!info.isUser));
isAdminOrAgent$ = this.isAdmin$.pipe(
concatMap((isAdmin) =>
this.isAgent$.pipe(
map((isAgent) => {
return isAdmin || isAgent;
}),
),
),
);
//#endregion
//
//#region App Event Emmitters ...
public BeforeAppUnload = new EventEmitter();
public AppUnload = new EventEmitter();
//#endregion
//#endregion
//
//#region Constructor ...
constructor(
@Inject(X_CONFIG)
public config: XConfig,
public authService: XAccountService,
public apiValidators: ApiValidators,
public managerService: XManagerService,
public accountsService: XAccountService,
public electronService: XElectronService,
public saherElmAPIService: XSaherElmAPIService,
public mediaDevicesService: XMediaDevicesService,
public contentShareService: XContentShareService,
public scrollingInfoProvider: XScrollingProviderService,
) {
super(config);
//
// Check Refresh Token is Avaialble ...
if (
!isNullOrUndefined(config.refreshTokensDelay) &&
config.refreshTokensDelay > 0
) {
//
// Setting Timer ...
timer(0, config.refreshTokensDelay).subscribe(async (counter) => {
//
// Check is User Logged in or not ...
const isLoggedIn = await this.authService.isLoggedIn();
if (isLoggedIn) {
//
// Retrieve User State ...
const state = await toPromise(this.authService.state$);
if (!isNullOrUndefined(state)) {
//
// Check User Token Expiration ...
const isExpired = await this.authService.isExpired();
if (isExpired) {
//
// Try to Refresh Tokens if Expired ...
const tokens = await toPromise(
this.authService.refreshTokensOnly(),
);
if (!isNullOrUndefined(tokens)) {
//
// Update User Tokens ...
let oldInfo: XLoginResponseDto = {
accessToken: state.accessToken,
refreshToken: state.refreshToken,
expiresAt: state.expiresAt,
profile: { ...state.profile },
};
//
await this.authService.updateUserTokens(oldInfo, tokens);
}
}
}
}
});
}
}
//#endregion
//
//#region Actions ...
isMe(userId: string) {
return this.user$.pipe(
map(
(profile) =>
!isNullOrUndefined(profile) &&
!isNullOrEmptyString(userId) &&
(userId === profile.email ||
userId === profile.userId ||
userId === profile.userName ||
userId === profile.phoneNumber),
),
);
}
/**
* Check Specified User identifier is point to current user or not ...
*
* @param userId
* @returns
*/
isNotMe(userId: string) {
//
return this.user$.pipe(
map(
(profile) =>
!isNullOrUndefined(profile) &&
!isNullOrEmptyString(userId) &&
userId !== profile.email &&
userId !== profile.userId &&
userId !== profile.userName &&
userId !== profile.phoneNumber,
),
);
}
/**
* Generate and Retrieve Specified User's Profile View Page URL ...
*
* @param userID
* @param returnURL
* @returns
*/
getUserProfileURL(userID: string, returnURL?: string) {
//
let result = '';
//
// Normalize Args ...
if (isNullOrEmptyString(userID)) {
return result;
}
//
const profileURL = this.managerService.getPageRoute(Pages.ProfileView);
const profileFullURL = this.managerService.getFullUrl(profileURL);
//
result = `${profileFullURL}?${XParam.Id}=${userID}`;
if (!isNullOrEmptyString(returnURL)) {
result = `${result}&${XParam.ReturnUrl}=${returnURL}`;
}
//
return result;
}
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\services\x-content-share-service.ts
import { Injectable } from '@angular/core';
import { isNullOrUndefined, XBaseService } from 'x-framework-core';
/**
* a Model for Sharng ...
*/
export interface XShrableDto {
title: string;
content: string;
link?: string;
}
@Injectable({
providedIn: 'root',
})
export class XContentShareService extends XBaseService {
/**
* Check Share Content is Supported or not ...
*/
isShareSupport() {
return !isNullOrUndefined(navigator.share);
}
/**
* Share Specific Data ...
*
* @param model XSharableDto instance
* @returns boolean
*/
async share(model: XShrableDto) {
//
// Validate State and Args ...
if (isNullOrUndefined(model) || !this.isShareSupport()) {
return false;
}
//
await navigator.share({
title: model.title,
text: model.content,
url: model.link,
});
//
return true;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\services\x-electron.service.ts
import {
getMills,
parseJson,
XBaseService,
throwException,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import { Subject } from 'rxjs';
import {
XElectronChannel,
XElectronChannelIdentifier,
XElectronMessageDto,
} from '../typings/x-electron.typings';
import { IpcRenderer } from 'electron';
import { Inject, Injectable } from '@angular/core';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { filter, first, map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class XElectronService extends XBaseService {
//
//#region Props ...
//
private IPC_RENDERER: IpcRenderer;
private ELECTRON_WINDOW_ID: string;
private MESSAGE_SUBJECT = new Subject<XElectronMessageDto>();
//
readonly message$ = this.MESSAGE_SUBJECT.asObservable();
//
//#region Getters ...
get windowId() {
return this.ELECTRON_WINDOW_ID;
}
//#endregion
//#endregion
//
//#region Constructor ...
constructor(
@Inject(X_CONFIG)
config: XConfig
) {
super(config);
//
this.initialService();
this.logDebug('initialElectron');
}
//#endregion
//
//#region Actions ...
/**
* init service ...
*
* @param force re initial if initialized before ...
*/
init(force?: boolean) {
this.initialService(force);
}
/**
* determines an application is Electron app or not ...
*
* @returns a boolean value
*/
isElectron() {
//
// Find Electron in User Agent ...
const result =
!isNullOrUndefined(window) &&
!isNullOrUndefined(window.navigator) &&
!isNullOrEmptyString(window.navigator.userAgent)
? window.navigator.userAgent.toLowerCase().includes('electron')
: false;
//
// Return result ...
return result;
}
/**
* sending message to window of Electron ...
*
* @param message an instance of XElectronMessageDto for sending to window ...
*/
send(message: XElectronMessageDto) {
//
if (
isNullOrUndefined(message) ||
isNullOrUndefined(this.IPC_RENDERER) ||
(isNullOrEmptyString(this.ELECTRON_WINDOW_ID) &&
isNullOrEmptyString(message.sender))
) {
return;
}
//
if (isNullOrEmptyString(message.sender)) {
message.sender = this.ELECTRON_WINDOW_ID;
}
//
if (isNullOrEmptyString(message.channel)) {
message.channel = XElectronChannel.Default;
}
//
if (isNullOrUndefined(message.timestamp)) {
message.timestamp = getMills();
}
//
this.IPC_RENDERER.send(message.channel, message);
}
//#endregion
//
//#region Protected ...
actionHandler<TReturn>(
channel: XElectronChannelIdentifier,
actionName: string,
payload?: any
) {
//
if (!this.isElectron()) {
throwException('NotAllowed');
}
//
this.send({
channel,
message: actionName,
payload,
});
//
return this.message$.pipe(
filter((msg) => msg.channel === channel && msg.message === actionName),
map((msg) => msg.payload as TReturn),
first()
);
}
//
actionHandlerForFileChannel<TReturn>(actionName: string, payload?: any) {
return this.actionHandler<TReturn>(
XElectronChannel.FileService,
actionName,
payload
);
}
//
actionHandlerForProjectChannel<TReturn>(actionName: string, payload?: any) {
return this.actionHandler<TReturn>(
XElectronChannel.ProjectsService,
actionName,
payload
);
}
//#endregion
//
//#region Private ...
private initialService(force?: boolean) {
//
if (!!force) {
this.IPC_RENDERER = null;
}
//
if (!isNullOrUndefined(this.IPC_RENDERER)) {
return;
}
//
if (!isNullOrUndefined(window.require)) {
//
try {
//
this.IPC_RENDERER = window.require('electron').ipcRenderer;
this.registerMessageListener();
//
this.logDebug('Electron loaded successfully ...');
} catch (e) {
//
this.logWarn('Electron not loaded ...');
throw e;
}
} else {
this.logWarn('Electron not loaded ...');
}
}
private registerMessageListener() {
//
if (isNullOrUndefined(this.IPC_RENDERER)) {
return;
}
//
// Register Handshake Reciever ...
this.IPC_RENDERER.on(XElectronChannel.Handshak, (event, message) => {
//
if (isNullOrUndefined(message)) {
return;
}
//
const mMessage: XElectronMessageDto =
parseJson<XElectronMessageDto>(message);
//
this.ELECTRON_WINDOW_ID = mMessage.reciever;
//
const handshakeReplyMessag: XElectronMessageDto = {
sender: mMessage.reciever,
reciever: mMessage.sender,
channel: XElectronChannel.Handshak,
};
//
this.send(handshakeReplyMessag);
this.logDebug('Handshake Events: ', message, mMessage);
});
//
let channels = { ...Object.assign({}, XElectronChannel) };
delete channels.Handshak;
//
// Register Non Handshake Events ...
Object.keys(channels).forEach((channel) => {
this.IPC_RENDERER.on(channels[channel], (event, message) => {
//
if (isNullOrUndefined(message)) {
return;
}
//
const mMessage: XElectronMessageDto =
parseJson<XElectronMessageDto>(message);
//
this.MESSAGE_SUBJECT.next(mMessage);
//
this.logDebug('Message Events: ', mMessage);
});
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\services\x-media-devices.service.ts
import { Injectable } from '@angular/core';
import { isValueInEnum } from 'src/app/views/tools/v-base.tools';
import { isNullOrUndefined, XLoggable } from 'x-framework-core';
//
//#region Definitions ...
export enum XCameraFacing {
User = 'user',
Screen = 'screen',
Environment = 'environment',
}
export type XCameraFacingIdentifier = XCameraFacing | string;
export function isXCameraFacing(value: string) {
return isValueInEnum(value, Object.assign(XCameraFacing, {}));
}
//#endregion
@Injectable({
providedIn: 'root',
})
export class XMediaDevicesService extends XLoggable {
//
//#region Media Devices ...
/**
* Enumerate Media Devices ...
*
* @returns
*/
async getMediaDevices() {
return await navigator.mediaDevices.enumerateDevices();
}
/**
* retrieve Requested Media Stream ...
*
* @param mode
* @returns
*/
async getMediaDevice(mode: XCameraFacing = XCameraFacing.User) {
//
let result: MediaStream = undefined;
//
if (!isXCameraFacing(mode)) {
return result;
}
//
try {
//
if (mode === XCameraFacing.User) {
result = await this.getFrontCameraDevice();
} else if (mode === XCameraFacing.Environment) {
result = await this.getBackCameraDevice();
} else if (mode === XCameraFacing.Screen) {
result = await this.getDisplayMediaDevice();
}
} catch {}
//
return result;
}
/**
* Retrieve Video Input Devices ...
*
* @returns
*/
async getVideoInputMediaDevices() {
//
return (await this.getMediaDevices()).filter(
(device) => device.kind === 'videoinput'
);
}
/**
* Retrieve Audio Input Devices ...
*
* @returns
*/
async getAudioInputDevices() {
//
return (await this.getMediaDevices()).filter(
(device) => device.kind === 'audioinput'
);
}
/**
* Retrieve Audio Output Devices ...
*
* @returns
*/
async getAudioOutputDevices() {
//
return (await this.getMediaDevices()).filter(
(device) => device.kind === 'audiooutput'
);
}
/**
* Retrieve Front Camera Device ...
*
* @returns
*/
async getFrontCameraDevice() {
//
return await navigator.mediaDevices.getUserMedia({
video: { facingMode: XCameraFacing.User },
audio: true, // front camera
});
}
/**
* Retrieve Back Camera Device ...
*
* @returns
*/
async getBackCameraDevice() {
//
return await navigator.mediaDevices.getUserMedia({
video: { facingMode: { exact: XCameraFacing.Environment } },
audio: true, // back camera
});
}
/**
* Retrieve Display Media Device for Screen Sharing ...
*
* @param options
* @returns
*/
async getDisplayMediaDevice(options?: any) {
//
if (isNullOrUndefined(options)) {
options = {};
}
//
// Always Pass Video ...
options = {
video: { cursor: 'always' },
};
//
return await (navigator.mediaDevices as any).getDisplayMedia(options);
}
/**
* Check Has Camera Facing or not ...
*
* @param facingMode
* @returns
*/
async hasCameraFacing(facingMode: XCameraFacingIdentifier) {
//
let result =
isXCameraFacing(facingMode) && facingMode !== XCameraFacing.Screen;
if (!result) {
return result;
}
//
try {
//
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: { exact: facingMode } },
});
//
stream.getTracks().forEach((track) => track.stop()); // stop after test
//
result = true;
} catch (err) {
result = false;
}
//
return result;
}
/**
* Check has Front Camera ...
*
* @returns
*/
async hasFrontCamera() {
return await this.hasCameraFacing(XCameraFacing.User);
}
/**
* Check has Back Camera ...
*
* @returns
*/
async hasBackCamera() {
//
const devices = await navigator.mediaDevices.enumerateDevices();
var result = devices.some(
(d) =>
d.kind === 'videoinput' &&
(d.label.toLowerCase().includes('back') ||
d.label.toLowerCase().includes('rear'))
);
//
return result;
}
/**
* Check Can Switch Camera ...
*
* @returns
*/
async canSwitchCamera() {
//
const hasBack = await this.hasBackCamera();
//
return hasBack;
}
/**
* Retrieve Default Media Devices ...
*
* @returns
*/
async getDefaultCamera() {
//
const hasFront = await this.hasFrontCamera();
if (hasFront) {
return await this.getFrontCameraDevice();
}
//
return navigator.mediaDevices.getUserMedia();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\services\x-scrolling-provider.service.ts
import { Injectable } from '@angular/core';
import {
isNullOrEmptyString,
isNullOrUndefined,
XLoggable,
} from 'x-framework-core';
export interface XScrollingHoldInfo {
identifier: string;
yOffset: number;
xOffset: number;
}
@Injectable({
providedIn: 'root',
})
export class XScrollingProviderService extends XLoggable {
//
private holdeds = new Map<string, XScrollingHoldInfo>();
/**
* Check Has Specified Scrolling Info or not ...
*
* @param identifier
* @returns
*/
has(identifier: string) {
return this.holdeds.has(identifier);
}
/**
* Get Specified Identifier Scrolling Info ...
*
* @param identifier
* @returns
*/
get(identifier: string) {
return this.holdeds.get(identifier);
}
/**
* Remove Specified Identifier Scrolling Info ...
*
* @param identifier
* @returns
*/
remove(identifier: string) {
//
let result = false;
//
result = this.holdeds.has(identifier);
if (result) {
result = this.holdeds.delete(identifier);
}
//
return result;
}
/**
* Set Specified Model info to Holds ...
*
* @param info
* @returns
*/
set(info: XScrollingHoldInfo) {
//
let result = false;
//
// Validate ...
if (isNullOrUndefined(info) || isNullOrEmptyString(info.identifier)) {
return result;
}
//
this.holdeds.set(info.identifier, info);
result = this.has(info.identifier);
//
return result;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\typings\shared.typings.ts
export const XSharedServiceKeys = {
PushConnectionID: 'PCID',
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\shared\typings\x-electron.typings.ts
import * as Electron from 'electron';
import { XBaseDto } from 'x-framework-core';
export enum XElectronChannel {
Handshak = 'xHandshake',
Default = 'xMessage',
FileService = 'xFileService',
ProjectsService = 'xProjectsService',
}
export type XElectronChannelIdentifier = XElectronChannel | string;
export interface XElectronMessageDto extends XBaseDto {
payload?: any;
sender?: string;
message?: string;
reciever?: string;
timestamp?: number;
channel?: XElectronChannelIdentifier;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\public-api.ts
//
// Tools ...
export * from './tools/date.tools';
export * from './tools/v-base.tools';
export * from './tools/action-provider.tools';
//
// Typings ...
export * from './typings/x.state.enum';
export * from './typings/common.typings';
export * from './typings/dto.common.typings';
export * from './typings/simple.fab.typings';
export * from './typings/simple.page.typings';
export * from './typings/v-base-form.typings';
export * from './typings/simple.form.typings';
export * from './typings/simple.list-item.typings';
export * from './typings/simple.actionbar.typings';
//
// Classes ...
export * from './classes/v-base.component';
//
// Directives ...
export * from './directives/x-html-include.directive';
export * from './directives/x-insert-view-ref.directive';
export * from './directives/x-render-component.directive';
export * from './directives/x-focused-provider.directive';
export * from './directives/x-on-escape-pressed.global.directive';
//
// Constants ...
export * from './constants/fab.defaults';
export * from './constants/button.defaults';
export * from './constants/actionbar.defaults';
export * from './constants/slide.options.defaults';
//
//#region Tests ...
//
// Icons Presenter ...
export * from './tests/v-icons-presenter/v-icons-presenter.component';
//#endregion
//
//#region Views ...
//
// VPage ...
export * from './v-page/v-page.component';
export * from './v-page-presenter/v-page-presenter.typings';
export * from './v-page-presenter/v-page-presenter.component';
//
// VError ...
export * from './v-error/v-error.component';
//
// VPaginator ...
export * from './v-paginator/v-paginator.typings';
export * from './v-paginator/v-paginator.component';
//
// VDto ...
export * from './v-dto/v-dto.typings';
export * from './v-dto/v-dto.component';
//
// VLazyView ...
export * from './v-lazy-view/v-lazy-view.typings';
export * from './v-lazy-view/v-lazy-view.component';
//
// VQueryPresenter ...
export * from './v-query-presenter/v-query-presenter.typings';
export * from './v-query-presenter/v-query-presenter.component';
//
// VPerson ...
export * from './v-person/v-person.tools';
export * from './v-person/v-person.typings';
export * from './v-person/v-person.component';
//
// VReference ...
export * from './v-reference/v-reference.typings';
export * from './v-reference/v-reference.configs';
export * from './v-reference/v-reference.component';
//
// VVideo ...
export * from './v-video/v-video.configs';
export * from './v-video/v-video.typings';
export * from './v-video/v-video.component';
//
//#region Account ...
//
// VUser ...
export * from './account/v-user/v-user.tools';
export * from './account/v-user/v-user.typings';
export * from './account/v-user/v-user.component';
//
// VFriendship ...
export * from './account/v-friendship/v-friendship.tools';
export * from './account/v-friendship/v-friendship.typings';
export * from './account/v-friendship/v-friendship.component';
//
// VAvatar ...
export * from './account/v-avatar/v-avatar.typings';
export * from './account/v-avatar/v-avatar.component';
//
// VProfile ...
export * from './account/v-profile/v-profile.typings';
export * from './account/v-profile/v-profile.component';
//
// VLogin ...
export * from './account/v-login/v-login.component';
//
// VRegister ...
export * from './account/v-register/v-register.component';
//
// Configs ...
export * from './account/account.configs';
//#endregion
//
//#region String ...
export * from './string/v-string.tools';
export * from './string/v-string.typings';
export * from './string/v-string/v-string.component';
export * from './string/v-locale/v-locale.component';
export * from './string/v-resource/v-resource.component';
//#endregion
//
//#region Tag ...
export * from './tags/v-tag.configs';
export * from './tags/v-tag/v-tag.typings';
export * from './tags/v-tag/v-tag.component';
export * from './tags/v-tag-input/v-tag-input.typings';
export * from './tags/v-tag-input/v-tag-input.component';
export * from './tags/v-tags-presenter/v-tags-presenter.typings';
export * from './tags/v-tags-presenter/v-tags-presenter.component';
//#endregion
//
//#region File ...
export * from './files/v-file.tools';
export * from './files/v-file.configs';
export * from './files/v-file/v-file.typings';
export * from './files/v-media/v-media.config';
export * from './files/v-file/v-file.component';
export * from './files/v-media/v-media.typings';
export * from './files/v-media/v-media.component';
export * from './files/v-file-input/v-file-input.typings';
export * from './files/v-file-input/v-file-input.component';
export * from './files/v-files-presenter/v-files-presenter.typings';
export * from './files/v-files-presenter/v-files-presenter.component';
//#endregion
//
//#region Webinar ...
export * from './webinar/v-webinar.tools';
export * from './webinar/v-webinar.configs';
export * from './webinar/v-webinar.typings';
export * from './webinar/v-webinar-view/v-webinar-view.component';
export * from './webinar/v-webinar-connect/v-webinar-connect.typings';
export * from './webinar/v-webinar-connect/v-webinar-connect.component';
//#endregion
//#endregion
//
// Module ...
export * from './views.module';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\views.module.ts
import {
XFrameworkCoreModule,
XDirectivesModule as XFrameworkCoreDirectivesModule,
} from 'x-framework-core';
import {
XManagerService,
XHttpInterceptorService,
XFrameworkServicesModule,
XDirectivesModule as XFrameworkServicesDirectivesModule,
} from 'x-framework-services';
import {
XIDEModule,
XFabModule,
XMapModule,
XTabsModule,
XPageModule,
XCardModule,
XIconModule,
XListModule,
XGridModule,
XFormModule,
XTableModule,
XThemeModule,
XEmptyModule,
XColorModule,
XButtonModule,
XSliderModule,
XSpinnerModule,
XSlotterModule,
XCounterModule,
XStepperModule,
XDialogsModule,
XMarkdownModule,
XSearchBarModule,
XThumbnailModule,
XActionBarModule,
XDataPagerModule,
XFileUploadModule,
XProgressBarModule,
XColorPickerModule,
XTabNavigatorModule,
XFileDropAreaModule,
XAvatarUploadModule,
XImageCropperModule,
XDataScrollerModule,
XBaseComponentModule,
XExpandableListModule,
XModelPresenterModule,
XExpandablePanelModule,
XDescriptionListModule,
XFrameworkComponentsModule,
} from 'x-framework-components';
import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { CommonModule } from '@angular/common';
import { VDto } from './v-dto/v-dto.component';
import { BidiModule } from '@angular/cdk/bidi';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { MatChipsModule } from '@angular/material/chips';
import { VPageComponent } from './v-page/v-page.component';
import { VTagComponent } from './tags/v-tag/v-tag.component';
import { VErrorComponent } from './v-error/v-error.component';
import { VVideoComponent } from './v-video/v-video.component';
import { VLazyView } from './v-lazy-view/v-lazy-view.component';
import { VFileComponent } from './files/v-file/v-file.component';
import { VPersonComponent } from './v-person/v-person.component';
import { VUserComponent } from './account/v-user/v-user.component';
import { VMediaComponent } from './files/v-media/v-media.component';
import { VLoginComponent } from './account/v-login/v-login.component';
import { VStringComponent } from './string/v-string/v-string.component';
import { VLocaleComponent } from './string/v-locale/v-locale.component';
import { VAvatarComponent } from './account/v-avatar/v-avatar.component';
import { VPaginatorComponent } from './v-paginator/v-paginator.component';
import { VReferenceComponent } from './v-reference/v-reference.component';
import { VProfileComponent } from './account/v-profile/v-profile.component';
import { VTagInputComponent } from './tags/v-tag-input/v-tag-input.component';
import { XHTMLIncludeDirective } from './directives/x-html-include.directive';
import { VResourceComponent } from './string/v-resource/v-resource.component';
import { VPagePresenter } from './v-page-presenter/v-page-presenter.component';
import { VFileInputComponent } from './files/v-file-input/v-file-input.component';
import { XInsertViewRefDirective } from './directives/x-insert-view-ref.directive';
import { VFriendshipComponent } from './account/v-friendship/v-friendship.component';
import { XFocusedProviderDirective } from './directives/x-focused-provider.directive';
import { XRenderComponentDirective } from './directives/x-render-component.directive';
import { VWebinarViewComponent } from './webinar/v-webinar-view/v-webinar-view.component';
import { VQueryPresenterComponent } from './v-query-presenter/v-query-presenter.component';
import { VTagsPresenterComponent } from './tags/v-tags-presenter/v-tags-presenter.component';
import { VIconsPresenterComponent } from './tests/v-icons-presenter/v-icons-presenter.component';
import { VFilesPresenterComponent } from './files/v-files-presenter/v-files-presenter.component';
import { VWebinarConnectComponent } from './webinar/v-webinar-connect/v-webinar-connect.component';
import { XOnEscapePressedGlobalDirective } from './directives/x-on-escape-pressed.global.directive';
import { VRegisterComponent } from './account/v-register/v-register.component';
@NgModule({
entryComponents: [
VAvatarComponent,
VProfileComponent,
VTagInputComponent,
VWebinarConnectComponent,
],
declarations: [
//
// Directives ...
XHTMLIncludeDirective,
XInsertViewRefDirective,
XFocusedProviderDirective,
XRenderComponentDirective,
XOnEscapePressedGlobalDirective,
//
//#region Tests ...
VIconsPresenterComponent,
//#endregion
//
// Account ...
VUserComponent,
VLoginComponent,
VAvatarComponent,
VProfileComponent,
VRegisterComponent,
VFriendshipComponent,
//
// Regular ...
VPageComponent,
VErrorComponent,
//
// Lazy View ...
VDto,
VLazyView,
VPaginatorComponent,
//
// Data Fetcher Page ...
VPagePresenter,
//
VVideoComponent,
VPersonComponent,
VReferenceComponent,
VQueryPresenterComponent,
//
// String ...
VStringComponent,
VLocaleComponent,
VResourceComponent,
//
// Tag ...
VTagComponent,
VTagInputComponent,
VTagsPresenterComponent,
//
// File ...
VFileComponent,
VMediaComponent,
VFileInputComponent,
VFilesPresenterComponent,
//
// Webinar ...
VWebinarViewComponent,
VWebinarConnectComponent,
],
providers: [
XManagerService,
//
// Register Http Interceptor ...
{
provide: HTTP_INTERCEPTORS,
useClass: XHttpInterceptorService,
multi: true,
},
],
imports: [
//
BidiModule,
IonicModule,
CommonModule,
//
XFrameworkCoreModule,
XFrameworkCoreDirectivesModule,
//
XFrameworkServicesModule,
XFrameworkServicesDirectivesModule,
//
XEmptyModule,
XDialogsModule,
XBaseComponentModule,
XFrameworkComponentsModule,
//
XIDEModule,
XFabModule,
XMapModule,
XPageModule,
XCardModule,
XIconModule,
XFormModule,
XTabsModule,
XListModule,
XGridModule,
XColorModule,
XTableModule,
XThemeModule,
XButtonModule,
XSliderModule,
XSpinnerModule,
XStepperModule,
XSlotterModule,
XCounterModule,
XMarkdownModule,
XDataPagerModule,
XSearchBarModule,
XThumbnailModule,
XActionBarModule,
XFileUploadModule,
XProgressBarModule,
XColorPickerModule,
XImageCropperModule,
XFileDropAreaModule,
XDataScrollerModule,
XTabNavigatorModule,
XAvatarUploadModule,
XExpandableListModule,
XModelPresenterModule,
XExpandablePanelModule,
XDescriptionListModule,
//
MatChipsModule,
],
exports: [
//
BidiModule,
IonicModule,
CommonModule,
//
XFrameworkCoreModule,
XFrameworkCoreDirectivesModule,
//
XFrameworkServicesModule,
XFrameworkServicesDirectivesModule,
//
XEmptyModule,
XDialogsModule,
XBaseComponentModule,
//
XIDEModule,
XFabModule,
XMapModule,
XPageModule,
XCardModule,
XIconModule,
XFormModule,
XTabsModule,
XListModule,
XGridModule,
XColorModule,
XTableModule,
XThemeModule,
XButtonModule,
XSliderModule,
XSpinnerModule,
XStepperModule,
XSlotterModule,
XCounterModule,
XMarkdownModule,
XDataPagerModule,
XSearchBarModule,
XThumbnailModule,
XActionBarModule,
XFileUploadModule,
XProgressBarModule,
XColorPickerModule,
XImageCropperModule,
XFileDropAreaModule,
XDataScrollerModule,
XTabNavigatorModule,
XAvatarUploadModule,
XExpandableListModule,
XModelPresenterModule,
XExpandablePanelModule,
XDescriptionListModule,
//
MatChipsModule,
//
// Directives ...
XHTMLIncludeDirective,
XInsertViewRefDirective,
XFocusedProviderDirective,
XRenderComponentDirective,
XOnEscapePressedGlobalDirective,
//
//#region Tests ...
VIconsPresenterComponent,
//#endregion
//
// Account ...
VUserComponent,
VLoginComponent,
VAvatarComponent,
VProfileComponent,
VRegisterComponent,
VFriendshipComponent,
//
// Regular ...
VPageComponent,
VErrorComponent,
//
// Lazy View ...
VDto,
VLazyView,
VPaginatorComponent,
//
VPagePresenter,
//
VVideoComponent,
VPersonComponent,
VReferenceComponent,
VQueryPresenterComponent,
//
// String ...
VStringComponent,
VLocaleComponent,
VResourceComponent,
//
// Tag ...
VTagComponent,
VTagInputComponent,
VTagsPresenterComponent,
//
// File ...
VFileComponent,
VMediaComponent,
VFileInputComponent,
VFilesPresenterComponent,
//
// Webinar ...
VWebinarViewComponent,
VWebinarConnectComponent,
],
})
export class ViewsModule {}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\account.configs.ts
import { XIconNames } from 'x-framework-components';
import { XSimplePage } from '../typings/simple.page.typings';
//
//#region Localization Configuration ...
export enum AccountResourceIDs {
account = 'account',
accounts = 'accounts',
profile = 'profile',
profiles = 'profiles',
avatar = 'avatar',
avatars = 'avatars',
register = 'register',
accept_terms = 'accept_terms',
email_confirmation = 'email_confirmation',
input_user_info = 'input_user_info',
input_user_avatar = 'input_user_avatar',
finish_registration = 'finish_registration',
account_page_title = 'account_page_title',
account_page_description = 'account_page_description',
profile_page_title = 'profile_page_title',
profile_page_description = 'profile_page_description',
login_page_title = 'login_page_title',
login_page_description = 'login_page_description',
logout_page_title = 'logout_page_title',
logout_page_description = 'logout_page_description',
register_page_title = 'register_page_title',
register_page_description = 'register_page_description',
email_confirm_page_title = 'email_confirm_page_title',
email_confirm_page_description = 'email_confirm_page_description',
phone_confirm_page_title = 'phone_confirm_page_title',
phone_confirm_page_description = 'phone_confirm_page_description',
reset_password_page_title = 'reset_password_page_title',
reset_password_page_description = 'reset_password_page_description',
change_password_page_title = 'change_password_page_title',
change_password_page_description = 'change_password_page_description',
search_profiles_page_title = 'search_profiles_page_title',
search_profiles_page_description = 'search_profiles_page_description',
}
//
//#region Signle Resources ...
//
//#region account ...
export const account_fa = {
id: 'account',
value: 'حساب',
};
export const account_en = {
id: 'account',
value: 'Account',
};
//#endregion
//
//#region accounts ...
export const accounts_fa = {
id: 'accounts',
value: 'حساب ها',
};
export const accounts_en = {
id: 'accounts',
value: 'Accounts',
};
//#endregion
//
//#region profile ...
export const profile_fa = {
id: 'profile',
value: 'نمایه',
};
export const profile_en = {
id: 'profile',
value: 'Profile',
};
//#endregion
//
//#region profiles ...
export const profiles_fa = {
id: 'profiles',
value: 'نمایه ها',
};
export const profiles_en = {
id: 'profiles',
value: 'Profiles',
};
//#endregion
//
//#region avatar ...
export const avatar_fa = {
id: 'avatar',
value: 'تصویر نمایه',
};
export const avatar_en = {
id: 'avatar',
value: 'Profile Image',
};
//#endregion
//
//#region avatars ...
export const avatars_fa = {
id: 'avatars',
value: 'تصاویر نمایه ها',
};
export const avatars_en = {
id: 'avatars',
value: 'Profile Images',
};
//#endregion
//
//#region register ...
export const register_fa = {
id: 'register',
value: 'ثبت نام',
};
export const register_en = {
id: 'register',
value: 'Register',
};
//#endregion
//
//#region accept_terms ...
export const accept_terms_fa = {
id: 'accept_terms',
value: 'شرایط و ضوابط ثبت نام',
};
export const accept_terms_en = {
id: 'accept_terms',
value: 'Terms and Conditions',
};
//#endregion
//
//#region email_confirmation ...
export const email_confirmation_fa = {
id: 'email_confirmation',
value: 'تایید آدرس پست الکترونیک',
};
export const email_confirmation_en = {
id: 'email_confirmation',
value: 'Email Confirmation',
};
//#endregion
//
//#region input_user_info ...
export const input_user_info_fa = {
id: 'input_user_info',
value: 'دریافت اطلاعات کاربر',
};
export const input_user_info_en = {
id: 'input_user_info',
value: 'Get User Info',
};
//#endregion
//
//#region input_user_avatar ...
export const input_user_avatar_fa = {
id: 'input_user_avatar',
value: 'دریافت تصویر نمایه',
};
export const input_user_avatar_en = {
id: 'input_user_avatar',
value: 'Get User Avatar',
};
//#endregion
//
//#region finish_registration ...
export const finish_registration_fa = {
id: 'finish_registration',
value: 'اتمام ثبت نام',
};
export const finish_registration_en = {
id: 'finish_registration',
value: 'Finish Registration',
};
//#endregion
//
//#region account_page_title ...
export const account_page_title_fa = {
id: 'account_page_title',
value: 'حساب کاربری',
};
export const account_page_title_en = {
id: 'account_page_title',
value: 'User Account',
};
//#endregion
//
//#region account_page_description ...
export const account_page_description_fa = {
id: 'account_page_description',
value: 'مدیریت حساب کاربری',
};
export const account_page_description_en = {
id: 'account_page_description',
value: 'Manage Account',
};
//#endregion
//
//#region profile_page_title ...
export const profile_page_title_fa = {
id: 'profile_page_title',
value: 'نمایه کاربر',
};
export const profile_page_title_en = {
id: 'profile_page_title',
value: 'User Profile',
};
//#endregion
//
//#region profile_page_description ...
export const profile_page_description_fa = {
id: 'profile_page_description',
value: 'اطلاعات مربوط به حساب کاربری',
};
export const profile_page_description_en = {
id: 'profile_page_description',
value: 'User Account Info',
};
//#endregion
//
//#region login_page_title ...
export const login_page_title_fa = {
id: 'login_page_title',
value: 'ورود به سامانه',
};
export const login_page_title_en = {
id: 'login_page_title',
value: 'Login to System',
};
//#endregion
//
//#region login_page_description ...
export const login_page_description_fa = {
id: 'login_page_description',
value: 'ورود به سامانه و بهره مندی از خدمات',
};
export const login_page_description_en = {
id: 'login_page_description',
value: 'Login to System for use Services',
};
//#endregion
//
//#region logout_page_title ...
export const logout_page_title_fa = {
id: 'logout_page_title',
value: 'خروج از سامانه',
};
export const logout_page_title_en = {
id: 'logout_page_title',
value: 'Logout from System',
};
//#endregion
//
//#region logout_page_description ...
export const logout_page_description_fa = {
id: 'logout_page_description',
value: 'خروج از سامانه و بازگشت به صفحه نخست',
};
export const logout_page_description_en = {
id: 'logout_page_description',
value: 'Logout from System and go back to First',
};
//#endregion
//
//#region register_page_title ...
export const register_page_title_fa = {
id: 'register_page_title',
value: 'ثبت نام در سامانه',
};
export const register_page_title_en = {
id: 'register_page_title',
value: 'Register in System',
};
//#endregion
//
//#region register_page_description ...
export const register_page_description_fa = {
id: 'register_page_description',
value: 'ثبت نام و عضویت در سامانه',
};
export const register_page_description_en = {
id: 'register_page_description',
value: 'Signup and Register in System',
};
//#endregion
//
//#region email_confirm_page_title ...
export const email_confirm_page_title_fa = {
id: 'email_confirm_page_title',
value: 'تایید آدرس پست الکترونیک',
};
export const email_confirm_page_title_en = {
id: 'email_confirm_page_title',
value: 'Confirm Email Address',
};
//#endregion
//
//#region email_confirm_page_description ...
export const email_confirm_page_description_fa = {
id: 'email_confirm_page_description',
value: 'اعتبار سنجی آدرس پست الکترونیک',
};
export const email_confirm_page_description_en = {
id: 'email_confirm_page_description',
value: 'Verification of Email Address',
};
//#endregion
//
//#region phone_confirm_page_title ...
export const phone_confirm_page_title_fa = {
id: 'phone_confirm_page_title',
value: 'تایید شماره تلفن همراه',
};
export const phone_confirm_page_title_en = {
id: 'phone_confirm_page_title',
value: 'Confirm Mobile Number',
};
//#endregion
//
//#region phone_confirm_page_description ...
export const phone_confirm_page_description_fa = {
id: 'phone_confirm_page_description',
value: 'اعتبار سنجی شماره تلفن همراه',
};
export const phone_confirm_page_description_en = {
id: 'phone_confirm_page_description',
value: 'Verification of Mobile Phone Number',
};
//#endregion
//
//#region reset_password_page_title ...
export const reset_password_page_title_fa = {
id: 'reset_password_page_title',
value: 'بازنشانی کلمه عبور',
};
export const reset_password_page_title_en = {
id: 'reset_password_page_title',
value: 'Password Reset',
};
//#endregion
//
//#region reset_password_page_description ...
export const reset_password_page_description_fa = {
id: 'reset_password_page_description',
value: 'بازنشانی کلمه عبور فراموش شده',
};
export const reset_password_page_description_en = {
id: 'reset_password_page_description',
value: 'Resetting Forgotten Passwor',
};
//#endregion
//
//#region change_password_page_title ...
export const change_password_page_title_fa = {
id: 'change_password_page_title',
value: 'تغییر کلمه عبور',
};
export const change_password_page_title_en = {
id: 'change_password_page_title',
value: 'Password Change',
};
//#endregion
//
//#region change_password_page_description ...
export const change_password_page_description_fa = {
id: 'change_password_page_description',
value: 'تغییر کلمه عبور کاربر',
};
export const change_password_page_description_en = {
id: 'change_password_page_description',
value: "Change User's Password",
};
//#endregion
//
//#region search_profiles_page_title ...
export const search_profiles_page_title_fa = {
id: 'search_profiles_page_title',
value: 'جستجوی کاربران',
};
export const search_profiles_page_title_en = {
id: 'search_profiles_page_title',
value: 'Users Search',
};
//#endregion
//
//#region search_profiles_page_description ...
export const search_profiles_page_description_fa = {
id: 'search_profiles_page_description',
value: 'جستجوی کاربران جهت برقراری ارتباط',
};
export const search_profiles_page_description_en = {
id: 'search_profiles_page_description',
value: 'Search Users to Lookup Connection',
};
//#endregion
//#endregion
//
export const AccountLocalizationResourceIDs = Object.assign(
AccountResourceIDs,
{}
);
//
export const AccountLocalizationResources = {
//
account: {
fa: { ...account_fa },
en: { ...account_en },
},
accounts: {
fa: { ...accounts_fa },
en: { ...accounts_en },
},
profile: {
fa: { ...profile_fa },
en: { ...profile_en },
},
profiles: {
fa: { ...profiles_fa },
en: { ...profiles_en },
},
avatar: {
fa: { ...avatar_fa },
en: { ...avatar_en },
},
avatars: {
fa: { ...avatars_fa },
en: { ...avatars_en },
},
register: {
fa: { ...register_fa },
en: { ...register_en },
},
accept_terms: {
fa: { ...accept_terms_fa },
en: { ...accept_terms_en },
},
email_confirmation: {
fa: { ...email_confirmation_fa },
en: { ...email_confirmation_en },
},
input_user_info: {
fa: { ...input_user_info_fa },
en: { ...input_user_info_en },
},
input_user_avatar: {
fa: { ...input_user_avatar_fa },
en: { ...input_user_avatar_en },
},
finish_registration: {
fa: { ...finish_registration_fa },
en: { ...finish_registration_en },
},
account_page_title: {
fa: { ...account_page_title_fa },
en: { ...account_page_title_en },
},
account_page_description: {
fa: { ...account_page_description_fa },
en: { ...account_page_description_en },
},
profile_page_title: {
fa: { ...profile_page_title_fa },
en: { ...profile_page_title_en },
},
profile_page_description: {
fa: { ...profile_page_description_fa },
en: { ...profile_page_description_en },
},
login_page_title: {
fa: { ...login_page_title_fa },
en: { ...login_page_title_en },
},
login_page_description: {
fa: { ...login_page_description_fa },
en: { ...login_page_description_en },
},
logout_page_title: {
fa: { ...logout_page_title_fa },
en: { ...logout_page_title_en },
},
logout_page_description: {
fa: { ...logout_page_description_fa },
en: { ...logout_page_description_en },
},
register_page_title: {
fa: { ...register_page_title_fa },
en: { ...register_page_title_en },
},
register_page_description: {
fa: { ...register_page_description_fa },
en: { ...register_page_description_en },
},
email_confirm_page_title: {
fa: { ...email_confirm_page_title_fa },
en: { ...email_confirm_page_title_en },
},
email_confirm_page_description: {
fa: { ...email_confirm_page_description_fa },
en: { ...email_confirm_page_description_en },
},
phone_confirm_page_title: {
fa: { ...phone_confirm_page_title_fa },
en: { ...phone_confirm_page_title_en },
},
phone_confirm_page_description: {
fa: { ...phone_confirm_page_description_fa },
en: { ...phone_confirm_page_description_en },
},
reset_password_page_title: {
fa: { ...reset_password_page_title_fa },
en: { ...reset_password_page_title_en },
},
reset_password_page_description: {
fa: { ...reset_password_page_description_fa },
en: { ...reset_password_page_description_en },
},
change_password_page_title: {
fa: { ...change_password_page_title_fa },
en: { ...change_password_page_title_en },
},
change_password_page_description: {
fa: { ...change_password_page_description_fa },
en: { ...change_password_page_description_en },
},
search_profiles_page_title: {
fa: { ...search_profiles_page_title_fa },
en: { ...search_profiles_page_title_en },
},
search_profiles_page_description: {
fa: { ...search_profiles_page_description_fa },
en: { ...search_profiles_page_description_en },
},
};
//#endregion
//
//#region Page Configuration ...
//
export enum AccountRoutes {
Login = 'login',
Logout = 'logout',
Default = 'account',
Profile = 'profile',
Register = 'register',
EmailConfirm = 'email_confirm',
PhoneConfirm = 'phone_confirm',
ResetPassword = 'reset_password',
ChangePassword = 'change_password',
SearchProfiles = 'search_profiles',
}
//
export const Account: XSimplePage = {
id: `${AccountRoutes.Default}`,
name: `${AccountRoutes.Default}`,
title: AccountResourceIDs.account_page_title,
description: AccountResourceIDs.account_page_description,
baseRoute: `${AccountRoutes.Default}`,
route: ['/', `${AccountRoutes.Default}`],
icon: XIconNames.collapse,
childs: [
//
//#region Login ...
{
id: `${AccountRoutes.Login}`,
name: `${AccountRoutes.Login}`,
title: AccountResourceIDs.login_page_title,
description: AccountResourceIDs.login_page_description,
baseRoute: `${AccountRoutes.Login}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.Login}`],
icon: XIconNames.login,
},
//#endregion
//
//#region Logout ...
{
id: `${AccountRoutes.Logout}`,
name: `${AccountRoutes.Logout}`,
title: AccountResourceIDs.logout_page_title,
description: AccountResourceIDs.logout_page_description,
baseRoute: `${AccountRoutes.Logout}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.Logout}`],
icon: XIconNames.logout,
},
//#endregion
//
//#region Profile ...
{
id: `${AccountRoutes.Profile}`,
name: `${AccountRoutes.Profile}`,
title: AccountResourceIDs.profile_page_title,
description: AccountResourceIDs.profile_page_description,
baseRoute: `${AccountRoutes.Profile}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.Profile}`],
icon: XIconNames.register,
},
//#endregion
//
//#region Register ...
{
id: `${AccountRoutes.Register}`,
name: `${AccountRoutes.Register}`,
title: AccountResourceIDs.register_page_title,
description: AccountResourceIDs.register_page_description,
baseRoute: `${AccountRoutes.Register}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.Register}`],
icon: XIconNames.register,
},
//#endregion
//
//#region EmailConfirm ...
{
id: `${AccountRoutes.EmailConfirm}`,
name: `${AccountRoutes.EmailConfirm}`,
title: AccountResourceIDs.email_confirm_page_title,
description: AccountResourceIDs.email_confirm_page_description,
baseRoute: `${AccountRoutes.EmailConfirm}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.EmailConfirm}`],
icon: XIconNames.register,
},
//#endregion
//
//#region PhoneConfirm ...
{
id: `${AccountRoutes.PhoneConfirm}`,
name: `${AccountRoutes.PhoneConfirm}`,
title: AccountResourceIDs.phone_confirm_page_title,
description: AccountResourceIDs.phone_confirm_page_description,
baseRoute: `${AccountRoutes.PhoneConfirm}`,
route: ['/', `${AccountRoutes.Default}`, `${AccountRoutes.PhoneConfirm}`],
icon: XIconNames.register,
},
//#endregion
//
//#region ResetPassword ...
{
id: `${AccountRoutes.ResetPassword}`,
name: `${AccountRoutes.ResetPassword}`,
title: AccountResourceIDs.reset_password_page_title,
description: AccountResourceIDs.reset_password_page_description,
baseRoute: `${AccountRoutes.ResetPassword}`,
route: [
'/',
`${AccountRoutes.Default}`,
`${AccountRoutes.ResetPassword}`,
],
icon: XIconNames.register,
},
//#endregion
//
//#region ChangePassword ...
{
id: `${AccountRoutes.ChangePassword}`,
name: `${AccountRoutes.ChangePassword}`,
title: AccountResourceIDs.change_password_page_title,
description: AccountResourceIDs.change_password_page_description,
baseRoute: `${AccountRoutes.ChangePassword}`,
route: [
'/',
`${AccountRoutes.Default}`,
`${AccountRoutes.ChangePassword}`,
],
icon: XIconNames.register,
},
//#endregion
//
//#region SearchProfiles ...
{
id: `${AccountRoutes.SearchProfiles}`,
name: `${AccountRoutes.SearchProfiles}`,
title: AccountResourceIDs.search_profiles_page_title,
description: AccountResourceIDs.search_profiles_page_description,
baseRoute: `${AccountRoutes.SearchProfiles}`,
route: [
'/',
`${AccountRoutes.Default}`,
`${AccountRoutes.SearchProfiles}`,
],
icon: XIconNames.register,
},
//#endregion
],
};
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-avatar\v-avatar.component.html
<section
#vBaseDialogContainer
class="v-base-dialog"
[isForeground]="false"
[xColor]="getValue(backgroundColor) | async"
>
<!-- Title Toolbar -->
<section #vBaseDialogToolbar>
<!-- Toolbar -->
<ion-toolbar
#vToolbar
class="v-base-dialog-toolbar"
[xColor]="getValue(toolbarColor) | async"
>
<!-- Slots -->
<div class="v-base-dialog-toolbar-area">
<x-slotter>
<!-- Start -->
<x-slot
[name]="SlotNames.START"
[cssClass]="['ion-text-start', 'ion-float-start']"
>
<div
matRipple
(click)="handleClosePopup()"
[matTooltip]="getResourceValue(ResourceIDs.back) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="backIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
<!-- Center -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<ion-title class="v-base-dialog-toolbar-item center-vertical-flex">
<h3>{{ getValue(fullName) | async }}</h3>
</ion-title>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.END"
[cssClass]="['ion-text-end', 'ion-float-end']"
>
<div
matRipple
(click)="handleSelectAction()"
*ngIf="
(notEmptyValue(userId) | async) &&
(sharedService.isNotMe(userId) | async) &&
(sharedService.isAuthenticated$ | async)
"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="selectIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
[matTooltip]="getResourceValue(AppResourceIDs.profile) | async"
></x-icon>
</div>
</x-slot>
</x-slotter>
</div>
</ion-toolbar>
<!-- Progressbar -->
<ion-progress-bar
type="indeterminate"
*ngIf="getValue(showProgrssbar) | async"
[color]="getValue(progressbarColor) | async"
></ion-progress-bar>
</section>
<!-- Components -->
<section class="v-base-dialog-content">
<!-- Content -->
<ng-container *ngTemplateOutlet="contentRef"></ng-container>
</section>
</section>
<!-- -->
<!-- Template -->
<!-- -->
<!-- Content Template -->
<ng-template #contentRef>
<img
class="v-avatar-image animated zoomIn center-vertical"
[src]="getValue(avatarUrl) | async"
/>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-avatar\v-avatar.component.scss
@import "../../../../theme/theme.scss";
:host {
--dialog-toolbar-height: 0px;
--background: var(-x-color-light);
}
x-icon {
--size: 24px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 !important;
}
.v-base-dialog {
width: 100%;
height: 100%;
// height: calc(100% - var(--dialog-toolbar-height));
// margin-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-content {
height: 100%;
//
padding-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-toolbar-area {
margin: 8px;
}
.v-base-dialog-toolbar-item {
width: auto;
height: 48px;
}
.v-base-dialog-toolbar-button {
width: 32px;
margin: {
left: 4px;
right: 4px;
}
}
//
// Non Global ...
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-avatar\v-avatar.component.ts
import {
Input,
Component,
ViewChild,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XColorWithBrightness,
} from 'x-framework-core';
import {
VAvatarCssVar,
VAvatarModalContext,
VAvatarReturnAction,
VAvatarModalContextIdentifier,
VAvatarReturnActionIdentifier,
} from './v-avatar.typings';
import { VBaseComponent } from '../../classes/v-base.component';
import { XIconNames, XSlotLayout, XSlotName } from 'x-framework-components';
/**
* a Component to Show User Avatar as Model ...
*/
@Component({
selector: 'v-avatar',
templateUrl: './v-avatar.component.html',
styleUrls: ['./v-avatar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VAvatarComponent extends VBaseComponent {
//
//#region Properties ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
/**
* retrieve Toolbar container ViewChild
* to Calculate Toolbar Height ...
*/
@ViewChild('vBaseDialogToolbar')
vBaseDialogToolbarContainer!: ElementRef;
/**
* Specified Modal Type ...
*/
@Input()
context: VAvatarModalContextIdentifier;
/**
* user Id ...
*/
@Input()
userId: string;
/**
* Specified Avatar Image Url ...
*/
@Input()
avatarUrl: string;
/**
* Specified User's Full Name ...
*/
@Input()
fullName: string;
/**
* Background Color of Component ...
*/
@Input()
backgroundColor: XColorIdentifier = XColorWithBrightness.Light;
/**
* Toolbar Color ...
*/
@Input()
toolbarColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
/**
* Progressbar Color ...
*/
@Input()
progressbarColor: XColorIdentifier = XColorWithBrightness.Warning;
showProgrssbar = false;
/**
* Action Icon Names Specifiecations ...
*/
@Input()
cancelIcon = XIconNames.close;
@Input()
selectIcon = XIconNames.profile;
@Input()
backIcon = XIconNames.arrow_back;
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.applyContentSize();
}
/**
* Register Required Handlers ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region UI Handlers ...
/**
* Use for Handle Modela Closing Action based on Context ...
* @returns
*/
async handleClosePopup(action?: VAvatarReturnActionIdentifier) {
//
if (isNullOrUndefined(this.context) || isNullOrEmptyString(this.context)) {
return;
}
//
switch (this.context) {
//
case VAvatarModalContext.Modal:
await this.managerService.dialogService.modalController.dismiss(action);
break;
//
case VAvatarModalContext.PopOver:
await this.managerService.dialogService.popoverController.dismiss(
action
);
break;
}
}
/**
* When User Clicked on Select Icon ...
*/
async handleSelectAction() {
await this.handleClosePopup(VAvatarReturnAction.OpenProfile);
}
//#endregion
//
//#region Private ...
/**
* Calculate Toolbar Height and add a CSSVar to
* use it for Content Sizing ...
*
* @returns
*/
private async applyContentSize() {
//
const height =
this.vBaseDialogToolbarContainer.nativeElement.getBoundingClientRect()
.height;
if (height === 0) {
setTimeout(() => this.applyContentSize(), 100);
return;
}
//
this.setCssVar(VAvatarCssVar.DialogToolbarHeight, `${height}px`);
this.detectChanges();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-avatar\v-avatar.typings.ts
export enum VAvatarModalContext {
Modal = 'modal',
PopOver = 'pop_over',
}
export type VAvatarModalContextIdentifier = VAvatarModalContext | string;
export enum VAvatarCssVar {
DialogToolbarHeight = 'dialog-toolbar-height',
}
export enum VAvatarReturnAction {
OpenProfile = 'open_profile',
}
export type VAvatarReturnActionIdentifier = VAvatarReturnAction | string;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-friendship\v-friendship.component.html
<div
class="v-friendship"
[xColor]="getValue(color) | async"
[ngClass]="getArrayValue(cssClass) | async"
[isForeground]="getValue(foregroundColor) | async"
>
<!-- Component -->
<section
[xColor]="getValue(color) | async"
[isForeground]="getValue(foregroundColor) | async"
*ngIf="(canShowInfo() | async) && (notNullOrUndefinedValue(model) | async)"
>
<!-- Friendship Info -->
<ng-container>
<!-- Follower/Following(s) -->
<ion-grid>
<ion-row class="ion-align-items-center">
<ion-col size="6" class="ion-text-center">
<h5
class="x-title"
[isForeground]="true"
[xColor]="getValue(labelColor) | async"
>
{{ resourceProvider(AppResourceIDs.followers) }}
</h5>
<h4
class="x-description"
[isForeground]="true"
[xColor]="getValue(valueColor) | async"
>
{{ getFollowersCount() | async }}
</h4>
</ion-col>
<ion-col size="6" class="ion-text-center">
<h5
class="x-title"
[isForeground]="true"
[xColor]="getValue(labelColor) | async"
>
{{ resourceProvider(AppResourceIDs.followings) }}
</h5>
<h4
class="x-description"
[isForeground]="true"
[xColor]="getValue(valueColor) | async"
>
{{ getFollowingsCount() | async }}
</h4>
</ion-col>
</ion-row>
</ion-grid>
<!-- Friendship State -->
<ng-container *ngIf="canShowState() | async">
<dl
*ngIf="isNotMe() | async"
class="v-friendship-state ion-no-margin"
[class.inline]="isNotMobileUi$ | async"
>
<!-- Friendship State -->
<dt
class="x-title"
[isForeground]="true"
[xColor]="getValue(labelColor) | async"
>
{{ resourceProvider(AppResourceIDs.friendship_state) }}
</dt>
<dd
class="x-description"
[isForeground]="true"
[xColor]="getValue(valueColor) | async"
>
{{ getValue(friendshipState) | async }}
</dd>
</dl>
</ng-container>
</ng-container>
</section>
<!-- Friendship Actions -->
<section
[xColor]="getValue(color) | async"
*ngIf="
(canShowActions() | async) &&
(notEmptyValue(actionType) | async) &&
(hasChildValue(friendshipActions) | async)
"
[isForeground]="getValue(foregroundColor) | async"
class="v-friendship-actions-container center-vertical-flex"
>
<x-slotter
[hasCenterSlot]="false"
[layout]="SlotLayouts.HORIZONTAL"
[endBottomSlotCssClass]="'dynamic-slot'"
>
<!-- START -->
<!-- Button/Fab(s) -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
<!-- AsButton -->
<section
[class.mobile]="isMobileUi$ | async"
*ngIf="canShowFriendshipActions() | async"
class="v-friendship-actions ion-text-center"
>
<!-- Loop through Actions -->
<ng-container
*ngFor="let ad of getArrayValue(friendshipActions) | async"
>
<ng-container
*ngTemplateOutlet="
actionButtonRef;
context: {
descriptor: ad,
buttonType:
actionType === FriendshipActionsPresentTypes.AsFAB ||
(actionType ===
FriendshipActionsPresentTypes.AsMenuAndFab &&
(isNotMobileUi$ | async))
? ButtonTypes.Icon
: actionType === FriendshipActionsPresentTypes.AsButton ||
(actionType ===
FriendshipActionsPresentTypes.AsMenuAndButton &&
(isNotMobileUi$ | async))
? ButtonTypes.Raised
: ButtonTypes.Icon
}
"
></ng-container>
</ng-container>
</section>
</x-slot>
<!-- END -->
<!-- Menu -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
<!-- Menu Only Used when -->
<ng-container
*ngIf="
actionType === FriendshipActionsPresentTypes.AsMenu ||
((actionType === FriendshipActionsPresentTypes.AsMenuAndFab ||
actionType === FriendshipActionsPresentTypes.AsMenuAndButton) &&
(isMobileUi$ | async))
"
>
<x-button
[type]="'icon'"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled || loading"
(click)="handleFriendshipActionsMenuClicked($event)"
>
<x-icon
[loading]="loading"
[foregroundColor]="true"
[color]="ColorNames.Dark"
[name]="IconNames.menu_vertical"
[uiDisabled]="uiDisabled || loading"
></x-icon>
</x-button>
</ng-container>
</x-slot>
</x-slotter>
</section>
</div>
<!-- Nothing Template -->
<ng-template #nothingRef></ng-template>
<!-- -->
<!-- Actions Templates -->
<!-- -->
<!-- Button Template -->
<ng-template
#actionButtonRef
let-descriptor="descriptor"
let-buttonType="buttonType"
>
<x-button
[type]="buttonType"
[loading]="loading"
(click)="descriptor.handler()"
class="v-friendship-action-button"
[class.mobile]="isMobileUi$ | async"
[uiDisabled]="uiDisabled || loading"
[iconColor]="getValue(descriptor.color) | async"
[icon]="getValue(descriptor.icon) | async"
[color]="getValue(descriptor.color) | async"
[title]="getResourceValue(descriptor.title) | async"
></x-button>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-friendship\v-friendship.component.scss
@import "../../../../theme/theme.scss";
//
.v-friendship {
width: 100%;
display: inline-block;
}
//
.v-friendship-actions,
.v-friendship-actions-container,
.v-friendship-actions-menu-button {
display: inline-block;
}
//
.v-friendship-state,
.v-friendship-actions-container {
width: 100%;
margin: {
top: 6px;
bottom: 6px;
}
}
//
.v-friendship-state {
text-align: center;
}
//
.v-friendship-action-button {
margin: {
left: 3px;
right: 3px;
}
}
//
.x-title,
.x-description {
display: block;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-friendship\v-friendship.component.ts
import {
Input,
Output,
Component,
EventEmitter,
ChangeDetectionStrategy,
} from '@angular/core';
import {
nameof,
XOneOrManyType,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XColorWithBrightness,
} from 'x-framework-core';
import {
FriendshipAction,
VFriendshipEventModel,
FriendshipActionDescriptor,
FriendshipActionsPresentType,
defaultFriendshipBlockDescriptor,
defaultFriendshipRejectDescriptor,
defaultFriendshipCancelDescriptor,
defaultFriendshipAcceptDescriptor,
defaultFriendshipUnblockDescriptor,
defaultFriendshipUnFollowDescriptor,
FriendshipActionsPresentTypeIdentifier,
defaultFriendshipFollowActionDescriptor,
} from './v-friendship.typings';
import {
XSlotName,
XButtonType,
XSlotLayout,
XPopoverOption,
XPopoverItemOptionSlot,
} from 'x-framework-components';
import {
XFriendshipType,
XFriendshipState,
XFriendshipInfoDto,
} from 'x-framework-identity-sdk';
import { concatMap, filter, map } from 'rxjs/operators';
import { FriendshipPresentType } from './v-friendship.typings';
import { VBaseComponent } from '../../classes/v-base.component';
/**
* Represent Friendship Info ...
*/
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-friendship',
templateUrl: './v-friendship.component.html',
styleUrls: ['./v-friendship.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VFriendshipComponent extends VBaseComponent {
//
//#region Props ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly ButtonTypes = Object.assign({}, XButtonType);
readonly FriendshipActionsPresentTypes = Object.assign(
{},
FriendshipActionsPresentType
);
readonly PresentTypes = Object.assign({}, FriendshipPresentType);
//
friendshipState: string;
friendshipActions: Array<FriendshipActionDescriptor> = [];
//
@Input()
cssClass: XOneOrManyType<string> = [];
//
@Input()
color: XColorIdentifier = XColorWithBrightness.Light;
@Input()
foregroundColor: boolean = false;
@Input()
labelColor: XColorIdentifier = XColorWithBrightness.Primary;
@Input()
valueColor: XColorIdentifier = XColorWithBrightness.Dark;
//
@Input()
showFriendshipState: boolean = true;
//
@Input()
presentType: FriendshipPresentType = FriendshipPresentType.AsProfile;
//
@Input()
actionType: FriendshipActionsPresentTypeIdentifier =
FriendshipActionsPresentType.AsButton;
//
@Input()
model: XFriendshipInfoDto;
//
//#region Events ...
@Output()
actionFired = new EventEmitter<VFriendshipEventModel>();
//#endregion
//#endregion
//
//#region Constructor ...
onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
//#region Model Changed ...
const isModelChanged = changeKeys.includes(
nameof<VFriendshipComponent>('model')
);
const isPresentTypeChanged = changeKeys.includes(
nameof<VFriendshipComponent>('presentType')
);
if (isModelChanged || isPresentTypeChanged) {
this.handlePrepareView();
}
//#endregion
}
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Handlers ...
//#endregion
//
//#region Task Handlers ...
//#endregion
//
//#region UI Providers ...
//
canShowInfo() {
return this.getValue(this.presentType).pipe(
map(
(pt) =>
pt === this.PresentTypes.AsProfile || pt === this.PresentTypes.AsInfo
)
);
}
//
canShowState() {
return this.getValue(this.presentType).pipe(
map(
(pt) =>
pt === this.PresentTypes.AsProfile || pt === this.PresentTypes.AsState
),
map(
(canShow) =>
canShow &&
this.showFriendshipState &&
!isNullOrEmptyString(this.friendshipState)
)
);
}
//
canShowActions() {
return this.getValue(this.presentType).pipe(
map(
(pt) =>
pt === this.PresentTypes.AsProfile ||
pt === this.PresentTypes.AsFullActions ||
pt === this.PresentTypes.AsFollowerActions ||
pt === this.PresentTypes.AsFollowingActions
)
);
}
//
canShowFriendshipActions() {
return this.getValue(this.canShowActions()).pipe(
map(
(canShow) =>
canShow &&
this.actionType !== this.FriendshipActionsPresentTypes.AsMenu
)
);
}
//
canShowFollowerActions() {
return this.getValue(this.presentType).pipe(
map(
(pt) =>
pt === this.PresentTypes.AsProfile ||
pt === this.PresentTypes.AsFullActions ||
pt === this.PresentTypes.AsFollowerActions
)
);
}
//
canShowFollowingActions() {
return this.getValue(this.presentType).pipe(
map(
(pt) =>
pt === this.PresentTypes.AsProfile ||
pt === this.PresentTypes.AsFullActions ||
pt === this.PresentTypes.AsFollowingActions
)
);
}
/**
* Friendship Info not for Current User ...
*/
isNotMe() {
return this.getValue(this.model).pipe(
filter((info) => !!info),
concatMap((info) => this.sharedService.authService.isNotMe(info.userId))
);
}
/**
* get Followers Count in current locale ...
*/
getFollowersCount() {
return this.getValue(this.model).pipe(
map((info) => {
return this.applyLocale(info ? info.followers.toString() : '0');
})
);
}
/**
* get Followings count in current locale ...
*/
getFollowingsCount() {
return this.getValue(this.model).pipe(
map((info) => {
return this.applyLocale(info ? info.followings.toString() : '0');
})
);
}
//#endregion
//
//#region UI Handlers ...
/**
* when user Click on friendship actions menu item ...
*
* @param event
*/
async handleFriendshipActionsMenuClicked(event: any) {
//
// Common Options ...
const onlyIcon = false;
const slot: XPopoverItemOptionSlot = 'start';
//
// Menu Popover Actions ...
const options: XPopoverOption[] = this.friendshipActions.map((fd) => {
return {
slot,
icon: fd.icon,
color: fd.color,
title: fd.title,
id: fd.identifier,
onlyIcon: onlyIcon,
handler: fd.handler,
disabled: this.uiDisabled || this.loading,
} as XPopoverOption;
});
//
if (!this.hasChild(options)) {
return;
}
//
// Present Context Menu ...
await this.popoverService.presentContextMenu(options, event);
}
//#endregion
//
//#region Overrides ...
/**
* Handle Loading Event ...
*
* @param loading
*/
onLoading(loading: boolean): void {
super.onLoading(loading);
//
if (loading) {
this.handleUiState(true);
} else {
this.handleUiState(false);
}
}
/**
* Handle Disabled Event ...
*
* @param isDisabled
*/
onUiDisabled(isDisabled: boolean): void {
super.onUiDisabled(isDisabled);
//
if (isDisabled) {
this.handleUiState(true);
} else {
this.handleUiState(false);
}
}
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Private ...
/**
* do all requirement preparation when a model
* attached to view ...
*/
private handlePrepareView() {
//
// Check Model Exists ...
if (isNullOrUndefined(this.model)) {
return;
}
//
// Generate Friendship State ...
if (this.model.isFollower && this.model.isFollowing) {
this.friendshipState = this.resourceProvider(this.AppResourceIDs.friend);
} else if (this.model.isFollower) {
this.friendshipState = this.resourceProvider(
this.AppResourceIDs.follower
);
} else if (this.model.isFollowing) {
this.friendshipState = this.resourceProvider(
this.AppResourceIDs.following
);
}
//
// Checking Actions ...
const hasActions = [
this.PresentTypes.AsProfile,
this.PresentTypes.AsFullActions,
this.PresentTypes.AsFollowerActions,
this.PresentTypes.AsFollowingActions,
].includes(this.presentType);
const hasFollowerActions =
hasActions &&
[
this.PresentTypes.AsProfile,
this.PresentTypes.AsFullActions,
this.PresentTypes.AsFollowerActions,
].includes(this.presentType);
const hasFollowingActions =
hasActions &&
[
this.PresentTypes.AsProfile,
this.PresentTypes.AsFullActions,
this.PresentTypes.AsFollowingActions,
].includes(this.presentType);
//
// Check Allow Actions Preparation or not ...
if (hasActions) {
//
const followerActions: Array<FriendshipAction> = [];
const followingActions: Array<FriendshipAction> = [];
//
// Check and Prepare Following Actions ...
if (hasFollowingActions) {
//
// Follow Action ...
const canFollow = [
XFriendshipState.None,
XFriendshipState.Rejected,
].includes(this.model.followingState);
if (canFollow) {
followingActions.push(FriendshipAction.Follow);
}
//
// Cancel Action ...
const canCancel = [
XFriendshipState.Pending,
XFriendshipState.Rejected,
].includes(this.model.followingState);
if (canCancel) {
followingActions.push(FriendshipAction.Cancel);
}
//
// UnFollow ...
const canUnfollow =
this.model.followingState === XFriendshipState.Accepted;
if (canUnfollow) {
followingActions.push(FriendshipAction.UnFollow);
}
}
//
// Check and Prepare Follower Actions ...
if (hasFollowerActions) {
//
// Handle Accept Action ...
const canAccept = this.model.followerState === XFriendshipState.Pending;
if (canAccept) {
followerActions.push(FriendshipAction.Accept);
}
//
// Handle Reject Action ...
const canReject = this.model.followerState === XFriendshipState.Pending;
if (canReject) {
followerActions.push(FriendshipAction.Reject);
}
//
// Handle Block Action ...
const canBlock = this.model.followerState === XFriendshipState.Accepted;
if (canBlock) {
followerActions.push(FriendshipAction.Block);
}
//
// Handle UnBlock Action .../
const canUnblock =
this.model.followerState === XFriendshipState.Blocked;
if (canUnblock) {
followerActions.push(FriendshipAction.UnBlock);
}
//
// Handle UnFollow Action ...
const canUnfollow =
this.model.followerState === XFriendshipState.Accepted;
if (canUnfollow) {
followerActions.push(FriendshipAction.UnFollow);
}
}
//
// Check Actions ...
if (this.hasChild(followerActions) || this.hasChild(followingActions)) {
this.prepareActions(followerActions, followingActions);
}
}
}
/**
* Preparing Require Action Descriptors ...
*
* @param follower
* @param following
* @returns
*/
private prepareActions(
follower: Array<FriendshipAction>,
following: Array<FriendshipAction>
) {
//
this.friendshipActions = [];
if (isNullOrUndefined(this.model)) {
this.detectChanges();
return;
}
//
let actions: Array<FriendshipActionDescriptor> = [];
//
// Prepare Follower Actions ...
if (this.hasChild(follower)) {
//
actions = [
...actions,
...follower.map((identifier) =>
this.getActionDescriptor(XFriendshipType.Follower, identifier)
),
];
}
//
// Prepare Following Actions ...
if (this.hasChild(following)) {
//
actions = [
...actions,
...following.map((identifier) =>
this.getActionDescriptor(XFriendshipType.Following, identifier)
),
];
}
//
if (this.hasChild(actions)) {
this.friendshipActions = [...actions];
}
//
this.detectChanges();
}
/**
* Prepare and Retrieve Specified Action Desxcriptor ...
*
* @param type
* @param action
* @returns
*/
private getActionDescriptor(type: XFriendshipType, action: FriendshipAction) {
let result: FriendshipActionDescriptor;
switch (action) {
//
case FriendshipAction.Accept:
result = {
...defaultFriendshipAcceptDescriptor,
};
break;
//
case FriendshipAction.Reject:
result = {
...defaultFriendshipRejectDescriptor,
};
break;
//
case FriendshipAction.Block:
result = {
...defaultFriendshipBlockDescriptor,
};
break;
//
case FriendshipAction.UnBlock:
result = {
...defaultFriendshipUnblockDescriptor,
};
break;
//
case FriendshipAction.Cancel:
result = {
...defaultFriendshipCancelDescriptor,
};
break;
//
case FriendshipAction.Follow:
result = {
...defaultFriendshipFollowActionDescriptor,
};
break;
//
case FriendshipAction.UnFollow:
//
// Here we have to Change Title of
// Default action based on Given Type ...
result = {
...defaultFriendshipUnFollowDescriptor,
//
title:
type === XFriendshipType.Follower
? this.AppResourceIDs.unfollow_follower
: type === XFriendshipType.Following
? this.AppResourceIDs.unfollow_following
: defaultFriendshipFollowActionDescriptor.title,
};
break;
}
//
// Attach Requirements to Default Descriptors ...
//
// Set Friendship Type ...
result.type = type;
//
// Prepare Action Model ...
const actionModel: VFriendshipEventModel = {
type,
action: action,
destUser: this.model.userId,
};
//
// Set Handler to Fire Event ...
result.handler = () => {
this.actionFired.emit(actionModel);
};
//
return result;
}
/**
* handle ui state changed event ...
*
* @param disabled
*/
private async handleUiState(disabled: boolean) {}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-friendship\v-friendship.tools.ts
import {
isNullOrUndefined,
XResourceIdentifier,
isNullOrEmptyString,
hasTaskId,
} from 'x-framework-core';
import {
FriendshipActionDescriptor,
defaultFriendshipBlockDescriptor,
defaultFriendshipCancelDescriptor,
defaultFriendshipAcceptDescriptor,
defaultFriendshipRejectDescriptor,
defaultFriendshipUnblockDescriptor,
defaultFriendshipUnFollowDescriptor,
defaultFriendshipFollowActionDescriptor,
} from './v-friendship.typings';
import {
XFriendDto,
XUserProfileDto,
XFriendshipType,
XFriendshipState,
XFriendshipInfoDto,
} from 'x-framework-identity-sdk';
import { AppResourceIDs } from 'src/app/config/localization.config';
const defaultUserProfileFullName = '...';
const defaultUserAvatar = '/assets/image/default-user-image.png';
const defaultPlaceHolderImage = '/assets/image/img-placeholder.png';
/**
* retrieve a XFriendshipInfoDto instance Object's Friendship state Resource ID ...
*
* @param model
* @returns
*/
export function getFriendshipStateResourceID(model: XFriendshipInfoDto) {
//
let result: XResourceIdentifier = undefined;
//
if (isNullOrUndefined(model)) {
return result;
}
//
if (!!model.isFollower && !!model.isFollowing) {
result = AppResourceIDs.friend;
} else if (!!model.isFollower) {
result = AppResourceIDs.follower;
} else if (!!model.isFollowing) {
result = AppResourceIDs.following;
}
//
return result;
}
/**
* Prepare User Profile Full Name ...
*
* @param model
* @returns
*/
export function getFullName(model: XUserProfileDto) {
//
if (
isNullOrUndefined(model) ||
((isNullOrUndefined(model.firstName) ||
isNullOrEmptyString(model.firstName)) &&
(isNullOrUndefined(model.lastName) ||
isNullOrEmptyString(model.lastName)))
) {
return defaultUserProfileFullName;
}
//
return `${model.firstName} ${model.lastName}`;
}
/**
* Retrieve Specified FriendshipType's Resource ID ...
*
* @param type
* @returns
*/
export function getFriendshipTypeResourceID(type: XFriendshipType) {
//
let result: XResourceIdentifier;
//
// Validate Arg ...
if (isNullOrUndefined(type)) {
return result;
}
//
switch (type) {
//
case XFriendshipType.Follower:
result = AppResourceIDs.follower;
break;
//
case XFriendshipType.Following:
result = AppResourceIDs.following;
break;
}
//
return result;
}
/**
* Retrieve Specified FrindshipState's Resource ID ...
* (also based on FriendshipType if provided)
*
* @param state
* @param type
* @returns
*/
export function getFriendshipStateByTypeResourceID(
state: XFriendshipState,
type?: XFriendshipType
) {
//
let result: XResourceIdentifier;
//
// Validate Arg ...
if (isNullOrUndefined(state)) {
return result;
}
//
switch (state) {
//
case XFriendshipState.None:
result = AppResourceIDs.unknown;
break;
//
case XFriendshipState.Accepted:
//
if (isNullOrUndefined(type)) {
result = AppResourceIDs.accepted;
} else if (type === XFriendshipType.Follower) {
result = AppResourceIDs.follower;
} else if (type === XFriendshipType.Following) {
result = AppResourceIDs.following;
} else {
result = AppResourceIDs.accepted;
}
break;
//
case XFriendshipState.Rejected:
result = AppResourceIDs.rejected;
break;
//
case XFriendshipState.Pending:
//
if (isNullOrUndefined(type)) {
result = AppResourceIDs.pending;
} else if (type === XFriendshipType.Follower) {
result = AppResourceIDs.pending_follower;
} else if (type === XFriendshipType.Following) {
result = AppResourceIDs.pending_following;
} else {
result = AppResourceIDs.pending;
}
break;
//
case XFriendshipState.Blocked:
result = AppResourceIDs.blocked;
break;
}
//
return result;
}
/**
* retrieve a XUserProfileDto instance Object's Friendship state Resource ID ...
*
* @param model
* @returns
*/
export function getProfileFriendshipStateResourceID(model: XUserProfileDto) {
//
let result: XResourceIdentifier = undefined;
//
if (!isNullOrUndefined(model) && !isNullOrUndefined(model.friendshipInfo)) {
result = getFriendshipStateResourceID(model.friendshipInfo);
}
//
return result;
}
/**
* Retrieve Required Actions Descriptors based on FriendDto ...
* these are Default Actions and Must be reviewed ...
*
* @param item
* @returns
*/
export function parseFriendAction(item: XFriendDto) {
//
const result: Array<FriendshipActionDescriptor> = [];
//
// Collect Default Actions Based On Friendship Type ...
if (item.type === XFriendshipType.Follower) {
//
// Accept ...
if (canAcceptFriendshipState(item.state)) {
result.push({ ...defaultFriendshipAcceptDescriptor });
}
//
// Reject ...
if (canRejectFriendshipState(item.state)) {
result.push({ ...defaultFriendshipRejectDescriptor });
}
//
// Block ...
if (canBlockFriendshipState(item.state)) {
result.push({ ...defaultFriendshipBlockDescriptor });
}
//
// Unblock ...
if (canUnblockFriendshipState(item.state)) {
result.push({ ...defaultFriendshipUnblockDescriptor });
}
//
// UnFollow ...
if (canUnFollowFriendshipState(item.state)) {
result.push({ ...defaultFriendshipUnFollowDescriptor });
}
} else if (item.type === XFriendshipType.Following) {
//
// Follow ...
if (canFollowFriendshipState(item.state)) {
result.push({ ...defaultFriendshipFollowActionDescriptor });
}
//
// Cancel ...
if (canCancelFriendshipState(item.state)) {
result.push({ ...defaultFriendshipCancelDescriptor });
}
//
// UnFollow ...
if (canUnFollowFriendshipState(item.state)) {
result.push({ ...defaultFriendshipUnFollowDescriptor });
}
}
//
return result;
}
/**
* get Model Full Name ...
*
* @param model
* @returns
*/
export function getFullNameOfFriend(model: XFriendDto) {
//
let result = '';
//
if (isNullOrUndefined(model)) {
return result;
}
//
result = `${model.firstname} ${model.lastname}`;
//
return result;
}
/**
* Check Can Follow or not ...
*
* @param state
* @returns
*/
export function canFollowFriendshipState(state: XFriendshipState) {
return [XFriendshipState.None, XFriendshipState.Rejected].includes(state);
}
/**
* Check Can Cancel or not ...
*
* @param state
* @returns
*/
export function canCancelFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Pending, XFriendshipState.Rejected].includes(state);
}
/**
* Check Can UnFollow or not ...
*
* @param state
* @returns
*/
export function canUnFollowFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Accepted].includes(state);
}
/**
* Check Can Block or not ...
*
* @param state
* @returns
*/
export function canBlockFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Accepted].includes(state);
}
/**
* Check Can UnBlock or not ...
*
* @param state
* @returns
*/
export function canUnblockFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Blocked].includes(state);
}
/**
* Check Can Accept or not ...
*
* @param state
* @returns
*/
export function canAcceptFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Pending].includes(state);
}
/**
* Check Can Reject or not ...
*
* @param state
* @returns
*/
export function canRejectFriendshipState(state: XFriendshipState) {
return [XFriendshipState.Pending].includes(state);
}
//
//#region Friendship Tasks ...
/**
* All Available Friendship Tasks ...
*/
export enum XFriendshipTasks {
Block = 'block',
Follow = 'follow',
Accept = 'accept',
Reject = 'reject',
Cancel = 'cancel',
UnBlock = 'un_block',
LoadProfile = 'load_profile',
UnFollowFollower = 'un_follow_follower',
UnFollowFollowing = 'un_follow_following',
}
export type XFriendshipTasksIdentifier = XFriendshipTasks | string;
/**
* export an array of all available Friendship Tasks ...
*
* @returns
*/
export function getFriendshipTasks() {
return [
XFriendshipTasks.Block,
XFriendshipTasks.Follow,
XFriendshipTasks.Accept,
XFriendshipTasks.Reject,
XFriendshipTasks.Cancel,
XFriendshipTasks.UnBlock,
XFriendshipTasks.LoadProfile,
XFriendshipTasks.UnFollowFollower,
XFriendshipTasks.UnFollowFollowing,
];
}
/**
* Check a Task Name is Friendship Task or not ...
*
* @param name
* @returns
*/
export function isFriendshipTask(name: XFriendshipTasksIdentifier) {
//
const allTasks = getFriendshipTasks();
const result = allTasks.find(
(t) => name === t || (hasTaskId(name) && name.includes(t))
);
//
return result;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-friendship\v-friendship.typings.ts
import {
XColorIdentifier,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import { XIconNames } from 'x-framework-components';
import { XFriendshipType } from 'x-framework-identity-sdk';
import { AppResourceIDs } from 'src/app/config/localization.config';
/**
* Represent a Frendship Action Descriptor ...
*/
export interface FriendshipActionDescriptor {
icon: string;
payload?: string;
handler?: () => void;
type: XFriendshipType;
color: XColorIdentifier;
title: XResourceIdentifier;
identifier: FriendshipAction;
}
export type FriendshipTypeIdentifier = XFriendshipType | string;
/**
* Represent Friendship Actions ...
*/
export enum FriendshipAction {
Follow = 'follow',
Cancel = 'cancel',
Accept = 'accept',
Reject = 'reject',
Block = 'block',
UnBlock = 'unblock',
UnFollow = 'unfollow',
}
export type FriendshipActionIdentifier = FriendshipAction | string;
/**
* How to Represent Actions in Friendship View ...
*/
export enum FriendshipActionsPresentType {
AsFAB = 'AsFAB',
AsMenu = 'AsMenu',
AsButton = 'AsButton',
AsMenuAndFab = 'AsMenuAndFab',
AsMenuAndButton = 'AsMenuAndButton',
}
export type FriendshipActionsPresentTypeIdentifier =
| FriendshipActionsPresentType
| string;
/**
* Action Event Model ...
*/
export interface VFriendshipEventModel {
destUser: string;
type: FriendshipTypeIdentifier;
action: FriendshipActionIdentifier;
}
/**
* All Available Present Types for Friendship View ...
*/
export enum FriendshipPresentType {
/**
* only shown follwer/following(s) count ...
*/
AsInfo = 'as_info',
/**
* represent current friendship state ...
*/
AsState = 'as_state',
/**
* contains all available actions ...
*/
AsProfile = 'as_profile',
/**
* contains only actions ...
*/
AsFullActions = 'as_full_actions',
/**
* contains only follower actions ...
*/
AsFollowerActions = 'as_follower_actions',
/**
* contains only following actions ...
*/
AsFollowingActions = 'as_following_actions',
}
export type FriendshipPresentTypeIdentifier = FriendshipPresentType | string;
//
// Here we implemenmt all required Default Action Descriptors ...
/**
* Default Follow Action ...
*/
export const defaultFriendshipFollowActionDescriptor: FriendshipActionDescriptor =
{
title: AppResourceIDs.follow,
type: XFriendshipType.Following,
icon: XIconNames.friendship_reuest,
identifier: FriendshipAction.Follow,
color: XColorWithBrightness.PrimaryShade,
};
/**
* Default Cancel Action ...
*/
export const defaultFriendshipCancelDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.cancel_request,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_cancel,
identifier: FriendshipAction.Cancel,
color: XColorWithBrightness.WarningShade,
};
/**
* Default UnFollow Action ...
*/
export const defaultFriendshipUnFollowDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.unfollow,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_unfollow,
identifier: FriendshipAction.UnFollow,
color: XColorWithBrightness.DangerShade,
};
/**
* Default Accept Action ...
*/
export const defaultFriendshipAcceptDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.accept_request,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_accept,
identifier: FriendshipAction.Accept,
color: XColorWithBrightness.SuccessShade,
};
/**
* Default Reject Action ...
*/
export const defaultFriendshipRejectDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.cancel_request,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_reject,
identifier: FriendshipAction.Reject,
color: XColorWithBrightness.DangerShade,
};
/**
* Default Block Action ...
*/
export const defaultFriendshipBlockDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.block,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_block,
identifier: FriendshipAction.Block,
color: XColorWithBrightness.DangerShade,
};
/**
* Default Unblock Action ...
*/
export const defaultFriendshipUnblockDescriptor: FriendshipActionDescriptor = {
title: AppResourceIDs.unblock,
type: XFriendshipType.Follower,
icon: XIconNames.friendship_unblock,
identifier: FriendshipAction.UnBlock,
color: XColorWithBrightness.PrimaryShade,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-login\v-login.component.html
<v-dto [loading]="loading" [uiDisabled]="uiDisabled" #dtoPresenter [dtoContainer]="this"></v-dto>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-login\v-login.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-login\v-login.component.ts
import {
Output,
Component,
EventEmitter,
ChangeDetectionStrategy,
} from '@angular/core';
import {
getFieldNames,
XStandardType,
XExceptionIDs,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XFormControlType,
XFormControlConfig,
XFormControlAppearance,
} from 'x-framework-components';
import { Validators } from '@angular/forms';
import { XLoginRequestDto } from 'x-framework-identity-sdk';
import {
VBaseDtoComponent,
VBaseDtoModelAction,
VBaseDtoModelFiredActionModel,
VBaseDtoPresentType,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { Subject } from 'rxjs';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-login',
templateUrl: './v-login.component.html',
styleUrls: ['./v-login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VLoginComponent extends VBaseDtoComponent<XLoginRequestDto> {
//
//#region Props ...
//
@Output()
login: EventEmitter<void> = new EventEmitter<void>();
//
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
readonly title = this.resourceProvider(this.AppResourceIDs.login_page_title);
//
isInPage = true;
wrapWithCard = true;
showFieldsForm = false;
cardShowActions = true;
passwordVisible = false;
showValidationErrors = false;
showFormCancelButton = false;
presentType = VBaseDtoPresentType.AsForm;
cardTitle = this.AppResourceIDs.login_page_title;
formActionButtonAddResource = this.AppResourceIDs.login;
formActionButtonUpdateResource = this.AppResourceIDs.login;
//
defaultModel: XLoginRequestDto = {
userSelectBy: '',
password: '',
device: undefined,
language: undefined,
};
readonly FieldNames = getFieldNames(this.defaultModel);
/**
* Describe Model ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// UserSelect By ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.userSelectBy,
type: this.ModelFieldTypes.String,
value: this.defaultModel.userSelectBy,
label: this.AppResourceIDs.user_identifier,
formControlConfig: {
index: 0,
propName: this.FieldNames.userSelectBy,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
focusHandler: new Subject(),
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
// label: this.AppResourceIDs.user_identifier,
appearance: this.FormControlAppearances.Outline,
placeholder: this.AppResourceIDs.user_identifier,
tooltip: this.AppResourceIDs.user_identifier_description,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.user,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
//
// Password ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.password,
value: this.defaultModel.password,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.password,
formControlConfig: {
index: 1,
propName: this.FieldNames.password,
type: {
type: this.FormControlTypes.Password,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
tooltip: this.AppResourceIDs.password,
// label: this.AppResourceIDs.password,
placeholder: this.AppResourceIDs.password,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.verified,
},
suffix: {
handler: () => {
this.togglePasswordVisible();
},
applyStateColor: false,
color: this.ColorNames.PrimaryShade,
name: this.passwordVisible
? this.IconNames.visible_off
: this.IconNames.visible,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
];
//#endregion
//
//#region LifeCycles ...
registerViewHandlers(): void {
super.registerViewHandlers();
//
// Handle Fire Login Event ...
this.applyTakeUntilWrapper(
this.dtoModelActionFired.asObservable()
).subscribe((action: VBaseDtoModelFiredActionModel) => {
//
// Handle Login Action ...
if (action.action === VBaseDtoModelAction.AddOrUpdate) {
//
if (this.lock) {
this.lock = false;
}
//
this.login.emit();
}
});
}
//#endregion
//
//#region Abstract ...
/**
* Validate Model ...
*
* @param model
* @returns
*/
validate(
model?: Partial<XLoginRequestDto>
): VModelValidationResult<XLoginRequestDto> {
//
let result: VModelValidationResult<XLoginRequestDto>;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validations ...
//
// Model Not Null or Undefined ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs)
);
} else {
//
// Check Other Validations ...
//
// User Select By ...
isValid = !isNullOrEmptyString(normalized.userSelectBy);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidUserSelector)
);
}
//
// Password ...
isValid = !isNullOrEmptyString(normalized.password);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidPassword)
);
}
}
//
isValid = !this.hasChild(validationErrors);
//
// Prepare Result ...
result = {
isValid,
normalized,
validationErrors,
isValidForAdd: true,
isValidForUpdate: true,
};
//
return result;
}
/**
* Prepare Model for Actions ...
*
* @param model XLoginRequestDto Model ...
* @returns
*/
prepare(model?: XLoginRequestDto): XLoginRequestDto {
//
if (isNullOrUndefined(model)) {
model = {
...this.defaultModel,
};
}
//
// Preparing Model Filesd ...
model = {
userSelectBy: !isNullOrEmptyString(model.userSelectBy)
? model.userSelectBy
: this.defaultModel.userSelectBy,
password: !isNullOrEmptyString(model.password)
? model.password
: this.defaultModel.password,
device: !isNullOrUndefined(model.device)
? model.device
: this.managerService.settingsService.device,
language: !isNullOrEmptyString(model.language)
? model.language
: this.managerService.localizationService.locale,
};
//
return model;
}
/**
* Handle Register Focus Handler for User Select By ...
*
* @param to
* @param from
*/
onPresentTypeChanged(to: string, from: string): void {
//
if (to === this.PresentTypes.AsForm) {
//
// Make Sure runs when Model Form Config Prepared ...
if (isNullOrUndefined(this.modelFormConfig)) {
//
setTimeout(() => this.onPresentTypeChanged(to, from), 100);
return;
}
//
this.registerFocusHandler(
this.modelFormConfig.name,
this.FieldNames.userSelectBy
);
}
}
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region Overrides ...
//#endregion
//
//#region Private ...
private async togglePasswordVisible() {
//
// Toggle Password Visibility ...
this.passwordVisible = !this.passwordVisible;
//
// Apply Changes on Password based on Flag ...
this.defaultFieldDescriptors[1] = {
...this.fieldDescriptors[1],
formControlConfig: {
...this.fieldDescriptors[1].formControlConfig,
type: {
...this.fieldDescriptors[1].formControlConfig.type,
type: this.passwordVisible
? this.FormControlTypes.Text
: this.FormControlTypes.Password,
},
appearance: {
...this.fieldDescriptors[1].formControlConfig.appearance,
icons: {
...this.fieldDescriptors[1].formControlConfig.appearance.icons,
suffix: {
...this.fieldDescriptors[1].formControlConfig.appearance.icons
.suffix,
applyStateColor: false,
color: this.ColorNames.PrimaryShade,
name: this.passwordVisible
? this.IconNames.visible_off
: this.IconNames.visible,
},
},
},
},
};
//
// Reset From Model Config to Apply Changes ...
this.modelFormConfig = undefined;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-profile\v-profile.component.html
<section
#vBaseDialogContainer
class="v-base-dialog"
[isForeground]="false"
[xColor]="getValue(backgroundColor) | async"
>
<!-- Title Toolbar -->
<section #vBaseDialogToolbar>
<!-- Toolbar -->
<ion-toolbar
#vToolbar
class="v-base-dialog-toolbar"
[xColor]="getValue(toolbarColor) | async"
>
<!-- Slots -->
<div class="v-base-dialog-toolbar-area">
<x-slotter>
<!-- Start -->
<x-slot
[name]="SlotNames.START"
[cssClass]="['ion-text-start', 'ion-float-start']"
>
<div
matRipple
(click)="handleClosePopup()"
[matTooltip]="getResourceValue(ResourceIDs.back) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="backIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
<!-- Center -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<ion-title class="v-base-dialog-toolbar-item center-vertical-flex">
<h3>{{ getResourceValue(AppResourceIDs.profile) | async }}</h3>
</ion-title>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.END"
[cssClass]="['ion-text-end', 'ion-float-end']"
>
<div
matRipple
*ngIf="false"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="selectIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
[matTooltip]="getResourceValue(AppResourceIDs.profile) | async"
></x-icon>
</div>
</x-slot>
</x-slotter>
</div>
</ion-toolbar>
<!-- Progressbar -->
<ion-progress-bar
type="indeterminate"
*ngIf="getValue(showProgrssbar) | async"
[color]="getValue(progressbarColor) | async"
></ion-progress-bar>
</section>
<!-- Components -->
<section class="v-base-dialog-content">
<!-- Content -->
<ng-container *ngTemplateOutlet="contentRef"></ng-container>
</section>
</section>
<!-- -->
<!-- Template -->
<!-- -->
<!-- Content Template -->
<ng-template #contentRef>
<!-- Implement User View -->
<ng-container *ngIf="notNullOrUndefinedValue(profile) | async">
<div class="v-user-profile-container">
<v-user
[model]="profile"
[supportEdit]="false"
[presentType]="UserPresentTypes.AsProfile"
></v-user>
</div>
</ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-profile\v-profile.component.scss
@import "../../../../theme/theme.scss";
:host {
--dialog-toolbar-height: 0px;
--background: var(-x-color-light);
}
x-icon {
--size: 24px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 !important;
}
.v-base-dialog {
width: 100%;
height: 100%;
// height: calc(100% - var(--dialog-toolbar-height));
// margin-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-content {
height: 100%;
//
padding-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-toolbar-area {
margin: 8px;
}
.v-base-dialog-toolbar-item {
width: auto;
height: 48px;
}
.v-base-dialog-toolbar-button {
width: 32px;
margin: {
left: 4px;
right: 4px;
}
}
//
// Non Global ...
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-profile\v-profile.component.ts
import {
Input,
Component,
ViewChild,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XTask,
nameof,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XColorWithBrightness,
} from 'x-framework-core';
import {
VProfileCssVar,
VProfileModalContext,
VProfileComponentTasks,
VProfileModalContextIdentifier,
} from './v-profile.typings';
import { XUserProfileDto } from 'x-framework-identity-sdk';
import { VUserPresentType } from '../v-user/v-user.typings';
import { VBaseComponent } from '../../classes/v-base.component';
import { XIconNames, XSlotLayout, XSlotName } from 'x-framework-components';
/**
* a Component For Use to Show User Profile as Model ...
*/
@Component({
selector: 'v-profile',
templateUrl: './v-profile.component.html',
styleUrls: ['./v-profile.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VProfileComponent extends VBaseComponent {
//
//#region Properties ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly UserPresentTypes = Object.assign({}, VUserPresentType);
/**
* retrieve Toolbar container ViewChild
* to Calculate Toolbar Height ...
*/
@ViewChild('vBaseDialogToolbar')
vBaseDialogToolbarContainer!: ElementRef;
/**
* Specified Modal Type ...
*/
@Input()
context: VProfileModalContextIdentifier;
@Input()
userId: string;
/**
* Specified User's Full Name ...
*/
@Input()
fullName: string;
/**
* User Profile Info ...
*/
profile: XUserProfileDto;
/**
* Background Color of Component ...
*/
@Input()
backgroundColor: XColorIdentifier = XColorWithBrightness.Light;
/**
* Toolbar Color ...
*/
@Input()
toolbarColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
/**
* Progressbar Color ...
*/
@Input()
progressbarColor: XColorIdentifier = XColorWithBrightness.Warning;
showProgrssbar = false;
/**
* Action Icon Names Specifiecations ...
*/
@Input()
cancelIcon = XIconNames.close;
@Input()
selectIcon = XIconNames.profile;
@Input()
backIcon = XIconNames.arrow_back;
//#endregion
//
//#region LifeCycles ...
async onInit() {
super.onInit();
//
if (!isNullOrEmptyString(this.userId)) {
await this.issueGetProfileTask();
}
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
const isUserIdChanged = changeKeys.includes(
nameof<VProfileComponent>('userId')
);
if (isUserIdChanged && !isNullOrEmptyString(this.userId)) {
await this.issueGetProfileTask();
}
}
afterViewInit(): void {
super.afterViewInit();
//
this.applyContentSize();
}
/**
* Register Required Handlers ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region Task Handlers ...
async onTaskStart(name: string, task: XTask<any>): Promise<void> {
super.onTaskStart(name, task);
//
if (name === VProfileComponentTasks.GetProfile) {
//
this.showProgrssbar = true;
await this.setLoadingState(true);
}
}
async onTaskFinished(name: string, task: XTask<any>): Promise<void> {
super.onTaskFinished(name, task);
//
if (name === VProfileComponentTasks.GetProfile) {
//
this.showProgrssbar = false;
await this.setLoadingState(false);
}
//
if (name === VProfileComponentTasks.GetProfile) {
this.handleGetProfileTaskFinished(task.result);
}
}
async onTaskFailed(name: string, task: XTask<any>): Promise<void> {
super.onTaskFailed(name, task);
//
if (name === VProfileComponentTasks.GetProfile) {
//
this.showProgrssbar = false;
await this.setLoadingState(false);
}
}
//#endregion
//
//#region UI Handlers ...
/**
* Use for Handle Modela Closing Action based on Context ...
* @returns
*/
async handleClosePopup() {
//
if (isNullOrUndefined(this.context) || isNullOrEmptyString(this.context)) {
return;
}
//
switch (this.context) {
//
case VProfileModalContext.Modal:
await this.managerService.dialogService.modalController.dismiss();
break;
//
case VProfileModalContext.PopOver:
await this.managerService.dialogService.popoverController.dismiss();
break;
}
}
//#endregion
//
//#region Overrides ...
onLoading(loading: boolean): void {
super.onLoading(loading);
//
this.showProgrssbar = loading;
this.detectChanges();
}
//#endregion
//
//#region Private ...
/**
* issue task to get user profile ...
*/
private async issueGetProfileTask() {
//
if (isNullOrEmptyString(this.userId)) {
return;
}
//
const mTaskName = VProfileComponentTasks.GetProfile;
const mTask = this.sharedService.authService.getProfile(this.userId);
//
await this.addOrUpdateTask({
priority: 0,
affectLoading: true,
name: mTaskName,
task: mTask,
});
//
await this.dispatchTask(mTaskName);
}
/**
* Handle User Profiile Recieived ...
*
* @param profile
*/
private handleGetProfileTaskFinished(profile: XUserProfileDto) {
//
this.profile = {
...profile,
};
//
this.detectChanges();
}
/**
* Calculate Toolbar Height and add a CSSVar to
* use it for Content Sizing ...
*
* @returns
*/
private async applyContentSize() {
//
const height =
this.vBaseDialogToolbarContainer.nativeElement.getBoundingClientRect()
.height;
if (height === 0) {
setTimeout(() => this.applyContentSize(), 100);
return;
}
//
this.setCssVar(VProfileCssVar.DialogToolbarHeight, `${height}px`);
this.detectChanges();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-profile\v-profile.typings.ts
export enum VProfileModalContext {
Modal = 'modal',
PopOver = 'pop_over',
}
export type VProfileModalContextIdentifier = VProfileModalContext | string;
export enum VProfileCssVar {
DialogToolbarHeight = 'dialog-toolbar-height',
}
export enum VProfileComponentTasks {
GetProfile = 'egt_profile',
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-register\v-register.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
class="v-card v-dto-card"
[avatarUrl]="getCardAvatar()"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
[class.v-no-margin-card]="getValue(removeCardMargin) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
class="v-dto v-component-wrapper"
[ngClass]="getArrayValue(cssClass) | async"
>
<!-- Component Data -->
<ng-container *ngIf="getValue(step) | async as rStep">
<!-- Present Registration Steps Template -->
<ng-container
*ngTemplateOutlet="
rStep === RegistrationSteps.Terms
? termsAndConditionsStepRef
: unknownStepRef
"
></ng-container>
</ng-container>
</section>
</ng-template>
<!-- -->
<!-- Present Types Templates -->
<!-- -->
<!-- Unknow Template -->
<ng-template #unknownStepRef> </ng-template>
<!-- Terms and Conditions Template -->
<ng-template #termsAndConditionsStepRef> Terms and Conditions ... </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-register\v-register.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-register\v-register.component.ts
import {
XTask,
isFunction,
XTaskPriority,
nameofInstance,
XOneOrManyType,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
RegistrationKeys,
RegistrationTaks,
VRegistrationSteps,
isContainsRegistrationTask,
} from './v-register.typings';
import {
XActionRequestDto,
XActionResponseDto,
} from 'x-framework-identity-sdk';
import { Observable } from 'rxjs';
import { Component, Input } from '@angular/core';
import { getRegistrationStepResourceID } from './v-register.tools';
import { VBaseInCardComponent } from '../../classes/v-base.component';
@Component({
selector: 'v-register',
templateUrl: './v-register.component.html',
styleUrls: ['./v-register.component.scss'],
})
export class VRegisterComponent extends VBaseInCardComponent {
//
//#region Props ...
readonly RegistrationSteps = Object.assign({}, VRegistrationSteps);
readonly RegistrationTaskNames = Object.assign({}, RegistrationTaks);
/**
* Custom CSS Classes ...
*/
cssClass: XOneOrManyType<string> = [];
/**
* Remove Card Marigin ...
*/
removeCardMargin: boolean = false;
//
@Input()
getTermsAndConditionsTask: (lang: string) => Observable<string> = undefined;
//
step: VRegistrationSteps;
actionRequest: XActionRequestDto;
actionResponse: XActionResponseDto;
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.loadStepInfo();
}
async onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanged = false;
//
// getTermsAndConditionsTask ...
isChanged = changeKeys.includes(
nameofInstance(this, 'getTermsAndConditionsTask'),
);
if (isChanged) {
//
let isValid =
!isNullOrUndefined(this.getTermsAndConditionsTask) &&
isFunction(this.getTermsAndConditionsTask);
if (!isValid) {
//
this.getTermsAndConditionsTask = (lang: string) => {
return this.sharedService.saherElmAPIService.terms.termsProviderService.get(
lang,
);
};
}
}
//
if (applyChanged) {
this.detectChanges();
}
}
//#endregion
//
//#region Task Handlers ...
/**
* Handle Task Start ...
*
* @param name
* @param task
*/
async onTaskStart(name: string, task: XTask<any>) {
super.onTaskStart(name, task);
//
// Validate Task Name ...
let isValid = isContainsRegistrationTask(name);
if (!isValid) {
return;
}
//
await this.setLoadingState(true);
}
/**
* Handle Task Failed ...
*
* @param name
* @param task
*/
async onTaskFailed(name: string, task: XTask<any>) {
super.onTaskFailed(name, task);
//
// Validate Task Name ...
let isValid = isContainsRegistrationTask(name);
if (!isValid) {
return;
}
//
await this.setLoadingState(false);
this.showError(task.error);
}
/**
* Handle Task Finished ...
*
* @param name
* @param task
*/
async onTaskFinished(name: string, task: XTask<any>) {
super.onTaskFinished(name, task);
//
// Validate Task Name ...
let isValid = isContainsRegistrationTask(name);
if (!isValid) {
return;
}
//
await this.setLoadingState(false);
//
if (name === this.RegistrationTaskNames.GetTermsAndConditions) {
//
// this.showSuccess();
this.handleTermsAndConditionsTaskFinished(task.result);
}
}
//#endregion
//
//#region Providers ...
loadStepInfo() {
//
// Read Registration Step ...
if (isNullOrUndefined(this.step)) {
//
this.step =
this.managerService.secureStorageService.readOfType<VRegistrationSteps>(
RegistrationKeys.Step,
false,
);
//
// Set Terms as First Step ...
if (
isNullOrUndefined(this.step) ||
this.step == VRegistrationSteps.UnKnown
) {
this.step = VRegistrationSteps.Terms;
}
}
//
// Read Action Request ...
if (isNullOrUndefined(this.actionRequest)) {
//
this.actionRequest =
this.managerService.secureStorageService.readOfType<XActionRequestDto>(
RegistrationKeys.ActionRequest,
false,
);
}
//
// Read Action Response ...
if (isNullOrUndefined(this.actionResponse)) {
//
this.actionResponse =
this.managerService.secureStorageService.readOfType<XActionResponseDto>(
RegistrationKeys.ActionResponse,
false,
);
}
//
// Reload Card Sub Title ...
if (!isNullOrUndefined(this.step)) {
//
let stepResourceID = getRegistrationStepResourceID(this.step);
if (!isNullOrUndefined(stepResourceID)) {
this.cardSubTitle = this.resourceProvider(stepResourceID);
}
}
//
// Prepare Current Step ...
this.prepareStep();
//
// Seva Step Info ...
this.saveStepInfo();
}
getCardAvatar() {
//
let result: string = undefined;
//
if (
!isNullOrUndefined(this.actionRequest) &&
!isNullOrUndefined(this.actionRequest.avatar)
) {
//
return this.actionRequest.avatar.name;
}
//
return result;
}
/**
* Preparing Registration Step Info ...
*
* @returns
*/
prepareStep() {
//
if (
isNullOrUndefined(this.step) ||
this.step !== this.RegistrationSteps.Terms ||
!isFunction(this.getTermsAndConditionsTask) ||
isNullOrUndefined(this.getTermsAndConditionsTask)
) {
return;
}
//
let isPassed = false;
//
// Required Preparation Based on Step ...
if (this.step === this.RegistrationSteps.Terms) {
//
// Check Terms and Condiions Must read or not ...
let hasTerms =
!isNullOrUndefined(this.actionRequest) &&
isNullOrEmptyString(this.actionRequest.termsAndConditions);
isPassed = hasTerms && !!this.actionRequest.acceptTerms;
//
if (!isPassed) {
//
// Read Terms and Conditions and Attach it to ActionRequest if not Exists ...
if (!hasTerms) {
this.handleReadTermsAndConditions();
}
} else {
}
//
return;
}
}
//#endregion
//
//#region Handlers ...
handleReadTermsAndConditions() {
//
// Validate ...
let isValid =
isNullOrUndefined(this.actionRequest) ||
isNullOrEmptyString(this.actionRequest.termsAndConditions);
if (!isValid) {
return;
}
//
this.issueGetTermsAndConditionsTask();
}
//#endregion
//
//#region Private ...
private saveStepInfo() {
//
// Step ...
this.managerService.secureStorageService.write(
RegistrationKeys.Step,
this.step,
true,
);
//
// Action Request ...
this.managerService.secureStorageService.write(
RegistrationKeys.ActionRequest,
this.actionRequest,
true,
);
//
// Action Response ...
this.managerService.secureStorageService.write(
RegistrationKeys.ActionResponse,
this.actionResponse,
true,
);
}
private async issueGetTermsAndConditionsTask() {
//
const name = this.RegistrationTaskNames.GetTermsAndConditions;
//
let lang = '';
if (
!isNullOrUndefined(this.actionRequest) &&
!isNullOrEmptyString(this.actionRequest.lang)
) {
lang = this.actionRequest.lang;
} else {
lang = this.managerService.currentLocale;
}
if (isNullOrEmptyString(lang)) {
return;
}
//
const task = this.getTermsAndConditionsTask(lang);
if (isNullOrUndefined(task)) {
return;
}
//
// Add Or Update Task ...
await this.addOrUpdateTask({
name,
task,
affectLoading: true,
priority: XTaskPriority.High,
});
//
// Dispatch Task ...
await this.dispatchTask(name);
}
private handleTermsAndConditionsTaskFinished(result: string) {
console.log('recieved terms: ', result);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-register\v-register.tools.ts
import { VRegistrationSteps } from './v-register.typings';
import { AccountLocalizationResourceIDs } from '../account.configs';
import { isNullOrUndefined, XResourceIdentifier } from 'x-framework-core';
export function getRegistrationStepResourceID(step: VRegistrationSteps) {
//
let result: XResourceIdentifier = undefined;
//
if (!isNullOrUndefined(step)) {
//
switch (step) {
//
case VRegistrationSteps.Terms:
result = AccountLocalizationResourceIDs.accept_terms;
break;
//
case VRegistrationSteps.EmailConfirmation:
result = AccountLocalizationResourceIDs.email_confirmation;
break;
//
case VRegistrationSteps.UserInfo:
result = AccountLocalizationResourceIDs.input_user_info;
break;
//
case VRegistrationSteps.AttachProfileImage:
result = AccountLocalizationResourceIDs.input_user_avatar;
break;
//
case VRegistrationSteps.FinishRegistration:
result = AccountLocalizationResourceIDs.finish_registration;
break;
}
}
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-register\v-register.typings.ts
import { isValueInEnum } from '../../tools/v-base.tools';
import { hasChild, isNullOrUndefined } from 'x-framework-core';
/**
* All Steps Which has Passed for Registration ...
*/
export enum VRegistrationSteps {
/**
* Unknown ...
*/
UnKnown = 0,
/**
* User Must Accept Terms and Conditions ...
*/
Terms = 1,
/**
* User Confirm Email Address ...
*/
EmailConfirmation = 2,
/**
* User Input Info ...
*/
UserInfo = 3,
/**
* User add Profile Image ...
*/
AttachProfileImage = 4,
/**
* Finish User Registration ...
*/
FinishRegistration = 5,
}
/**
* Registration Keys ...
*/
export enum RegistrationKeys {
Step = 'RStep',
ActionRequest = 'RAReq',
ActionResponse = 'RARes',
}
//
//#region Tasks ...
export enum RegistrationTaks {
GetTermsAndConditions = 'Get_Terms_And_Conditions',
}
export type RegistrationTaksIdentifier = RegistrationTaks | string;
export function isRegistrationTaks(value: string) {
return isValueInEnum(value, Object.assign({}, RegistrationTaks));
}
export function isContainsRegistrationTask(value: string) {
//
let taskNames = [RegistrationTaks.GetTermsAndConditions];
//
let result =
hasChild(taskNames) &&
!isNullOrUndefined(taskNames.find((tn) => value.includes(tn)));
//
return result;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-terms\v-terms.component.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-terms\v-terms.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-terms\v-terms.component.ts
import { Input } from '@angular/core';
import {
isNullOrEmptyString,
nameofInstance,
XLocale,
XOneOrManyType,
} from 'x-framework-core';
import { VBaseInCardComponent } from '../../classes/v-base.component';
import {
isVTermsPresentType,
VTermsPresentType,
VTermsPresentTypeIdentifier,
} from './v-terms.typings';
export class VTermsComponent extends VBaseInCardComponent {
//
//#region Props ...
readonly PresentTypes = Object.assign({}, VTermsPresentType);
/**
* Css Class List ...
*/
@Input()
cssClass: XOneOrManyType<string>;
/**
* Present Type ...
*/
@Input()
presentType: VTermsPresentTypeIdentifier = this.PresentTypes.AsText;
/**
* Specified Terms and Conditions Language ...
*/
@Input()
language: XLocale | string;
@Input()
terms: string;
//
lang = '';
termsAndConditions = '';
//#endregion
//
//#region Life Cycles ...
async afterViewInit() {
super.afterContentInit();
//
await this.handlePreparePresentType();
}
async onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (!isVTermsPresentType(this.presentType)) {
//
applyChanges = true;
this.presentType = this.PresentTypes.AsText;
}
}
//
// Language ...
isChanged = changeKeys.includes(nameofInstance(this, 'language'));
if (isChanged) {
//
if (isNullOrEmptyString(this.language)) {
//
applyChanges = true;
this.language = this.managerService.currentLocale;
}
}
//
// Prepare Present Type ...
isChanged =
changeKeys.includes(nameofInstance(this, 'terms')) ||
changeKeys.includes(nameofInstance(this, 'language')) ||
changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
await this.handlePreparePresentType();
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region Providers ...
//#endregion
//
//#region Privates ...
private async handlePreparePresentType() {}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-terms\v-terms.typings.ts
import { isValueInEnum } from '../../tools/v-base.tools';
//
//#region PresentType ...
export enum VTermsPresentType {
AsNone = 'as_none',
AsText = 'as_text',
AsForm = 'as_form',
AsMarkdown = 'as_markdown',
AsTermsManagement = 'as_terms_management',
}
export type VTermsPresentTypeIdentifier = VTermsPresentType | string;
export function isVTermsPresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VTermsPresentType));
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-user\v-user.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[actionProvider]="cardActionProvider"
class="v-card v-dto-card v-user-card"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[avatarUrl]="getValue(cardAvatarUrl) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
[class.v-no-margin-card]="getValue(removeCardMargin) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
*ngIf="getValue(putUserInfoOnCardHeader) | async"
>
<!-- Actions -->
<section class="v-user-friendship-actions">
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
cssClass: 'v-user-friendship-container',
actionType: FriendshipActionsPresentTypes.AsMenu,
presentType: FriendshipPresentTypes.AsFollowingActions
}
"
></ng-container>
</section>
</div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
matRipple
(click)="handleUserSelected()"
class="v-user v-dto v-component-wrapper"
[ngClass]="getArrayValue(cssClass) | async"
[matRippleDisabled]="
!(getValue(canSelectUser) | async) &&
!((getValue(loading) | async) || (getValue(uiDisabled) | async))
"
>
<!-- Component Data -->
<ng-container *ngIf="getValue(presentType) | async as prType">
<ng-container
*ngTemplateOutlet="
prType === VPresentTypes.AsNone
? nothingPresentTypeRef
: prType === VPresentTypes.AsForm
? formPresentTypeRef
: prType === VPresentTypes.AsInfo
? infoPresentTypeRef
: prType === VPresentTypes.AsProfile
? profilePresentTypeRef
: prType === VPresentTypes.AsAvatar
? avatarPresentTypeRef
: prType === VPresentTypes.AsFollower
? followerPresentTypeRef
: prType === VPresentTypes.AsFollowing
? followingPresentTypeRef
: prType === VPresentTypes.AsFollowerActions
? followerActionsPresentTypeRef
: prType === VPresentTypes.AsFollowingActions
? followingActionsPresentTypeRef
: nothingPresentTypeRef
"
></ng-container>
</ng-container>
<!-- Component Validation Erros -->
<section
class="v-dto-validation-errors"
*ngIf="
(notNullOrUndefinedValue(modelState) | async) &&
hasChildValue((getValue(modelState) | async).errors) | async
"
>
<ng-container *ngTemplateOutlet="dtoValidationErrorsRef"></ng-container>
</section>
<!-- Component Actions -->
<section
class="v-dto-actions"
*ngIf="
(canShowDtoActions() | async) ||
(getValue(presentType) | async) === PresentTypes.AsForm
"
>
<ng-container *ngTemplateOutlet="dtoActionsRef"></ng-container>
</section>
</section>
</ng-template>
<!-- -->
<!-- Present Types Templates -->
<!-- -->
<!-- Avatar Present Type -->
<!-- Only Shows User Avatar -->
<ng-template #avatarPresentTypeRef>
<ng-container
*ngTemplateOutlet="
userInfoRef;
context: {
containsAvatar: true,
containsFullName: false,
avatarSlot: avatarSlot,
fullNameSlot: fullNameSlot,
containsFriendshipActions: false,
friendshipSlot: friendshipActionsSlot
}
"
></ng-container>
</ng-template>
<!-- Info Present Type -->
<!-- Show User Avatars and Full Name -->
<ng-template #infoPresentTypeRef>
<ng-container
*ngTemplateOutlet="
userInfoRef;
context: {
containsAvatar: true,
containsFullName: true,
avatarSlot: avatarSlot,
containsUserRole: true,
userRoleSlot: fullNameSlot,
fullNameSlot: fullNameSlot,
containsFriendshipState: false,
containsFriendshipActions: false,
friendshipStateSlot: fullNameSlot,
friendshipSlot: friendshipActionsSlot
}
"
></ng-container>
</ng-template>
<!-- Follower Present Type -->
<!-- Show User Info and it's related Friendship Actions -->
<ng-template #followerPresentTypeRef>
<ng-container
*ngTemplateOutlet="
userInfoRef;
context: {
containsAvatar: true,
containsFullName: true,
avatarSlot: avatarSlot,
fullNameSlot: fullNameSlot,
containsFriendshipState: true,
containsFriendshipActions: true,
friendshipStateSlot: fullNameSlot,
friendshipSlot: friendshipActionsSlot,
friendshipPresentType: FriendshipPresentTypes.AsFollowerActions
}
"
></ng-container>
</ng-template>
<!-- Follower Actions Present Type -->
<!-- Only Friendship Actions Present -->
<ng-template #followerActionsPresentTypeRef>
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
actionType: friendshipActionsType,
presentType: FriendshipPresentTypes.AsFollowerActions
}
"
></ng-container>
</ng-template>
<!-- Following Present Type -->
<!-- Show User Info and it's related Friendship Actions -->
<ng-template #followingPresentTypeRef>
<ng-container
*ngTemplateOutlet="
userInfoRef;
context: {
containsAvatar: true,
containsFullName: true,
avatarSlot: avatarSlot,
fullNameSlot: fullNameSlot,
containsFriendshipState: true,
containsFriendshipActions: true,
friendshipStateSlot: fullNameSlot,
friendshipSlot: friendshipActionsSlot,
friendshipPresentType: FriendshipPresentTypes.AsFollowingActions
}
"
></ng-container>
</ng-template>
<!-- Following Actions Present Type -->
<!-- Only Friendship Actions Present -->
<ng-template #followingActionsPresentTypeRef>
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
actionType: friendshipActionsType,
presentType: FriendshipPresentTypes.AsFollowingActions
}
"
></ng-container>
</ng-template>
<!-- Profile Present Type -->
<!-- User Info full / User Following Counts / User Following Actions -->
<ng-template #profilePresentTypeRef>
<!-- Following Present Type Ref (User Avatar/ FullName/ FriendshipState/ FollowinhActions Menu ) -->
<ng-container *ngTemplateOutlet="followingPresentTypeRef"></ng-container>
<section class="v-user-wrapper">
<!-- Represent Follower Following count(s) -->
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
presentType: FriendshipPresentTypes.AsInfo
}
"
></ng-container>
<!-- As Following Actions -->
<ng-container *ngIf="getValue(showActionsOnProfileView) | async">
<ng-container
*ngTemplateOutlet="followingActionsPresentTypeRef"
></ng-container>
</ng-container>
<!-- Other Profile Specifications -->
<!-- Represent Fields which Described in Field Descriptors -->
<!-- this Fields can only Editable When User Profile is Same -->
<ng-container *ngTemplateOutlet="profileFieldsRef"></ng-container>
</section>
</ng-template>
<!-- Form Present Type Template -->
<ng-template #formPresentTypeRef>
<section class="v-dto-form">
<!-- Form Template Presenter -->
<ng-container *ngIf="getValue(getFormModelConfig()) | async as formConfig">
<ng-container
*ngTemplateOutlet="
formPresenterRef;
context: {
formConfig: formConfig
}
"
></ng-container>
</ng-container>
</section>
</ng-template>
<!-- Nothing Present Type Template -->
<ng-template #nothingPresentTypeRef></ng-template>
<!-- -->
<!-- Other Templates -->
<!-- -->
<!-- Field Label Presenter Template -->
<ng-template #fieldLabelPresenterRef let-key="key">
<span
[isForeground]="true"
[xColor]="labelColor"
*ngIf="getValue(canShowFieldLabel(key)) | async"
class="v-dto-field-label v-dto-field-label-{{ key }}"
>
{{ getFieldProvidedLabel(key) }}
</span>
</ng-template>
<!-- Field Present Toggler Template -->
<ng-template
let-key="key"
#fieldPresentToggelrRef
let-isFieldView="isFieldView"
>
<!-- Form View Toggler / Form Apply ... -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[uiDisabled]="uiDisabled || loading"
[icon]="
isFieldView
? (getValue(fieldFormIcon) | async)
: (getValue(fieldFormApplyIcon) | async)
"
[iconColor]="
isFieldView
? (getValue(fieldFormIconColor) | async)
: (getValue(fieldFormApplyIconColor) | async)
"
(clicked)="
handleFormFieldAction(
key,
isFieldView ? FieldViewActions.ShowForm : FieldViewActions.Apply
)
"
class="v-dto-field-present-toggelr v-dto-field-{{ key }}-present-toggler"
></x-button>
<!-- Form Cancel -->
<x-button
*ngIf="!isFieldView"
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[uiDisabled]="uiDisabled || loading"
[icon]="getValue(fieldFormCancelIcon) | async"
[iconColor]="getValue(fieldFormCancelIconColor) | async"
(clicked)="handleFormFieldAction(key, FieldViewActions.Cancel)"
class="v-dto-field-form-cancel v-dto-field-{{ key }}-form-cancel"
></x-button>
</ng-template>
<!-- Form Presenter Template -->
<ng-template #formPresenterRef let-formConfig="formConfig">
<x-form
[isInPage]="false"
[wrapWithCard]="false"
[loading]="loading"
[formConfig]="formConfig"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="formActionProvider"
[cssClass]="getArrayValue(cssClass) | async"
[fillColor]="getValue(formFillColor) | async"
(valueChanged)="handleFormValueChanged($event)"
[activeColor]="getValue(formActiveColor) | async"
[regularColor]="getValue(formRegularColor) | async"
[invalidColor]="getValue(formInvalidColor) | async"
[pendingColor]="getValue(formPendingColor) | async"
[disabledColor]="getValue(formDisabledColor) | async"
[disabledFillColor]="getValue(formDisabledFillColor) | async"
></x-form>
</ng-template>
<!-- -->
<!-- Complex Presentation Templates -->
<!-- -->
<!-- User Info Template -->
<ng-template
#userInfoRef
let-avatarSlot="avatarSlot"
let-fullNameSlot="fullNameSlot"
let-userRoleSlot="userRoleSlot"
let-containsAvatar="containsAvatar"
let-friendshipSlot="friendshipSlot"
let-containsFullName="containsFullName"
let-containsUserRole="containsUserRole"
let-friendshipStateSlot="friendshipStateSlot"
let-friendshipPresentType="friendshipPresentType"
let-containsFriendshipState="containsFriendshipState"
let-containsFriendshipActions="containsFriendshipActions"
>
<section
[isForeground]="false"
class="v-user-info-container"
*ngIf="canShowUserInfo() | async"
[xColor]="getValue(userInfoColor) | async"
>
<x-slotter
[isForeground]="false"
[xColor]="getValue(userInfoColor) | async"
[endBottomSlotCssClass]="
getSlotContainerCSSClasses(SlotNames.END) | async
"
[startTopSlotCssClass]="
getSlotContainerCSSClasses(SlotNames.START) | async
"
[centerSlotCssClass]="
getSlotContainerCSSClasses(SlotNames.CENTER) | async
"
[hasEndOrBottomSlot]="
(containsAvatar && avatarSlot === SlotNames.END) ||
(containsFullName && fullNameSlot === SlotNames.END) ||
(containsUserRole && userRoleSlot === SlotNames.END) ||
(containsFriendshipActions && friendshipSlot === SlotNames.END) ||
(containsFriendshipState && friendshipStateSlot === SlotNames.END)
"
[hasStartOrTopSlot]="
(containsAvatar && avatarSlot === SlotNames.START) ||
(containsFullName && fullNameSlot === SlotNames.START) ||
(containsUserRole && userRoleSlot === SlotNames.START) ||
(containsFriendshipActions && friendshipSlot === SlotNames.START) ||
(containsFriendshipState && friendshipStateSlot === SlotNames.START)
"
[hasCenterSlot]="
(containsAvatar && avatarSlot === SlotNames.CENTER) ||
(containsFullName && fullNameSlot === SlotNames.CENTER) ||
(containsUserRole && userRoleSlot === SlotNames.CENTER) ||
(containsFriendshipActions && friendshipSlot === SlotNames.CENTER) ||
(containsFriendshipState && friendshipStateSlot === SlotNames.CENTER)
"
>
<!-- START -->
<x-slot
[name]="SlotNames.START"
[cssClass]="getSloInlinetCssClasses(SlotNames.START) | async"
>
<!-- Avatar -->
<section
class="v-user-avatar-container"
*ngIf="containsAvatar && avatarSlot === SlotNames.START"
>
<ng-container *ngTemplateOutlet="avatarRef"></ng-container>
</section>
<!-- User Info -->
<div
class="v-user-info-container"
*ngIf="
(containsFullName && fullNameSlot === SlotNames.START) ||
(containsFriendshipState && friendshipStateSlot === SlotNames.START)
"
>
<!-- FullName -->
<section
class="v-user-fullname-container"
*ngIf="containsFullName && fullNameSlot === SlotNames.START"
>
<ng-container *ngTemplateOutlet="fullNameRef"></ng-container>
</section>
<!-- User Role -->
<section
class="v-user-role-container"
*ngIf="containsUserRole && userRoleSlot === SlotNames.START"
>
<ng-container *ngTemplateOutlet="userRoleRef"></ng-container>
</section>
<!-- FriendshipState -->
<section
class="v-user-friendship-state-container"
*ngIf="
containsFriendshipState && friendshipStateSlot === SlotNames.START
"
>
<ng-container *ngTemplateOutlet="friendshipStateRef"></ng-container>
</section>
</div>
<!-- Actions -->
<section
class="v-user-friendship-actions"
*ngIf="
containsFriendshipActions &&
(isFriendshipActionsSupported() | async) &&
friendshipActionsSlot === this.SlotNames.START
"
>
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
presentType: friendshipPresentType,
cssClass: 'v-user-friendship-container',
actionType: FriendshipActionsPresentTypes.AsMenu
}
"
></ng-container>
</section>
</x-slot>
<!-- CENTER -->
<x-slot
[name]="SlotNames.CENTER"
[cssClass]="getSloInlinetCssClasses(SlotNames.CENTER) | async"
>
<!-- Avatar -->
<section
class="v-user-avatar-container"
*ngIf="containsAvatar && avatarSlot === SlotNames.CENTER"
>
<ng-container *ngTemplateOutlet="avatarRef"></ng-container>
</section>
<!-- User Info -->
<div
class="v-user-info-container"
*ngIf="
(containsFullName && fullNameSlot === SlotNames.CENTER) ||
(containsFriendshipState &&
friendshipStateSlot === SlotNames.CENTER)
"
>
<!-- FullName -->
<section
class="v-user-fullname-container"
*ngIf="containsFullName && fullNameSlot === SlotNames.CENTER"
>
<ng-container *ngTemplateOutlet="fullNameRef"></ng-container>
</section>
<!-- User Role -->
<section
class="v-user-role-container"
*ngIf="containsUserRole && userRoleSlot === SlotNames.CENTER"
>
<ng-container *ngTemplateOutlet="userRoleRef"></ng-container>
</section>
<!-- FriendshipState -->
<section
class="v-user-friendship-state-container"
*ngIf="
containsFriendshipState &&
friendshipStateSlot === SlotNames.CENTER
"
>
<ng-container *ngTemplateOutlet="friendshipStateRef"></ng-container>
</section>
</div>
<!-- Actions -->
<section
class="v-user-friendship-actions"
*ngIf="
containsFriendshipActions &&
(isFriendshipActionsSupported() | async) &&
friendshipActionsSlot === this.SlotNames.CENTER
"
>
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
presentType: friendshipPresentType,
cssClass: 'v-user-friendship-container',
actionType: FriendshipActionsPresentTypes.AsMenu
}
"
></ng-container>
</section>
</x-slot>
<!-- END -->
<x-slot
[name]="SlotNames.END"
[cssClass]="getSloInlinetCssClasses(SlotNames.END) | async"
>
<!-- Avatar -->
<section
class="v-user-avatar-container"
*ngIf="containsAvatar && avatarSlot === SlotNames.END"
>
<ng-container *ngTemplateOutlet="avatarRef"></ng-container>
</section>
<!-- User Info -->
<div
class="v-user-info-container"
*ngIf="
(containsFullName && fullNameSlot === SlotNames.END) ||
(containsFriendshipState && friendshipStateSlot === SlotNames.END)
"
>
<!-- FullName -->
<section
class="v-user-fullname-container"
*ngIf="containsFullName && fullNameSlot === SlotNames.END"
>
<ng-container *ngTemplateOutlet="fullNameRef"></ng-container>
</section>
<!-- User Role -->
<section
class="v-user-role-container"
*ngIf="containsUserRole && userRoleSlot === SlotNames.END"
>
<ng-container *ngTemplateOutlet="userRoleRef"></ng-container>
</section>
<!-- FriendshipState -->
<section
class="v-user-friendship-state-container"
*ngIf="
containsFriendshipState && friendshipStateSlot === SlotNames.END
"
>
<ng-container *ngTemplateOutlet="friendshipStateRef"></ng-container>
</section>
</div>
<!-- Actions -->
<section
class="v-user-friendship-actions"
*ngIf="
containsFriendshipActions &&
(isFriendshipActionsSupported() | async) &&
friendshipActionsSlot === this.SlotNames.END
"
>
<ng-container
*ngTemplateOutlet="
friendshipRef;
context: {
presentType: friendshipPresentType,
cssClass: 'v-user-friendship-container',
actionType: FriendshipActionsPresentTypes.AsMenu
}
"
></ng-container>
</section>
</x-slot>
</x-slotter>
</section>
</ng-template>
<!-- Fields Template -->
<ng-template #profileFieldsRef>
<section
class="v-dto-view"
*ngIf="hasChildValue(extractDescribedKeys()) | async"
>
<ng-container *ngIf="getArrayValue(extractDescribedKeys()) | async as keys">
<ng-container *ngFor="let key of keys">
<div
class="v-dto-field v-dto-field-{{ key }}"
*ngIf="getValue(canShowField(key)) | async"
[class.v-dto-field-view]="getValue(isFieldView(key)) | async"
[class.v-dto-field-form]="getValue(isFormView(key)) | async"
>
<!-- Check Specified Key has Template or not -->
<!-- Default Template -->
<ng-container *ngIf="!(getValue(hasCustomTemplate(key)) | async)">
<!-- Field View -->
<ng-container *ngIf="getValue(isFieldView(key)) | async">
<!-- Field View Toggel -->
<!-- Position: Before Label -->
<span
*ngIf="
(getValue(showFieldsForm) | async) &&
(getValue(canPresentFieldForm(key)) | async) &&
(getValue(fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeLabel
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
<!-- Break Line -->
<br *ngIf="getValue(canBreakContent(key)) | async" />
<!-- Field View Toggel -->
<!-- Position: Before Value -->
<span
*ngIf="
(getValue(showFieldsForm) | async) &&
(getValue(canPresentFieldForm(key)) | async) &&
(getValue(fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeValue
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Filed Content -->
<span
[xColor]="valueColor"
[isForeground]="true"
class="v-dto-field-value v-dto-field-value-{{ key }}"
>
{{ getFieldProvidedValue(key) }}
</span>
<!-- Field View Toggel -->
<!-- Position: After Value -->
<span
*ngIf="
(getValue(showFieldsForm) | async) &&
(getValue(canPresentFieldForm(key)) | async) &&
(getValue(fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.AfterValue
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(isFieldView(key)) | async
}
"
></ng-container>
</span>
</ng-container>
<!-- Form View -->
<ng-container *ngIf="getValue(isFormView(key)) | async">
<!-- Field Label -->
<ng-container *ngIf="getValue(canBreakContent(key)) | async">
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
</ng-container>
<!-- Create an Slotter for Form Field -->
<x-slotter
[layout]="SlotLayouts.HORIZONTAL"
[startTopSlotCssClass]="'x-dynamic-slot'"
[endBottomSlotCssClass]="'x-dynamic-slot'"
[hasStartOrTopSlot]="!(getValue(canBreakContent(key)) | async)"
>
<!-- Start (Field Label) -->
<x-slot
[name]="SlotNames.START"
[cssClass]="'ion-text-start'"
*ngIf="!(getValue(canBreakContent(key)) | async)"
>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
</x-slot>
<!-- Center (Field Form) -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-start'">
<!-- Field Form Presenter -->
<span
[xColor]="valueColor"
[isForeground]="true"
[class.rtl]="
getValue(
managerService.settingsService.uiDirection === 'rtl'
)
"
class="v-dto-field-form v-dto-field-form-slot v-dto-field-{{
key
}}-form"
*ngIf="
getValue(getFieldFormControlConfig(key))
| async as fiedlFormConfig
"
>
<!-- Form Template Present -->
<ng-container
*ngTemplateOutlet="
formPresenterRef;
context: {
formConfig: fiedlFormConfig
}
"
></ng-container>
</span>
</x-slot>
<!-- End (Field Toggle) -->
<x-slot [name]="SlotNames.END">
<!-- Field View Toggel -->
<span
*ngIf="getValue(canPresentFieldView(key)) | async"
class="v-dto-field-pesent-as-view v-dto-field-{{
key
}}-present-as-view"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(isFieldView(key)) | async
}
"
></ng-container>
</span>
</x-slot>
</x-slotter>
</ng-container>
</ng-container>
<!-- Custom Template -->
<ng-container *ngIf="getValue(hasCustomTemplate(key)) | async">
<!-- Field View Toggel -->
<!-- Position: Before Label -->
<span
*ngIf="
(getValue(fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeLabel &&
getValue(canPresentFieldForm(key)) | async
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
<!-- Break Line -->
<br *ngIf="getValue(canBreakContent(key)) | async" />
<!-- Here we Have to Handle Array Type -->
<ng-container
*ngIf="getValue(getFieldType(key)) | async as fieldType"
>
<!-- Only Non Array Types -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(fieldType) | async) &&
fieldType !== ModelFieldTypes.Array
"
>
</ng-container>
</ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</section>
</ng-template>
<!-- -->
<!-- User Present Types -->
<!-- -->
<!-- Avatar Template -->
<ng-template #avatarRef>
<img
[src]="getAvatarUrl() | async"
class="v-avatar-image animated zoomIn"
*ngIf="notNullOrUndefinedValue(model) | async"
/>
</ng-template>
<!-- FullName Template -->
<ng-template #fullNameRef>
<section
class="v-user-full-name animated zoomIn"
*ngIf="notNullOrUndefinedValue(model) | async"
>
{{ getUserFullName() | async }}
</section>
</ng-template>
<!-- User Role Template -->
<ng-template #userRoleRef>
<section
class="v-user-role animated zoomIn"
*ngIf="getUserRole() | async as userRole"
>
{{ userRole }}
</section>
</ng-template>
<!-- Friendship State Template -->
<ng-template #friendshipStateRef>
<section
class="v-user-friendship-state animated zoomIn"
*ngIf="getFriendshipState() | async as fState"
>
{{ fState }}
</section>
</ng-template>
<!-- Friendship Template -->
<ng-template
#friendshipRef
let-cssClass="cssClass"
let-actionType="actionType"
let-presentType="presentType"
>
<v-friendship
[loading]="loading"
[actionType]="actionType"
[presentType]="presentType"
[model]="model.friendshipInfo"
[color]="ColorNames.Transparent"
[uiDisabled]="uiDisabled || loading"
*ngIf="
(isFriendshipActionsSupported() | async) &&
(notNullOrUndefinedValue(model) | async)
"
(actionFired)="handleFriendshipActionFired($event)"
[cssClass]="notNullOrUndefinedValue(cssClass) ? cssClass : ''"
></v-friendship>
</ng-template>
<!-- -->
<!-- DTO Field Templates -->
<!-- -->
<!-- Field Label Presenter Template -->
<ng-template #fieldLabelPresenterRef let-key="key">
<span
[isForeground]="true"
[xColor]="labelColor"
*ngIf="getValue(canShowFieldLabel(key)) | async"
class="v-dto-field-label v-dto-field-label-{{ key }}"
>
{{ getFieldProvidedLabel(key) }}
</span>
</ng-template>
<!-- Field Present Toggler Template -->
<ng-template
let-key="key"
#fieldPresentToggelrRef
let-isFieldView="isFieldView"
>
<!-- Form View Toggler / Form Apply ... -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[uiDisabled]="uiDisabled || loading"
[icon]="
isFieldView
? (getValue(fieldFormIcon) | async)
: (getValue(fieldFormApplyIcon) | async)
"
[iconColor]="
isFieldView
? (getValue(fieldFormIconColor) | async)
: (getValue(fieldFormApplyIconColor) | async)
"
(clicked)="
handleFormFieldAction(
key,
isFieldView ? FieldViewActions.ShowForm : FieldViewActions.Apply
)
"
class="v-dto-field-present-toggelr v-dto-field-{{ key }}-present-toggler"
></x-button>
<!-- Form Cancel -->
<x-button
*ngIf="!isFieldView"
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[uiDisabled]="uiDisabled || loading"
[icon]="getValue(fieldFormCancelIcon) | async"
[iconColor]="getValue(fieldFormCancelIconColor) | async"
(clicked)="handleFormFieldAction(key, FieldViewActions.Cancel)"
class="v-dto-field-form-cancel v-dto-field-{{ key }}-form-cancel"
></x-button>
</ng-template>
<!-- Actions Template -->
<ng-template #dtoActionsRef>
<!-- All Available DTO Actions -->
<x-slotter [layout]="SlotLayouts.HORIZONTAL">
<!-- Start -->
<!-- Add Or Update -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-center'">
<x-button
[loading]="loading"
[type]="ButtonTypes.Raised"
[color]="ColorNames.Primary"
*ngIf="notNullOrUndefinedValue(modelState) | async"
(click)="onDtoModelAction(BaseDtoModelActions.AddOrUpdate)"
[title]="
(getValue(modelState) | async).isDefault
? ResourceIDs.add
: ResourceIDs.update
"
[uiDisabled]="
uiDisabled || loading || !(getValue(canAddOrUpdate()) | async)
"
>
</x-button>
</x-slot>
<!-- Center -->
<!-- Reset -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<x-button
[loading]="loading"
[type]="ButtonTypes.Raised"
[title]="ResourceIDs.reset"
(click)="handleResetModel()"
[color]="ColorNames.Warning"
[uiDisabled]="
uiDisabled || loading || !(getValue(canReset()) | async)
"
>
</x-button>
</x-slot>
<!-- End -->
<!-- Cancel -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-center'">
<x-button
[loading]="loading"
[type]="ButtonTypes.Raised"
[color]="ColorNames.Danger"
[title]="ResourceIDs.cancel"
*ngIf="notNullOrUndefinedValue(modelState) | async"
(click)="handleCancelEditModel(VPresentTypes.AsProfile)"
[uiDisabled]="
loading ||
uiDisabled ||
!(
(getValue(modelState) | async).hasOpenFormField ||
(getValue(presentType) | async) === PresentTypes.AsForm
)
"
></x-button>
</x-slot>
</x-slotter>
</ng-template>
<!-- Validation Errors Template -->
<ng-template #dtoValidationErrorsRef>
<ng-container
*ngIf="getArrayValue(modelState.errors) | async as validationErrors"
>
<ng-container *ngFor="let err of validationErrors">
<section
[isForeground]="true"
class="v-dto-validation-error"
[xColor]="getValue(formInvalidColor) | async"
>
{{ err }}
</section>
</ng-container>
</ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-user\v-user.component.scss
:host {
--avatar-size: 40px;
}
.x-header {
flex: 1 1 auto;
}
.v-dto-field {
width: 100%;
display: block;
margin: {
top: 8px;
bottom: 8px;
}
}
.v-user {
width: 100%;
display: block;
}
.v-user-wrapper {
padding: 6px;
}
.v-avatar-image {
//
width: var(--avatar-size);
height: var(--avatar-size);
//
clip-path: circle();
}
.v-user-full-name,
.v-user-friendship-state {
//
margin: {
left: 6px;
right: 6px;
}
}
.v-user-role,
.v-user-friendship-state {
font-size: xx-small;
}
.v-user-info-container {
//
width: 100%;
display: block;
padding: {
left: 6px;
right: 6px;
}
// height: var(--avatar-size);
}
::ng-deep x-card.v-user-card {
.x-avatar {
width: var(--avatar-size) !important;
height: var(--avatar-size) !important;
}
}
:host ::ng-deep .v-user-friendship-container {
.x-button {
.x-icon {
color: inherit !important;
}
}
}
.v-dto-field-value-phoneNumber {
direction: ltr;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-user\v-user.component.ts
import {
Input,
Inject,
NgZone,
Output,
Component,
Renderer2,
ElementRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
getTopRole,
getGenderResourceId,
getRoleTitleResourceId,
} from './v-user.tools';
import {
XTask,
nameof,
getMills,
getTaskId,
getTaskName,
isValidEmail,
XStandardType,
XExceptionIDs,
getFieldNames,
XTaskPriority,
isNullOrUndefined,
isValidPhoneNumber,
isNullOrEmptyString,
XResourceIdentifier,
} from 'x-framework-core';
import {
defaultAvatar,
UserProfileTasks,
VUserPresentType,
VUserDtoContainer,
VUserPresentTypeIdentifier,
} from './v-user.typings';
import {
toRoleInfo,
XAvailableRoles,
extractAvaialableRoles,
} from 'x-saherelm-api-sdk';
import {
XSlotName,
XFormValidators,
XPopoverService,
XFormControlType,
XFormControlConfig,
XSlotNameIdentifier,
XColorActionIdentifier,
XFormControlAppearance,
XFormSelectControlConfig,
XFormSelectControlOption,
XFormDatePickerControlConfig,
XFormDatePickerControlPickerPosition,
} from 'x-framework-components';
import { forkJoin } from 'rxjs';
import {
XGender,
XUserProfileDto,
XFriendshipType,
defaultUserProfileDto,
XProfileUpdateRequestDto,
prepareUserProfileDtoFields,
} from 'x-framework-identity-sdk';
import {
VBaseDtoComponent,
VBaseDtoModelAction,
VModelFieldDescriptor,
VModelValidationResult,
VBaseDtoFieldFormPresentationIconPosition,
} from '../../v-dto/v-dto.typings';
import { Validators } from '@angular/forms';
import {
getFullName,
isFriendshipTask,
XFriendshipTasks,
getProfileFriendshipStateResourceID,
} from '../v-friendship/v-friendship.tools';
import {
FriendshipAction,
FriendshipPresentType,
VFriendshipEventModel,
FriendshipActionsPresentType,
FriendshipActionsPresentTypeIdentifier,
} from '../v-friendship/v-friendship.typings';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { map, first, concatMap } from 'rxjs/operators';
import { applyFormatDate, XDateFormat } from '../../tools/date.tools';
import { SharedService } from 'src/app/shared/services/shared.service';
/**
* a View Component based on VBaseDtoContainer for
* represent User profile ...
*/
@Component({
selector: 'v-user',
templateUrl: './v-user.component.html',
styleUrls: ['./v-user.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VUserComponent extends VBaseDtoComponent<XUserProfileDto> {
//
//#region Properties ...
//
readonly FriendshipActionsPresentTypes = Object.assign(
{},
FriendshipActionsPresentType,
);
readonly VPresentTypes = Object.assign({}, VUserPresentType);
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly BaseDtoModelActions = Object.assign({}, VBaseDtoModelAction);
readonly FriendshipPresentTypes = Object.assign({}, FriendshipPresentType);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
/**
* extract XUserProfile Dto's Field Names ...
*/
readonly FieldNames = getFieldNames(defaultUserProfileDto);
/**
* introduce Default Model Implementation ...
*/
readonly defaultModel: XUserProfileDto = {
...defaultUserProfileDto,
};
/**
* Card Avatar URL ...
*/
cardAvatarUrl: string = '';
/**
* Remove Card Margin ...
*/
removeCardMargin: boolean = true;
/**
* Configure Field Forms and Optional Values ...
*/
showFieldsForm = false;
hideOptionalFieldsOnView = true;
fieldFormButtonPosition =
VBaseDtoFieldFormPresentationIconPosition.BeforeValue;
/**
* Introduce Field Descriptors ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// User Name ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.userName,
value: this.defaultModel.userName,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.user_name,
} as VModelFieldDescriptor<string>,
//
// First Name ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.firstName,
value: this.defaultModel.firstName,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.first_name,
allowFieldForm: true,
formControlConfig: {
index: 0,
propName: this.FieldNames.firstName,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.first_name,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.profile,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
//
// Last Name ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.lastName,
value: this.defaultModel.lastName,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.last_name,
allowFieldForm: true,
formControlConfig: {
index: 1,
propName: this.FieldNames.lastName,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.last_name,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.profile,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
//
// Date of Birth ...
{
hide: false,
hideLabel: false,
type: this.ModelFieldTypes.Date,
key: this.FieldNames.dateOfBirth,
value: this.defaultModel.dateOfBirth,
label: this.AppResourceIDs.date_of_birth,
valueProvider: (value: Date) => {
//
let result: string = '';
//
if (!isNullOrUndefined(value)) {
result = applyFormatDate(
value,
XDateFormat.DateOfBirth,
this.managerService,
);
}
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 2,
propName: this.FieldNames.dateOfBirth,
type: {
type: this.FormControlTypes.Date,
config: {
applyStateColor: true,
iconColor: this.ColorNames.Tertiary,
toggleIcon: this.IconNames.calendar,
datePickerPosition: XFormDatePickerControlPickerPosition.Prefix,
} as XFormDatePickerControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.date_of_birth,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<Date>,
//
// Gender ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.gender,
value: this.defaultModel.gender,
type: this.ModelFieldTypes.Number,
label: this.AppResourceIDs.gender,
valueProvider: (value: XGender) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.resourceProvider(getGenderResourceId(value));
},
allowFieldForm: true,
formControlConfig: {
index: 3,
propName: this.FieldNames.gender,
type: {
type: this.FormControlTypes.Select,
config: {
multiple: false,
options: [
//
// Male ...
{
value: XGender.Male,
viewValue: this.resourceProvider(this.AppResourceIDs.male),
},
//
// Female ...
{
value: XGender.Female,
viewValue: this.resourceProvider(this.AppResourceIDs.female),
},
],
} as XFormSelectControlConfig<XGender>,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.gender,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<XGender>,
//
// Role/Role(s) ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.roles,
value: this.defaultModel.roles,
label: this.AppResourceIDs.role,
type: this.ModelFieldTypes.Array,
valueProvider: (value) => {
//
if (isNullOrUndefined(value) || !this.hasChild(value)) {
return '';
}
const topRole = getTopRole(value);
let result = this.resourceProvider(getRoleTitleResourceId(topRole));
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 4,
propName: this.FieldNames.roles,
type: {
type: this.FormControlTypes.Select,
config: {
multiple: true,
options: [
...extractAvaialableRoles().map((ar) => {
return {
value: ar.toString(),
viewValue: this.resourceProvider(getRoleTitleResourceId(ar)),
} as XFormSelectControlOption<string>;
}),
],
},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.role,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<Array<XAvailableRoles>>,
//
// Phone Number ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.phoneNumber,
value: this.defaultModel.phoneNumber,
label: this.AppResourceIDs.phone_number,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
allowFieldForm: true,
formControlConfig: {
index: 5,
propName: this.FieldNames.phoneNumber,
type: {
type: this.FormControlTypes.Tel,
config: {},
},
validators: {
validators: [
Validators.required,
XFormValidators.validatePhoneNumber('IR'),
],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.phone_number,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.mobile,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
//
// PhoneNumber Confirmed ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.phoneNumberConfirmed,
value: this.defaultModel.phoneNumberConfirmed,
label: this.AppResourceIDs.phone_number_state,
type: this.ModelFieldTypes.Boolean,
valueProvider: (value) => {
//
let resourceID: XResourceIdentifier = undefined;
//
if (!!value) {
resourceID = this.AppResourceIDs.confirmed;
} else {
resourceID = this.AppResourceIDs.unconfirmed;
}
//
let result: string = '';
if (!isNullOrUndefined(resourceID)) {
result = this.resourceProvider(resourceID);
} else {
result = value.toString();
}
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 6,
propName: this.FieldNames.phoneNumberConfirmed,
type: {
type: this.FormControlTypes.CheckBox,
config: undefined,
},
validators: {},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: undefined,
appearance: this.FormControlAppearances.Outline,
icons: {},
},
},
} as VModelFieldDescriptor<boolean>,
//
// Email ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.email,
value: this.defaultModel.email,
label: this.AppResourceIDs.email,
type: this.ModelFieldTypes.String,
allowFieldForm: true,
formControlConfig: {
index: 7,
propName: this.FieldNames.email,
type: {
type: this.FormControlTypes.Email,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.email,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.mail,
color: this.ColorNames.Tertiary,
},
},
},
} as XFormControlConfig<any>,
} as VModelFieldDescriptor<string>,
//
// Email Confirmed ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.emailConfirmed,
value: this.defaultModel.emailConfirmed,
label: this.AppResourceIDs.email_state,
type: this.ModelFieldTypes.Boolean,
valueProvider: (value) => {
//
let resourceID: XResourceIdentifier = undefined;
//
if (!!value) {
resourceID = this.AppResourceIDs.confirmed;
} else {
resourceID = this.AppResourceIDs.unconfirmed;
}
//
let result: string = '';
if (!isNullOrUndefined(resourceID)) {
result = this.resourceProvider(resourceID);
} else {
result = value.toString();
}
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 8,
propName: this.FieldNames.emailConfirmed,
type: {
type: this.FormControlTypes.CheckBox,
config: undefined,
},
validators: {},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: undefined,
appearance: this.FormControlAppearances.Outline,
icons: {},
},
},
} as VModelFieldDescriptor<boolean>,
//
// Creation Date ...
{
hide: false,
hideLabel: false,
type: this.ModelFieldTypes.Date,
key: this.FieldNames.creationDate,
value: this.defaultModel.creationDate,
label: this.AppResourceIDs.registration_date,
valueProvider: (value: Date) => {
//
let result: string = '';
//
if (!isNullOrUndefined(value)) {
result = applyFormatDate(
value,
XDateFormat.EventDate,
this.managerService,
);
}
//
return result;
},
} as VModelFieldDescriptor<Date>,
//
// Last Login Data ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.lastLogin,
type: this.ModelFieldTypes.Date,
value: this.defaultModel.lastLogin,
label: this.AppResourceIDs.last_login,
valueProvider: (value: Date) => {
//
let result: string = '';
//
if (!isNullOrUndefined(value)) {
result = applyFormatDate(
value,
XDateFormat.EventDate,
this.managerService,
);
}
//
return result;
},
} as VModelFieldDescriptor<Date>,
//
// Is Enabled ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.isEnable,
label: this.AppResourceIDs.state,
value: this.defaultModel.isEnable,
type: this.ModelFieldTypes.Boolean,
valueProvider: (value) => {
//
let resourceID: XResourceIdentifier;
if (!value) {
resourceID = this.AppResourceIDs.disabled;
} else if (!!value) {
resourceID = this.AppResourceIDs.enabled;
}
//
let result = '';
if (!isNullOrUndefined(resourceID)) {
result = this.resourceProvider(resourceID);
}
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 9,
propName: this.FieldNames.isEnable,
type: {
type: this.FormControlTypes.CheckBox,
config: undefined,
},
validators: {},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: undefined,
appearance: this.FormControlAppearances.Outline,
icons: {},
},
},
} as VModelFieldDescriptor<boolean>,
//
// Is Banned ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.isBanned,
value: this.defaultModel.isBanned,
type: this.ModelFieldTypes.Boolean,
label: this.AppResourceIDs.bann_state,
valueProvider: (value) => {
//
let resourceID: XResourceIdentifier;
if (!value) {
resourceID = this.AppResourceIDs.disabled;
} else if (!!value) {
resourceID = this.AppResourceIDs.enabled;
}
//
let result = '';
if (!isNullOrUndefined(resourceID)) {
result = this.resourceProvider(resourceID);
}
//
return result;
},
allowFieldForm: true,
formControlConfig: {
index: 10,
propName: this.FieldNames.isBanned,
type: {
type: this.FormControlTypes.CheckBox,
config: undefined,
},
validators: {},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: undefined,
appearance: this.FormControlAppearances.Outline,
icons: {},
},
},
} as VModelFieldDescriptor<boolean>,
];
//
//#region Input/Output(s) ...
/**
* Specifies Present Type ...
*/
@Input()
presentType: VUserPresentTypeIdentifier = this.propertyProvider.presentType;
/**
* Specify Color of User Info Container ...
*/
@Input()
userInfoColor: XColorActionIdentifier = this.propertyProvider.userInfoColor;
/**
* Specified Can User Selected or not ...
*/
@Input()
canSelectUser: boolean = this.propertyProvider.canSelectUser;
/**
* Handle User Selected Event ...
*/
@Output()
userSelected = new EventEmitter<XUserProfileDto>();
@Input()
putUserInfoOnCardHeader: boolean =
this.propertyProvider.putUserInfoOnCardHeader;
/**
* Specify the size of Avatar ...
*/
@Input()
avatarSize: number = this.propertyProvider.avatarSize;
/**
* Enable Auto Styling Slots using Css Classes ...
*/
@Input()
autoStylingSlots: boolean = this.propertyProvider.autoStylingSlots;
/**
* Specify Avatar Slot ...
*/
@Input()
avatarSlot: XSlotNameIdentifier = this.propertyProvider.avatarSlot;
/**
* Specify Full Name Slot ...
*/
@Input()
fullNameSlot: XSlotNameIdentifier = this.propertyProvider.fullNameSlot;
/**
* an Optional Parameters which specified edit fautures
* can processed or not ...
*/
@Input()
supportEdit?: boolean = false;
/**
* an Optional Parameters which specified FriendshipActions fautures
* can processed or not ...
*/
@Input()
supportFriendshipActions?: boolean = true;
/**
* Specify User Friendship Actions Menu Slot ...
*/
@Input()
friendshipActionsSlot: XSlotNameIdentifier =
this.propertyProvider.friendshipActionsSlot;
/**
* How to Represent Friendship Actions ...
*/
@Input()
friendshipActionsType: FriendshipActionsPresentTypeIdentifier =
this.propertyProvider.friendshipActionsType;
/**
* Show Friendship Actions on Profile View ...
*/
@Input()
showActionsOnProfileView: XStandardType<boolean> =
this.propertyProvider.showActionsOnProfileView;
//#endregion
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: VUserDtoContainer,
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService,
propertyProvider,
);
}
//#endregion
//
//#region LifeCycles ...
/**
* Handle Changes ...
*
* @param changeKeys
*/
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
const canSelectUserChanged = changeKeys.includes(
nameof<VUserComponent>('canSelectUser'),
);
if (canSelectUserChanged) {
//
if (isNullOrUndefined(this.canSelectUser)) {
this.canSelectUser = this.propertyProvider.canSelectUser;
}
}
//
const isAvatarChanged = changeKeys.includes(
nameof<VUserComponent>('avatarSize'),
);
if (isAvatarChanged) {
this.handleAvatarSizeChanged();
}
//
const isUserInfoColorChanged = changeKeys.includes(
nameof<VUserComponent>('userInfoColor'),
);
if (isUserInfoColorChanged) {
//
if (isNullOrUndefined(this.userInfoColor)) {
this.userInfoColor = this.propertyProvider.userInfoColor;
}
}
//
const isPutUserInfoOnCardHeaderChanged = changeKeys.includes(
nameof<VUserComponent>('putUserInfoOnCardHeader'),
);
if (isPutUserInfoOnCardHeaderChanged) {
if (isNullOrUndefined(this.putUserInfoOnCardHeader)) {
this.putUserInfoOnCardHeader =
this.propertyProvider.putUserInfoOnCardHeader;
}
//
if (this.putUserInfoOnCardHeader) {
await this.handlePutUserInfoOnCard();
}
}
//
const isAutoStylingSlotsChanged = changeKeys.includes(
nameof<VUserComponent>('autoStylingSlots'),
);
if (isAutoStylingSlotsChanged) {
//
if (isNullOrUndefined(this.autoStylingSlots)) {
this.autoStylingSlots = this.propertyProvider.autoStylingSlots;
}
}
//
const isFriendshipActionsTypeChanged = changeKeys.includes(
nameof<VUserComponent>('friendshipActionsType'),
);
if (isFriendshipActionsTypeChanged) {
if (isNullOrUndefined(isFriendshipActionsTypeChanged)) {
this.friendshipActionsType =
this.propertyProvider.friendshipActionsType;
}
}
//
const isShowActionsOnProfileViewChanged = changeKeys.includes(
nameof<VUserComponent>('showActionsOnProfileView'),
);
if (isShowActionsOnProfileViewChanged) {
if (isNullOrUndefined(this.showActionsOnProfileView)) {
this.showActionsOnProfileView =
this.propertyProvider.showActionsOnProfileView;
}
}
//
const isAvatarSlotChanged = changeKeys.includes(
nameof<VUserComponent>('avatarSlot'),
);
const isFullNameSlotChanged = changeKeys.includes(
nameof<VUserComponent>('fullNameSlot'),
);
const isFriendshipActionsSlotChanged = changeKeys.includes(
nameof<VUserComponent>('friendshipActionsSlot'),
);
if (
isAvatarSlotChanged ||
isFullNameSlotChanged ||
isFriendshipActionsSlotChanged
) {
//
this.handleUserInfoSlotChanges();
}
}
/**
* Register Required Instance Subscriptions ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
//
// Register User ...
this.applyTakeUntilWrapper(this.sharedService.user$).subscribe(
async (up) => {
await this.validateFieldsOnUser(up);
},
);
}
//#endregion
//
//#region Overrides ...
/**
* Validate Model for Actions ...
*
* @param model
*/
validate(
model?: Partial<XUserProfileDto>,
): VModelValidationResult<XUserProfileDto> {
//
let result: VModelValidationResult<XUserProfileDto>;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
//
// Model not null or Undefined ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// User ID ...
isValid = !isNullOrEmptyString(normalized.userId);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.user_id,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidUserName)}`,
);
}
//
// User Name ...
isValid = !isNullOrEmptyString(normalized.userName);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.user_name,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidUserName)}`,
);
}
//
// Email ...
isValid =
!isNullOrEmptyString(normalized.email) &&
isValidEmail(normalized.email);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.email,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidEmailAddress)}`,
);
}
//
// PhoneNumber ...
isValid =
!isNullOrEmptyString(normalized.phoneNumber) &&
isValidPhoneNumber(normalized.phoneNumber, 'IR');
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.phone_number,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidMobileNumber)}`,
);
}
//
// Date Of Birth ...
// User Must above 14 ...
isValid =
!isNullOrUndefined(normalized.dateOfBirth) &&
getMills(normalized.dateOfBirth) <=
getMills() - 14 * 12 * 24 * 60 * 60 * 1000;
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.date_of_birth,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidData)}`,
);
}
//
// First Name ...
isValid = !isNullOrEmptyString(normalized.firstName);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.first_name,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Last Name ...
isValid = !isNullOrEmptyString(normalized.lastName);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.last_name,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Gender ...
isValid =
!isNullOrUndefined(normalized.gender) &&
[XGender.Male, XGender.Female].includes(normalized.gender);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.gender,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Roles ...
isValid =
!isNullOrUndefined(normalized.roles) &&
this.hasChild(normalized.roles) &&
normalized.roles.every((r: any) =>
extractAvaialableRoles().includes(r),
);
if (!isValid) {
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.role,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
}
//
isValid = !this.hasChild(validationErrors);
const isValidForAdd = isNullOrEmptyString(normalized.userId);
const isValidForUpdate = !isNullOrEmptyString(normalized.userId);
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
/**
* Prepare Model Fields ...
*
* @param model
*/
prepare(model?: XUserProfileDto): XUserProfileDto {
//
if (isNullOrUndefined(model)) {
model = {
...this.model,
};
}
//
return prepareUserProfileDtoFields(model);
}
/**
* Do all we want on Model Assigned ...
*/
async onModelAssigned(): Promise<void> {
super.onModelAssigned();
//
if (this.putUserInfoOnCardHeader) {
await this.handlePutUserInfoOnCard();
}
//
// Handling User Field Validation ...
const up = await this.getValueAsync(this.sharedService.user$);
await this.validateFieldsOnUser(up);
}
//#endregion
//
//#region UI Providers ...
/**
* retrieve Profile Avatar ...
*
* @returns
*/
getAvatarUrl() {
return this.getValue(this.model).pipe(
map((profile) =>
!isNullOrUndefined(profile) &&
!isNullOrUndefined(profile.avatar) &&
!isNullOrEmptyString(profile.avatar)
? profile.avatar
: defaultAvatar,
),
);
}
/**
* retrieve Profile Full Name ...
*
* @returns
*/
getUserFullName() {
return this.getValue(this.model).pipe(
map((profile) => getFullName(profile)),
);
}
/**
* Retrieve User To Role Value Translation ...
*
* @returns
*/
getUserRole() {
//
return this.getValue(this.model).pipe(
map((model) => {
//
let result = '';
//
if (
!isNullOrUndefined(model) &&
!isNullOrUndefined(model.roles) &&
this.hasChild(model.roles)
) {
//
const topRole = getTopRole(model.roles);
result = this.resourceProvider(getRoleTitleResourceId(topRole));
}
//
return result;
}),
);
}
/**
* get User's Friendship state ...
*/
getFriendshipState() {
return this.getValue(this.model).pipe(
map((model) => {
//
let result = '';
//
if (
!isNullOrUndefined(model) &&
!isNullOrUndefined(model.friendshipInfo)
) {
//
let resourceID: XResourceIdentifier = undefined;
//
resourceID = getProfileFriendshipStateResourceID(model);
//
if (!isNullOrUndefined(resourceID)) {
result = this.resourceProvider(resourceID);
}
}
//
return result;
}),
);
}
/**
* Only Show User info Section when there isn't provided on Card ...
*
* @returns
*/
canShowUserInfo() {
return forkJoin({
wrapWithCard: this.getValue(this.wrapWithCard),
putUserInfoOnCardHeader: this.getValue(this.putUserInfoOnCardHeader),
}).pipe(
map((info) => !(!!info.wrapWithCard && !!info.putUserInfoOnCardHeader)),
);
}
/**
* Prepare Slot Containers Css Classes ...
*
* @param slot
* @returns
*/
getSlotContainerCSSClasses(slot: XSlotName) {
return forkJoin({
avatarSlot: this.getValue(this.avatarSlot),
fullNameSlot: this.getValue(this.fullNameSlot),
friendshipActionsSlot: this.getValue(this.friendshipActionsSlot),
}).pipe(
map((slots) => {
//
let result: Array<string> = [];
if (!this.autoStylingSlots) {
return result;
}
//
result = ['center-vertical-flex'];
//
if (slots.fullNameSlot !== slot) {
result.push('dynamic-slot');
}
//
return result;
}),
first(),
);
}
/**
* Retrieve Slot Inline Classes ...
*
* @param slot
* @returns
*/
getSloInlinetCssClasses(slot: XSlotName) {
return forkJoin({
avatarSlot: this.getValue(this.avatarSlot),
fullNameSlot: this.getValue(this.fullNameSlot),
friendshipActionsSlot: this.getValue(this.friendshipActionsSlot),
}).pipe(
map((slots) => {
//
let result: Array<string> = [];
if (!this.autoStylingSlots) {
return result;
}
//
if (slots.avatarSlot === this.SlotNames.START) {
//
if (slot === slots.avatarSlot || slot === slots.fullNameSlot) {
result.push('ion-text-start');
} else {
result.push('ion-text-end');
}
} else if (slots.avatarSlot === this.SlotNames.END) {
//
if (slot === slots.avatarSlot || slot === slots.fullNameSlot) {
result.push('ion-text-end');
} else {
result.push('ion-text-start');
}
} else if (slots.avatarSlot === this.SlotNames.CENTER) {
//
if (slot === slots.avatarSlot || slot === slots.fullNameSlot) {
result.push('ion-text-center');
} else {
//
let s = 'ion-text-start';
if (slots.friendshipActionsSlot === this.SlotNames.END) {
s = 'ion-text-end';
} else if (slots.friendshipActionsSlot === this.SlotNames.CENTER) {
s = 'ion-text-center';
}
//
result.push(s);
}
}
//
return result;
}),
first(),
);
}
/**
* Provides Friendship Actions is Supported or not ...
*
* @returns
*/
isFriendshipActionsSupported() {
return this.getValue(this.supportFriendshipActions).pipe(
map((isSupport) => isNullOrUndefined(isSupport) || !!isSupport),
);
}
//#endregion
//
//#region UI Handlers ...
/**
* Handle User Selected ...
*/
handleUserSelected() {
//
if (
isNullOrUndefined(this.model) ||
this.loading ||
this.uiDisabled ||
!this.canSelectUser
) {
return;
}
//
this.userSelected.emit(this.model);
}
/**
* issue Friendship Tasks ...
*
* @param event
*/
async handleFriendshipActionFired(event: VFriendshipEventModel) {
//
// Validate Event Model ...
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
isNullOrEmptyString(event.destUser)
) {
return;
}
//
let mTaskName = '';
let mTask = undefined;
//
// Specified Task Name and Task based on Required Action ...
switch (event.action) {
//
case FriendshipAction.Block:
//
mTaskName = XFriendshipTasks.Block;
mTask = this.sharedService.accountsService.block(event.destUser);
break;
//
case FriendshipAction.Accept:
//
mTaskName = XFriendshipTasks.Accept;
mTask = this.sharedService.accountsService.accept(event.destUser);
break;
//
case FriendshipAction.Cancel:
//
mTaskName = XFriendshipTasks.Cancel;
mTask = this.sharedService.accountsService.cancel(event.destUser);
break;
//
case FriendshipAction.Follow:
//
mTaskName = XFriendshipTasks.Follow;
mTask = this.sharedService.accountsService.follow(event.destUser);
break;
//
case FriendshipAction.Reject:
//
mTaskName = XFriendshipTasks.Reject;
mTask = this.sharedService.accountsService.reject(event.destUser);
break;
//
case FriendshipAction.UnBlock:
//
mTaskName = XFriendshipTasks.UnBlock;
mTask = this.sharedService.accountsService.unBlock(event.destUser);
break;
//
case FriendshipAction.UnFollow:
//
if (event.type === XFriendshipType.Follower) {
//
// UnFollow Follower ...
mTaskName = XFriendshipTasks.UnFollowFollower;
mTask = this.sharedService.accountsService.unFollowFollower(
event.destUser,
);
} else if (event.type === XFriendshipType.Following) {
//
// UnFollow Following ...
mTaskName = XFriendshipTasks.UnFollowFollowing;
mTask = this.sharedService.accountsService.unFollowFollowing(
event.destUser,
);
}
break;
}
//
// Check Task and Task Name prepared or not ...
if (isNullOrEmptyString(mTaskName) || isNullOrUndefined(mTask)) {
return;
}
//
// Add Or Update Task ...
await this.addOrUpdateTask({
task: mTask,
name: mTaskName,
priority: XTaskPriority.High,
});
//
// Dispatch Task ...
await this.dispatchTask(mTaskName);
}
//#endregion
//
//#region Private ...
/**
* Apply Avatar Size CSS Var ...
*/
private handleAvatarSizeChanged() {
//
// Normalize Avatar Size ...
if (isNullOrUndefined(this.avatarSize) || this.avatarSize <= 16) {
this.avatarSize = this.propertyProvider.avatarSize;
}
//
this.setCssVar('avatar-size', `${this.avatarSize}px`);
this.detectChanges();
}
/**
* Handle User Info Slot Changes ...
*/
private handleUserInfoSlotChanges() {
//
if (isNullOrUndefined(this.avatarSlot)) {
this.avatarSlot = this.propertyProvider.avatarSlot;
}
//
if (isNullOrUndefined(this.fullNameSlot)) {
this.fullNameSlot = this.propertyProvider.fullNameSlot;
}
//
if (isNullOrUndefined(this.friendshipActionsSlot)) {
this.friendshipActionsSlot = this.propertyProvider.friendshipActionsSlot;
}
//
this.detectChanges();
}
/**
* set requirements for show user info on Card Toolbar ...
*/
private async handlePutUserInfoOnCard() {
//
const cardTitle = this.cardTitle;
const cardSubTitle = this.cardSubTitle;
//
const role = await this.getValueAsync(this.getUserRole());
const fullName = await this.getValueAsync(this.getUserFullName());
const friendshipState = await this.getValueAsync(this.getFriendshipState());
//
this.cardAvatarUrl = await this.getValueAsync(this.getAvatarUrl());
this.cardTitle = fullName || cardTitle;
this.cardSubTitle = friendshipState || role || cardSubTitle;
}
/**
* Validate Fileds ...
*
* @param up
* @returns
*/
private async validateFieldsOnUser(up: XUserProfileDto) {
//
let isMe = false;
let isAdmin = false;
let canEdit = false;
//
// Check Support Edit Value ...
const supportEdit =
isNullOrUndefined(this.supportEdit) || !!this.supportEdit;
//
// Calculate requirement to Detect Requeted User is Same as Current User ...
// or Current User is Admin ...
if (!isNullOrUndefined(up) && !isNullOrUndefined(this.model)) {
//
isMe =
up.email === this.model.email ||
up.userId === this.model.userId ||
up.userName === this.model.userName ||
up.phoneNumber === this.model.phoneNumber;
//
const roleInfo = toRoleInfo(up);
isAdmin = roleInfo.isAdmin;
//
canEdit = isMe || isAdmin;
}
//
// Handle Configurations
// base on canEdit ...
if (canEdit && supportEdit) {
//
this.showFieldsForm = true;
this.hideOptionalFieldsOnView = false;
//
// Here We Have to Decide Which Fields Hide ased on
// Viewer Type of User ...
if (isMe && !isAdmin) {
//
// Disable Roles, Email Confirmed, Phone Confirmed, isEnabled, isBanned ...
this.disableFieldEdits(
[
'roles',
'email',
'isEnable',
'isBanned',
'phoneNumber',
'emailConfirmed',
'phoneNumberConfirmed',
],
true,
);
} else if (isAdmin) {
//
// Hide Nothing ...
}
} else {
//
this.showFieldsForm = false;
this.hideOptionalFieldsOnView = true;
//
// Disable FirstName and LastName on Can not Edit State ...
this.hideFields(['firstName', 'lastName'], true);
//
// Check onl Required Data to Show for User ...
if (!isMe && !isAdmin) {
//
// Hide these Fields for UnKnown Users ...
this.hideFields(
[
'roles',
'email',
'isBanned',
'isEnable',
'lastLogin',
'dateOfBirth',
'phoneNumber',
'emailConfirmed',
'phoneNumberConfirmed',
],
true,
);
}
}
//
// Apply Change Detect ...
this.runTimedOut(() => this.detectChanges(), 1000);
}
/**
* Check Task Finish and Issue Load Profile of Requested User ...
*
* @param name
* @param task
* @returns
*/
private async handleFriendshipTaskFinished(name: string, task: XTask<any>) {
//
if (
isNullOrUndefined(task) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.userId) ||
name === XFriendshipTasks.LoadProfile
) {
return;
}
//
// Here we have to Aanaly Task and Rtrieve User Profile Task and Issue it ...
//
const mTaskName = XFriendshipTasks.LoadProfile;
const mTask = this.sharedService.accountsService.getProfile(
this.model.userId,
);
//
await this.addOrUpdateTask({
task: mTask,
name: mTaskName,
priority: XTaskPriority.High,
});
//
await this.dispatchTask(mTaskName);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-user\v-user.tools.ts
import {
toNormalString,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
} from 'x-framework-core';
import { defaultAvatar, defaultFullName } from './v-user.typings';
import { XGender, XUserProfileDto } from 'x-framework-identity-sdk';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { getTopRoleByRoles, XAvailableRoles } from 'x-saherelm-api-sdk';
/**
* Generate User Full Name ...
*
* @param user
* @returns
*/
export function getUserFullName(user: XUserProfileDto) {
//
if (isNullOrUndefined(user)) {
return defaultFullName;
}
//
return `${user.firstName} ${user.lastName}`;
}
/**
* Get User Avatar URL ...
*
* @param user
* @returns
*/
export function getUserAvatarURL(user: XUserProfileDto) {
//
if (isNullOrUndefined(user)) {
return defaultAvatar;
}
//
return user.avatar || defaultAvatar;
}
/**
* Retrieve Specified Role Title Resource Id ...
*
* @param role
* @returns
*/
export function getRoleTitleResourceId(role: XAvailableRoles | string) {
//
let result: XResourceIdentifier = null;
if (isNullOrEmptyString(role)) {
return result;
}
//
const mRole = toNormalString(role);
switch (mRole) {
//
case 'admin':
result = AppResourceIDs.admin_role;
break;
//
case 'responsible_manager':
result = AppResourceIDs.responsible_manager_role;
break;
//
case 'journalist':
result = AppResourceIDs.journalist_role;
break;
//
case 'chief_clerk':
result = AppResourceIDs.chief_clerk_role;
break;
//
case 'chief_clerk_assistant':
result = AppResourceIDs.chief_clerk_assistant_role;
break;
//
case 'actuary':
result = AppResourceIDs.actuary_role;
break;
//
case 'actuary_assistant':
result = AppResourceIDs.actuary_assistant_role;
break;
//
case 'reporter':
result = AppResourceIDs.reporter_role;
break;
//
case 'reporter_assistant':
result = AppResourceIDs.reporter_assistant_role;
break;
//
case 'agent':
result = AppResourceIDs.agent_role;
break;
//
case 'user':
result = AppResourceIDs.user_role;
break;
}
//
return result;
}
/**
* Retrieve Specified Role Description Resource Id ...
*
* @param role
* @returns
*/
export function getRoleDescriptionResourceId(role: XAvailableRoles | string) {
//
let result: XResourceIdentifier = null;
if (isNullOrEmptyString(role)) {
return result;
}
//
const mRole = toNormalString(role);
switch (mRole) {
//
case 'admin':
result = AppResourceIDs.admin_role_description;
break;
//
case 'responsible_manager':
result = AppResourceIDs.responsible_manager_role_description;
break;
//
case 'journalist':
result = AppResourceIDs.journalist_role_description;
break;
//
case 'chief_clerk':
result = AppResourceIDs.chief_clerk_role_description;
break;
//
case 'chief_clerk_assistant':
result = AppResourceIDs.chief_clerk_assistant_role_description;
break;
//
case 'actuary':
result = AppResourceIDs.actuary_role_description;
break;
//
case 'actuary_assistant':
result = AppResourceIDs.actuary_assistant_role_description;
break;
//
case 'reporter':
result = AppResourceIDs.reporter_role_description;
break;
//
case 'reporter_assistant':
result = AppResourceIDs.reporter_assistant_role_description;
break;
//
case 'agent':
result = AppResourceIDs.agent_role_description;
break;
//
case 'user':
result = AppResourceIDs.user_role_description;
break;
}
//
return result;
}
/**
* Extract User Top Role ...
*
* @param user
* @returns
*/
export function getUserTopRole(user: XUserProfileDto) {
//
if (isNullOrUndefined(user)) {
return undefined;
}
//
return getTopRole(user.roles);
}
/**
* Extract Top Role based on Roles Array ...
*
* @param roles
* @returns
*/
export function getTopRole(roles: Array<XAvailableRoles | string>) {
return getTopRoleByRoles(roles);
}
/**
* Extract Gender Resource Id ...
*
* @param gender
* @returns
*/
export function getGenderResourceId(gender: XGender) {
//
if (gender === XGender.Male) {
return AppResourceIDs.male;
} else if (gender === XGender.Female) {
return AppResourceIDs.female;
}
//
return undefined;
}
/**
* Extract User Gender Resource Id ...
*
* @param user
* @returns
*/
export function getUserGenderResourceId(user: XUserProfileDto) {
//
if (!user) {
return undefined;
}
//
return getGenderResourceId(user.gender);
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\account\v-user\v-user.typings.ts
import {
XStandardType,
XColorIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
VFriendshipEventModel,
FriendshipActionsPresentType,
FriendshipActionsPresentTypeIdentifier,
} from '../v-friendship/v-friendship.typings';
import { EventEmitter, Injectable } from '@angular/core';
import { XUserProfileDto } from 'x-framework-identity-sdk';
import { XSlotName, XSlotNameIdentifier } from 'x-framework-components';
import { VBaseDtoContainer } from '../../v-dto/v-dto.typings';
//
//#region Resource and Typings ...
export const defaultFullName = '...';
export const defaultAvatar = '/assets/image/default-user-image.png';
/**
* Present Types ...
*/
export enum VUserPresentType {
/**
* Represent Nothing ...
*/
AsNone = 'as_none',
/**
* Represent a User as Form for Add or Update Configured Fields ...
*/
AsForm = 'as_form',
/**
* Represent Only Avatar ...
*/
AsAvatar = 'as_avatar',
/**
* Represent User Avatar and Full Name ...
*/
AsInfo = 'as_info',
/**
* Represent User Info and it's Related Axctions as a Follower ...
*/
AsFollower = 'as_follower',
AsFollowerActions = 'as_follower_actions',
/**
* Represent User Info and it's Related Axctions as a Following ...
*/
AsFollowing = 'as_following',
AsFollowingActions = 'as_following_actions',
/**
* Represent a User as it's Profile Summary which contains all
* available features ...
*/
AsProfile = 'as_profile',
}
export type VUserPresentTypeIdentifier = VUserPresentType | string;
export enum UserProfileTasks {
AddUserProfile = 'add_user_profile',
UpdateUserProfile = 'update_user_profile',
}
//#endregion
//
//#region Container ...
/**
* Contains all available Input/Output(s) default Values ...
*/
@Injectable({
providedIn: 'root',
})
export class VUserDtoContainer extends VBaseDtoContainer {
//
readonly userInfoColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
readonly putUserInfoOnCardHeader: boolean = false;
//
readonly avatarSize: number = 40;
readonly wrapWithCard: boolean = false;
readonly autoStylingSlots: boolean = true;
readonly avatarSlot: XSlotNameIdentifier = XSlotName.START;
readonly fullNameSlot: XSlotNameIdentifier = XSlotName.CENTER;
readonly friendshipActionsSlot: XSlotNameIdentifier = XSlotName.END;
readonly friendshipActionFired: EventEmitter<VFriendshipEventModel> =
new EventEmitter<VFriendshipEventModel>();
//
readonly showActionsOnProfileView: XStandardType<boolean> = false;
readonly friendshipActionsType: FriendshipActionsPresentTypeIdentifier =
FriendshipActionsPresentType.AsFAB;
//
readonly canSelectUser = false;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\classes\v-base.component.ts
import {
Inject,
Output,
NgZone,
Renderer2,
Component,
ElementRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
TemplateRef,
} from '@angular/core';
import {
formatDate,
isNullOrEmptyString,
isNullOrUndefined,
XColorWithBrightness,
} from 'x-framework-core';
import {
XPopoverService,
XComponentCardContainer,
XComponentCardContainerComponent,
} from 'x-framework-components';
import { X_CONFIG } from '../../config/x.config';
import { XConfig } from '../../config/app.config';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { getEventDateFormatString } from '../tools/v-base.tools';
import { AppResourceIDs } from '../../config/localization.config';
import { XBaseComponent, XIconNames } from 'x-framework-components';
import { SharedService } from 'src/app/shared/services/shared.service';
@Component({
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class VBaseComponent extends XBaseComponent {
//
//#region Props ...
//
readonly IconNames = Object.assign({}, XIconNames);
readonly AppResourceIDs = Object.assign({}, AppResourceIDs);
readonly ColorNames = Object.assign({}, XColorWithBrightness);
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService
) {
super(
config,
managerService,
element,
renderer,
ruler,
zone,
changeDetector
);
}
//#endregion
//
//#region UI Handlers ...
applyDefaultDate(value: Date) {
//
const result = formatDate(
value,
this.managerService.currentLocale,
getEventDateFormatString(this.managerService.currentLocale),
true
);
//
return result;
}
//#endregion
//
//#region Actions ...
/**
* 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 Confirmed Action ...
*
* @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 Private ...
//#endregion
}
@Component({
template: '',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class VBaseInCardComponent extends XComponentCardContainerComponent {
//
//#region Props ...
//
readonly IconNames = Object.assign({}, XIconNames);
readonly AppResourceIDs = Object.assign({}, AppResourceIDs);
readonly ColorNames = Object.assign({}, XColorWithBrightness);
//
@Output()
avatarClicked = new EventEmitter<void>();
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: XComponentCardContainer
) {
super(
config,
managerService,
element,
renderer,
ruler,
zone,
changeDetector,
propertyProvider
);
}
//#endregion
//
//#region UI Handlers ...
applyDefaultDate(value: Date) {
//
let result = '';
//
if (!isNullOrUndefined(value)) {
result = formatDate(
value,
this.managerService.currentLocale,
getEventDateFormatString(this.managerService.currentLocale),
true
);
}
//
return result;
}
//#endregion
//
//#region Actions ...
/**
* 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();
}
}
//#endregion
//
//#region Protected ...
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\constants\actionbar.defaults.ts
import { XColorWithBrightness } from 'x-framework-core';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { XActionBarItemSlot, XIconNames } from 'x-framework-components';
import { XSimpleActionBarItem } from '../typings/simple.actionbar.typings';
export const defaultCallRequestActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'CallRequest',
icon: XIconNames.phone,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.call_request,
iconColor: XColorWithBrightness.Success,
};
export const defaultCallEndActionBar: XSimpleActionBarItem = {
index: 0,
id: 'CallEnd',
onlyIcon: true,
icon: XIconNames.phone,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.call_end,
iconColor: XColorWithBrightness.Danger,
};
export const defaultMuteActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'Mute',
icon: XIconNames.volume_mute,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.mute,
iconColor: XColorWithBrightness.LightShade,
};
export const defaultUnMuteActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'Unmute',
icon: XIconNames.volume_high,
title: AppResourceIDs.unmute,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.LightShade,
};
export const defaultFullscreenActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'Fullscreen',
icon: XIconNames.fullscreen,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.fullscreen,
iconColor: XColorWithBrightness.Light,
};
export const defaultUnfullscreenActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'UnFullscreen',
slot: XActionBarItemSlot.START,
icon: XIconNames.close_fullscreen,
title: AppResourceIDs.unfullscreen,
iconColor: XColorWithBrightness.Light,
};
export const defaultMicOnActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'MicOn',
icon: XIconNames.mic,
title: AppResourceIDs.mic_on,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.Light,
};
export const defaultMicOffActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'MicOff',
icon: XIconNames.mic_off,
title: AppResourceIDs.mic_off,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.Light,
};
export const defaultVideoOnActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'VideoOn',
icon: XIconNames.videocam,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.video_on,
iconColor: XColorWithBrightness.Light,
};
export const defaultVideoOffActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'VideoOff',
slot: XActionBarItemSlot.START,
title: AppResourceIDs.video_off,
iconColor: XColorWithBrightness.Light,
icon: XIconNames.video_camera_front_off,
};
export const defaultSwitchToFrontCameraActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'SwitchToFrontCamera',
icon: XIconNames.cameraswitch,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.Light,
title: AppResourceIDs.switch_to_front_camera,
};
export const defaultSwitchToBackCameraActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'SwitchToBackCamera',
icon: XIconNames.cameraswitch,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.Light,
title: AppResourceIDs.switch_to_back_camera,
};
export const defaultStartScreenShareActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'StartScreenShare',
icon: XIconNames.screen_share,
slot: XActionBarItemSlot.START,
iconColor: XColorWithBrightness.Light,
title: AppResourceIDs.start_share_screen,
};
export const defaultStopScreenShareActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'StopScreenShare',
slot: XActionBarItemSlot.START,
icon: XIconNames.stop_screen_share,
iconColor: XColorWithBrightness.Light,
title: AppResourceIDs.stop_share_screen,
};
export const defaultStartRecordActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'StartRecord',
icon: XIconNames.screen_record,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.start_record,
iconColor: XColorWithBrightness.Danger,
};
export const defaultStopRecordActionBar: XSimpleActionBarItem = {
index: 0,
onlyIcon: true,
id: 'StopRecord',
icon: XIconNames.stop,
slot: XActionBarItemSlot.START,
title: AppResourceIDs.stop_record,
iconColor: XColorWithBrightness.Danger,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\constants\button.defaults.ts
import {
XIconNames,
XButtonType,
IXButtonContainer,
} from 'x-framework-components';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { XColorWithBrightness, XResourceIDs } from 'x-framework-core';
/**
* Extends a Partial Container interface for Configuring Buttons ...
*/
export interface VActionButtonConfig extends Partial<IXButtonContainer> {
action?: string;
}
//
//#region Default Action Buttons Configurations ...
export const defaultBaseActionButton: VActionButtonConfig = {
foregroundColor: true,
type: XButtonType.Icon,
};
export const defaultShareActionButton: VActionButtonConfig = {
...defaultBaseActionButton,
icon: XIconNames.share,
title: AppResourceIDs.share,
iconColor: XColorWithBrightness.Tertiary,
};
export const defaultCopyToClipboardActionButton: VActionButtonConfig = {
...defaultBaseActionButton,
icon: XIconNames.clipboard,
title: XResourceIDs.copy_to_clipboard,
iconColor: XColorWithBrightness.Tertiary,
};
// export const defaultCopyToClipboardActionButton: VActionButtonConfig = {
// ...defaultBaseActionButton,
// icon: XIconNames.clipboard,
// title: XResourceIDs.copy_to_clipboard,
// iconColor: XColorWithBrightness.Tertiary
// }
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\constants\fab.defaults.ts
import {
XIconNames,
XFabButtonSide,
XFabButtonSize,
XFabVerticalSlot,
XFabHorizontalSlot,
} from 'x-framework-components';
import {
XSimpleFabItem,
XSimpleFabButton,
} from '../typings/simple.fab.typings';
import { XColorWithBrightness, XResourceIDs } from 'x-framework-core';
import { AppResourceIDs } from 'src/app/config/localization.config';
//
// Edit ...
export const defaultEditFabButton: XSimpleFabButton = {
index: 0,
id: 'Edit',
disabled: false,
icon: XIconNames.edit,
side: XFabButtonSide.TOP,
size: XFabButtonSize.MINI,
tooltip: XResourceIDs.edit,
color: XColorWithBrightness.Tertiary,
};
//
// Remove ...
export const defaultRemoveFabButton: XSimpleFabButton = {
index: 0,
id: 'Remove',
disabled: false,
icon: XIconNames.remove,
side: XFabButtonSide.TOP,
size: XFabButtonSize.MINI,
tooltip: XResourceIDs.delete,
color: XColorWithBrightness.Danger,
};
//
// Add ...
export const defaultAddFabButton: XSimpleFabButton = {
index: 0,
id: 'Add',
disabled: false,
icon: XIconNames.add,
side: XFabButtonSide.TOP,
tooltip: XResourceIDs.add,
size: XFabButtonSize.REGULAR,
color: XColorWithBrightness.Primary,
};
//
// Upload ...
export const defaultUploadFabButton: XSimpleFabButton = {
index: 0,
id: 'Upload',
disabled: false,
icon: XIconNames.upload,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.upload,
color: XColorWithBrightness.Primary,
};
//
// Download ...
export const defaultDownloadFabButton: XSimpleFabButton = {
index: 0,
id: 'Download',
disabled: false,
side: XFabButtonSide.TOP,
icon: XIconNames.download,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.download,
color: XColorWithBrightness.Success,
};
//
// Select ...
export const defaultSelectFilesFabButton: XSimpleFabButton = {
index: 0,
disabled: false,
id: 'SelectFiles',
icon: XIconNames.touch,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.select,
color: XColorWithBrightness.Primary,
};
//
// More ...
export const defaultMoreFabButton: XSimpleFabButton = {
id: 'More',
index: 0,
disabled: false,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.options,
icon: XIconNames.menu_vertical,
color: XColorWithBrightness.Primary,
};
//
// Back ...
export const defaultBackFabButton: XSimpleFabButton = {
id: 'Back',
index: 0,
disabled: false,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.back,
icon: XIconNames.arrow_back,
color: XColorWithBrightness.Primary,
};
//
// Refresh ...
export const defaultRefreshFabButton: XSimpleFabButton = {
id: 'Refresh',
index: 0,
disabled: false,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.refresh,
icon: XIconNames.refresh,
color: XColorWithBrightness.Primary,
};
//
// Close ...
export const defaultCloseFabButton: XSimpleFabButton = {
id: 'Close',
index: 0,
disabled: false,
side: XFabButtonSide.TOP,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.close,
icon: XIconNames.close,
color: XColorWithBrightness.Danger,
};
//
// Connect ...
export const defaultConnectFabButton: XSimpleFabButton = {
id: 'Connect',
index: 0,
disabled: false,
side: XFabButtonSide.TOP,
icon: XIconNames.live_tv,
size: XFabButtonSize.REGULAR,
tooltip: AppResourceIDs.connect,
color: XColorWithBrightness.Success,
};
//
// Default Options Fab Item ...
export const defaultOptionsFabItem: XSimpleFabItem = {
fixed: true,
id: 'Options',
disabled: false,
size: XFabButtonSize.REGULAR,
tooltip: XResourceIDs.options,
icon: XIconNames.menu_vertical,
color: XColorWithBrightness.Primary,
//
// Positioning ...
vertical: XFabVerticalSlot.BOTTOM,
horizontal: XFabHorizontalSlot.END,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\constants\slide.options.defaults.ts
import { XIconNames } from 'x-framework-components';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { XColorWithBrightness, XResourceIDs } from 'x-framework-core';
import { XSimpleListItemSlideOption } from '../typings/simple.list-item.typings';
// Details ...
export const defaultDetailsSlideOption = {
id: 'Details',
slot: 'start',
onlyIcon: true,
disabled: false,
icon: XIconNames.visible,
title: XResourceIDs.details,
color: XColorWithBrightness.Secondary,
} as XSimpleListItemSlideOption;
//
// Edit ...
export const defaultEditSlideOption = {
id: 'Edit',
slot: 'start',
onlyIcon: true,
disabled: false,
icon: XIconNames.edit,
title: XResourceIDs.edit,
color: XColorWithBrightness.Tertiary,
} as XSimpleListItemSlideOption;
//
// Download ...
export const defaultDownloadSlideOption = {
slot: 'start',
id: 'Download',
onlyIcon: true,
disabled: false,
icon: XIconNames.download,
title: XResourceIDs.download,
color: XColorWithBrightness.Success,
} as XSimpleListItemSlideOption;
//
// Remove ...
export const defaultRemoveSlideOption = {
slot: 'end',
id: 'Remove',
onlyIcon: true,
disabled: false,
icon: XIconNames.remove,
title: XResourceIDs.delete,
color: XColorWithBrightness.Danger,
} as XSimpleListItemSlideOption;
//
// Close ...
export const defaultCloseSlideOption = {
slot: 'end',
id: 'Close',
onlyIcon: true,
disabled: false,
icon: XIconNames.close,
title: XResourceIDs.close,
color: XColorWithBrightness.Danger,
} as XSimpleListItemSlideOption;
//
// Connect ...
export const defaultConnectSlideOption = {
slot: 'start',
id: 'Connect',
onlyIcon: true,
disabled: false,
icon: XIconNames.web,
title: AppResourceIDs.connect,
color: XColorWithBrightness.Success,
} as XSimpleListItemSlideOption;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\directives\x-focused-provider.directive.ts
import { isNullOrEmptyString, isNullOrUndefined } from 'x-framework-core';
import { Input, Directive, Renderer2, AfterViewInit } from '@angular/core';
@Directive({
selector: '[XFocusedProvider]',
})
export class XFocusedProviderDirective implements AfterViewInit {
//
@Input('XFocusById') focusId?: string;
@Input('XFocusByClass') focusClass?: string;
//
focused: boolean = false;
intervalId: NodeJS.Timeout = undefined;
//
constructor(private renderer: Renderer2) {}
//
ngAfterViewInit(): void {
this.intervalId = setInterval(() => this.handleWorks(), 100);
}
//
private handleWorks() {
//
let target: HTMLElement = undefined;
if (
!isNullOrUndefined(this.intervalId) &&
isNullOrEmptyString(this.focusId) &&
isNullOrEmptyString(this.focusClass)
) {
clearInterval(this.intervalId);
}
//
// Handle Focus By Id ...
if (!isNullOrEmptyString(this.focusId)) {
target = document.getElementById(this.focusId);
}
//
// Handle Focus By Class Name ...
if (!isNullOrEmptyString(this.focusClass) && isNullOrUndefined(target)) {
target = document.querySelector(`.${this.focusClass}`) as HTMLElement;
}
//
// Check Conditions ...
if (!isNullOrUndefined(target) && !this.focused) {
//
target.focus();
this.focused = true;
clearInterval(this.intervalId);
}
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\directives\x-html-include.directive.ts
import {
Input,
OnChanges,
Directive,
ElementRef,
SimpleChanges,
} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { isNullOrEmptyString, keys, nameof, toPromise } from 'x-framework-core';
@Directive({
selector: '[xHTMLInclude]',
})
export class XHTMLIncludeDirective implements OnChanges {
//
@Input('xHTMLInclude')
xHTMLInclude: string;
//
constructor(private element: ElementRef, private http: HttpClient) {}
//
async ngOnChanges(changes: SimpleChanges): Promise<void> {
//
const changeKeys = keys(changes);
const isXHTMLIncludeChanged = changeKeys.includes(
nameof<XHTMLIncludeDirective>('xHTMLInclude')
);
if (isXHTMLIncludeChanged && !isNullOrEmptyString(this.xHTMLInclude)) {
await this.handleLoadingHTML();
}
}
//
async handleLoadingHTML() {
//
console.log('reading: ', this.xHTMLInclude);
const response = await toPromise(
this.http.get(this.xHTMLInclude, { responseType: 'text' })
);
//
this.element.nativeElement.innerHTML = response;
console.log(response);
// //
// fetch(this.xHTMLInclude)
// .then((response) => response.text())
// .then((data) => {
// //
// console.log(data);
// this.element.nativeElement.innerHTML = data;
// });
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\directives\x-insert-view-ref.directive.ts
import { Directive, Input, ViewContainerRef, ViewRef } from '@angular/core';
/**
* Get a ComponentRef<any> instance and Show it
* inside template ...
*/
@Directive({ selector: '[xInsertViewRef]' })
export class XInsertViewRefDirective {
//
@Input()
set xInsertViewRef(val: ViewRef | undefined) {
this.container.clear();
if (val) {
this.container.insert(val);
}
}
//
constructor(private container: ViewContainerRef) {}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\directives\x-on-escape-pressed.global.directive.ts
import { Directive, HostListener, Output, EventEmitter } from '@angular/core';
@Directive({
selector: '[xOnEscapeGlobal]',
})
export class XOnEscapePressedGlobalDirective {
//
@Output('xOnEscapeGlobal')
listener = new EventEmitter<void>();
//
// Listen globally on the document for keydown events
@HostListener('document:keydown.escape', ['$event'])
handleEscape(event: KeyboardEvent): void {
//
event.preventDefault();
this.listener.emit();
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\directives\x-render-component.directive.ts
import {
Type,
Input,
Output,
Directive,
OnChanges,
EventEmitter,
SimpleChanges,
ViewContainerRef,
ChangeDetectorRef,
ComponentFactoryResolver,
} from '@angular/core';
import { hasChild, isNullOrUndefined } from 'x-framework-core';
/**
* a Type for Component Props ...
*/
export type TXRenderComponentProps<T = any> = {
[key in keyof T]?: any;
};
/**
* Describe Specific Component ...
*/
export interface XRenderComponentDescriptor<T = any> {
/**
* Component Type ...
*/
type: Type<T>;
/**
* Component Props ...
*/
inputs: TXRenderComponentProps<T>;
}
/**
* a Directive to Create a Component ...
*/
@Directive({ selector: '[xRenderComponent]' })
export class XRenderComponentDirective implements OnChanges {
//
//#region Props ...
//
@Input('xRenderComponent')
type: Type<any>;
//
@Input('xRenderInputs')
inputs: TXRenderComponentProps<any>;
//
@Output('xOnRendered')
onRendered = new EventEmitter<any>();
//#endregion
//
//#region Constructor ...
constructor(
public changeDtector: ChangeDetectorRef,
public viewContainerRef: ViewContainerRef,
public componentFactoryResolver: ComponentFactoryResolver
) {}
//#endregion
//
//#region LifeCycles ...
ngOnChanges(changes: SimpleChanges): void {
//
let isChanged = false;
let applyChange = false;
//
if (!isNullOrUndefined(this.type) && !isNullOrUndefined(this.inputs)) {
this.handleCreateComponent();
}
}
//#endregion
//
//#region Private ...
private handleCreateComponent() {
//
if (this.viewContainerRef.length > 0) {
return;
}
//
// Clean Container ...
this.viewContainerRef.clear();
//
// Create Component Factory ...
const componentFacotry =
this.componentFactoryResolver.resolveComponentFactory(this.type);
//
// Create Component ...
const componentRef =
this.viewContainerRef.createComponent(componentFacotry);
//
// Set Properties ...
if (!isNullOrUndefined(this.inputs) && !isNullOrUndefined(componentRef)) {
//
const propKeys = Object.keys(this.inputs);
if (hasChild(propKeys)) {
//
propKeys.forEach((key) => {
(componentRef.instance as any)[key] = this.inputs[key];
});
}
}
//
if (!isNullOrUndefined(componentRef)) {
this.onRendered.emit(componentRef.instance);
}
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file.configs.ts
import { XIconNames } from "x-framework-components";
import { XSimplePage } from "../typings/simple.page.typings";
//
//#region Localization Configuration ...
export enum FileResourceIDs {
file = 'file',
files = 'files',
image = 'image',
audio = 'audio',
video = 'video',
document = 'document',
file_name = 'file_name',
thumbnail_path = 'thumbnail_path',
files_page_title = 'files_page_title',
files_page_description = 'files_page_description',
files_management_page_title = 'files_management_page_title',
files_management_page_description = 'files_management_page_description',
files_explorer_page_title = 'files_explorer_page_title',
files_explorer_page_description = 'files_explorer_page_description',
}
//
//#region Signle Resuorces ...
//
//#region file ...
export const file_fa = {
id: 'file',
value: 'فایل',
};
export const file_en = {
id: 'file',
value: 'File',
};
//#endregion
//
//#region files ...
export const files_fa = {
id: 'files',
value: 'فایل ها',
};
export const files_en = {
id: 'files',
value: 'Files',
};
//#endregion
//
//#region image ...
export const image_fa = {
id: 'image',
value: 'تصویر',
};
export const image_en = {
id: 'image',
value: 'Image',
};
//#endregion
//
//#region audio ...
export const audio_fa = {
id: 'audio',
value: 'صوت',
};
export const audio_en = {
id: 'audio',
value: 'Audio',
};
//#endregion
//
//#region video ...
export const video_fa = {
id: 'video',
value: 'ویدیو',
};
export const video_en = {
id: 'video',
value: 'Video',
};
//#endregion
//
//#region document ...
export const document_fa = {
id: 'document',
value: 'سند',
};
export const document_en = {
id: 'document',
value: 'Document',
};
//#endregion
//
//#region file_name ...
export const file_name_fa = {
id: 'file_name',
value: '',
};
export const file_name_en = {
id: 'file_name',
value: '',
};
//#endregion
//
//#region thumbnail_path ...
export const thumbnail_path_fa = {
id: 'thumbnail_path',
value: 'آدرس تصویر بندانگشتی',
};
export const thumbnail_path_en = {
id: 'thumbnail_path',
value: 'Thumbnail Path',
};
//#endregion
//
//#region files_page_title ...
export const files_page_title_fa = {
id: 'files_page_title',
value: 'فایل',
};
export const files_page_title_en = {
id: 'files_page_title',
value: 'File',
};
//#endregion
//
//#region files_page_description ...
export const files_page_description_fa = {
id: 'files_page_description',
value: 'ابزارهای مدیریت فایل ها',
};
export const files_page_description_en = {
id: 'files_page_description',
value: 'Tools for Files',
};
//#endregion
//
//#region files_management_page_title ...
export const files_management_page_title_fa = {
id: 'files_management_page_title',
value: 'مدیریت فایل ها',
};
export const files_management_page_title_en = {
id: 'files_management_page_title',
value: 'Files Management',
};
//#endregion
//
//#region files_management_page_description ...
export const files_management_page_description_fa = {
id: 'files_management_page_description',
value: 'مدیریت فایل ها',
};
export const files_management_page_description_en = {
id: 'files_management_page_description',
value: 'Files Management',
};
//#endregion
//
//#region files_explorer_page_title ...
export const files_explorer_page_title_fa = {
id: 'files_explorer_page_title',
value: 'فایل ها',
};
export const files_explorer_page_title_en = {
id: 'files_explorer_page_title',
value: 'Files',
};
//#endregion
//
//#region files_explorer_page_description ...
export const files_explorer_page_description_fa = {
id: 'files_explorer_page_description',
value: 'مرورگر فایل',
};
export const files_explorer_page_description_en = {
id: 'files_explorer_page_description',
value: 'File Explorer',
};
//#endregion
//#endregion
//
export const FileLocalizationsResourceIDs = Object.assign(FileResourceIDs, {});
//
export const FileLocalizationsResources = {
//
file: {
fa: { ...file_fa },
en: { ...file_en },
},
files: {
fa: { ...files_fa },
en: { ...files_en },
},
image: {
fa: { ...image_fa },
en: { ...image_en },
},
audio: {
fa: { ...audio_fa },
en: { ...audio_en },
},
video: {
fa: { ...video_fa },
en: { ...video_en },
},
document: {
fa: { ...document_fa },
en: { ...document_en },
},
file_name: {
fa: { ...file_name_fa },
en: { ...file_name_en },
},
thumbnail_path: {
fa: { ...thumbnail_path_fa },
en: { ...thumbnail_path_en },
},
files_page_title: {
fa: { ...files_page_title_fa },
en: { ...files_page_title_en },
},
files_page_description: {
fa: { ...files_page_description_fa },
en: { ...files_page_description_en },
},
files_management_page_title: {
fa: { ...files_management_page_title_fa },
en: { ...files_management_page_title_en },
},
files_management_page_description: {
fa: { ...files_management_page_description_fa },
en: { ...files_management_page_description_en },
},
files_explorer_page_title: {
fa: { ...files_explorer_page_title_fa },
en: { ...files_explorer_page_title_en },
},
files_explorer_page_description: {
fa: { ...files_explorer_page_description_fa },
en: { ...files_explorer_page_description_en },
},
};
//#endregion
//
//#region Page Configuration ...
//
export enum FileRoutes {
Default = 'files',
FilesExplorer = 'files-explorer',
FilesManagement = 'files-management',
}
//
export const Files: XSimplePage = {
id: `${FileRoutes.Default}`,
name: `${FileRoutes.Default}`,
title: FileResourceIDs.files_page_title,
description: FileResourceIDs.files_page_description,
baseRoute: `${FileRoutes.Default}`,
route: ['/', `${FileRoutes.Default}`],
icon: XIconNames.collapse,
childs: [
//
//#region Files Management ...
{
id: `${FileRoutes.FilesManagement}`,
name: `${FileRoutes.FilesManagement}`,
title: FileResourceIDs.files_management_page_title,
description: FileResourceIDs.files_management_page_description,
baseRoute: `${FileRoutes.FilesManagement}`,
route: ['/', `${FileRoutes.Default}`, `${FileRoutes.FilesManagement}`],
icon: XIconNames.live_tv,
},
//#endregion
//
//#region Files Explorer ...
{
id: `${FileRoutes.FilesExplorer}`,
name: `${FileRoutes.FilesExplorer}`,
title: FileResourceIDs.files_explorer_page_title,
description: FileResourceIDs.files_explorer_page_description,
baseRoute: `${FileRoutes.FilesExplorer}`,
route: ['/', `${FileRoutes.Default}`, `${FileRoutes.FilesExplorer}`],
icon: XIconNames.live_tv,
},
//#endregion
],
};
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file.tools.ts
import {
hasChild,
XFileType,
isExtensionOk,
isNullOrUndefined,
XResourceIdentifier,
} from 'x-framework-core';
import { XFileDto } from 'x-saherelm-api-sdk';
import { XFileStatus } from 'x-framework-components';
import { FileLocalizationsResourceIDs } from './v-file.configs';
import { AppResourceIDs } from 'src/app/config/localization.config';
/**
* Retrieve Propper Resource ID based on File Type ...
*
* @param type
* @returns
*/
export function getFileTypeResourceID(type?: XFileType) {
//
let result: XResourceIdentifier = AppResourceIDs.unknown;
//
if (!isNullOrUndefined(type)) {
//
switch (type) {
//
// Image Type ...
case XFileType.Image:
case XFileType.CoverImage:
case XFileType.ProfileImage:
result = FileLocalizationsResourceIDs.image;
break;
//
// Audio Type ...
case XFileType.Audio:
result = FileLocalizationsResourceIDs.audio;
break;
//
// Video Type ...
case XFileType.Video:
result = FileLocalizationsResourceIDs.video;
break;
//
// Document Type ...
case XFileType.Document:
result = FileLocalizationsResourceIDs.document;
break;
}
}
//
return result;
}
/**
* Get Specified File Status ...
*
* @param file
* @param allowedExtensions
* @param minAllowedFileSize
* @param maxAllowedFileSize
* @returns
*/
export function getFileStatus(
file: File,
allowedExtensions: Array<string> = [],
minAllowedFileSize: number = 0,
maxAllowedFileSize: number = 0,
) {
//
let result = XFileStatus.EMPTY;
//
let isPassed = false;
//
// Validate File Exists ...
if (isNullOrUndefined(file)) {
return result;
}
//
// Check File Size ...
if (file.size <= 0) {
//
result = XFileStatus.EMPTY;
return result;
}
//
// Check File Extension ...
// only if allowed Extensions Provided ...
if (!isNullOrUndefined(allowedExtensions) && hasChild(allowedExtensions)) {
//
isPassed = isExtensionOk(file, allowedExtensions);
if (!isPassed) {
//
result = XFileStatus.NOT_ALLOWED_TYPE;
return result;
}
}
//
// Check File Min Allowed Size ...
if (minAllowedFileSize > 0) {
//
isPassed = file.size >= minAllowedFileSize;
if (!isPassed) {
//
result = XFileStatus.MIN_SIZE_NOT_REACHED;
return result;
}
}
//
// Check File Max Allowed Size ...
if (maxAllowedFileSize > 0) {
//
isPassed = file.size <= maxAllowedFileSize;
if (!isPassed) {
//
result = XFileStatus.MAX_ALLOWED_SIZE_REACHED;
return result;
}
}
//
// Everything was Passed ...
result = XFileStatus.OK;
//
return result;
}
/**
* Validate Adding New File to a Collection Status ...
*
* @param file
* @param files
* @param maxAllowedFiles
* @returns
*/
export function getCanAddFileStatus(
file: File,
files: Array<XFileDto | File>,
maxAllowedFiles: number = 0,
) {
//
let result = XFileStatus.EMPTY;
//
result = getFileStatus(file);
if (result !== XFileStatus.OK) {
return result;
}
//
if (maxAllowedFiles > 0) {
//
let isPassed = files.length + 1 <= maxAllowedFiles;
if (!isPassed) {
//
result = XFileStatus.MAX_ALLOWED_FILES_REACHED;
return result;
}
}
//
result = XFileStatus.OK;
//
return result;
}
/**
* Validate Adding New File to a Collection Status ...
*
* @param file
* @param filesCount
* @param maxAllowedFiles
* @returns
*/
export function getCanAddFileStatusByFilesCount(
file: File,
filesCount: number = 0,
maxAllowedFiles: number = 0,
) {
//
let result = XFileStatus.EMPTY;
//
result = getFileStatus(file);
if (result !== XFileStatus.OK) {
return result;
}
//
if (maxAllowedFiles > 0) {
//
let isPassed = filesCount + 1 <= maxAllowedFiles;
if (!isPassed) {
//
result = XFileStatus.MAX_ALLOWED_FILES_REACHED;
return result;
}
}
//
result = XFileStatus.OK;
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file\v-file.component.html
<v-dto #dtoPresenter *ngIf="showComponent" [dtoContainer]="this">
<!-- Content Injections -->
<!-- Card Actions -->
<section
actions
*ngIf="
(getValue(wrapWithCard) | async) && (getValue(cardShowActions) | async)
"
>
<ng-content select="[actions]"></ng-content>
</section>
</v-dto>
<!-- -->
<!-- Owner Template -->
<!-- -->
<!-- References Template -->
<ng-template
#referencesRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<ng-container
*ngIf="
(notNullOrUndefinedValue(value) | async) &&
(notNullOrUndefinedValue(getReferencesListItems(value)) | async)
"
>
<x-list
[showEmpty]="true"
class="v-references-list"
[emptyHasRefresh]="false"
[itemTemplate]="referencesListItemRef"
[items]="getReferencesListItems(value)"
[actionProvider]="referencesListActionProvider"
>
</x-list>
</ng-container>
</ng-template>
<!-- References List Item Template -->
<ng-template #referencesListItemRef let-item="item">
<v-reference
[model]="item.data"
[presentType]="referencePresentType"
*ngIf="notNullOrUndefinedValue(item) | async"
></v-reference>
</ng-template>
<!-- Owner Template -->
<ng-template
#ownerRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<v-person
[model]="value"
[showFormResetButton]="false"
[showFormCancelButton]="false"
[showFormActionButton]="false"
[presentType]="ownerPersentType"
*ngIf="notNullOrUndefinedValue(value) | async"
></v-person>
</ng-template>
<!-- AsMedia PresentType Template -->
<ng-template #mediaPresentTypeRef>
<!-- Media Present -->
<ng-container *ngIf="notNullOrUndefinedValue(model) | async">
<v-media
[src]="model"
[presentType]="presentType"
[providedActions]="providedMediaActions"
></v-media>
</ng-container>
</ng-template>
<!-- AsFileUpload -->
<ng-template #fileUploadPresentTypeRef>
<!-- Presenting File Upload -->
<x-file-upload
[color]="fileUploadColor"
[fileDropAreaDisableFileCheck]="true"
[showFileDropArea]="showFileUploadDropArea"
[fileDropAreaIcon]="fileUploadDropAreaIcon"
[maxAllowedSize]="fileUploadMaxAllowedSize"
[fileDropAreaColor]="fileUploadDropAreaColor"
[foregroundColor]="fileUploadForegroundColor"
[maxAllowedFiles]="fileUploadMaxAllowedFiles"
[fileDropAreaMessage]="fileUploadDropAreaMessage"
[allowedExtensions]="fileUploadAllowedExtensions"
(fileChange)="handleFileUploadFileChanges($event)"
[emptyFileException]="fileUploadEmptyFileException"
[minAllowedFileSize]="fileUploadMinAllowedFileSize"
[maxAllowedFileSize]="fileUploadMaxAllowedFileSize"
[actionBarHideActions]="fileUploadActionBarHideActions"
[notAllowedFileException]="fileUploadNotAllowedFileException"
[maxAllowedSizeException]="fileUploadMaxAllowedSizeException"
[maxAllowedFilesException]="fileUploadMaxAllowedFilesException"
(actionBarActionFired)="handleFileUploadActionBarActionFired($event)"
[minAllowedFileSizeException]="fileUploadMinAllowedFileSizeException"
[maxAllowedFileSizeException]="fileUploadMaxAllowedFileSizeException"
></x-file-upload>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file\v-file.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file\v-file.component.ts
import {
Input,
Output,
Component,
ViewChild,
TemplateRef,
EventEmitter,
ChangeDetectionStrategy,
} from '@angular/core';
import {
getFileStatus,
getFileTypeResourceID,
getCanAddFileStatusByFilesCount,
} from '../v-file.tools';
import {
toArray,
XFileType,
XFileBlob,
XExceptionIDs,
getFieldNames,
nameofInstance,
throwException,
XOneOrManyType,
XColorIdentifier,
isNullOrUndefined,
getErrorException,
isNullOrEmptyString,
XResourceIdentifier,
XExceptionIdentifier,
} from 'x-framework-core';
import {
XFileDto,
XPersonDto,
XReferenceDto,
defaultFileDto,
prepareFileDtoFields,
} from 'x-saherelm-api-sdk';
import {
VFilePresentType,
isVFilePresentType,
VFilePresentTypeIdentifier,
} from './v-file.typings';
import { Subject } from 'rxjs';
import {
XFileStatus,
XListAction,
XIconIdentifier,
XListActionModel,
XActionBarActionFiredModel,
XFormDatePickerControlConfig,
XFormDatePickerControlPickerPosition,
} from 'x-framework-components';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import {
PersonPresentType,
isPersonPresentType,
PersonPresentTypeIdentifier,
} from '../../v-person/v-person.typings';
import { Validators } from '@angular/forms';
import {
defaultEditSlideOption,
defaultDownloadSlideOption,
} from '../../constants/slide.options.defaults';
import {
XSimpleListItem,
XSimpleListItemSlideOption,
} from '../../typings/simple.list-item.typings';
import {
VReferencePresentType,
isVReferencePresentType,
VReferencePresentTypeIdentifier,
} from '../../v-reference/v-reference.typings';
import {
VTagsPresenterActionModel,
VTagsPresenterModelState,
VTagsPresenterPresentType,
isVTagsPresenterPeresentType,
VTagsPresenterPresentTypeIdentfier,
} from '../../tags/v-tags-presenter/v-tags-presenter.typings';
@Component({
selector: 'v-file',
templateUrl: './v-file.component.html',
styleUrls: ['./v-file.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VFileComponent extends VBaseDtoComponent<XFileDto> {
//
//#region Props ...
readonly defaultModel: XFileDto = {
...defaultFileDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
/**
* Override Present Type to Handle Custom Types and also Prevent Form Type ...
*/
@Input()
presentType: VFilePresentTypeIdentifier = VFilePresentType.AsView;
/**
* Default Model Field Descriptors ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.id,
label: this.ResourceIDs.id,
value: this.defaultModel.id,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.type,
label: this.ResourceIDs.type,
value: this.defaultModel.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(
this.resourceProvider(getFileTypeResourceID(value)),
);
},
} as VModelFieldDescriptor<XFileType>,
//
// Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.name,
value: this.defaultModel.name,
label: this.AppResourceIDs.name,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Thumb ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.thumb,
value: this.defaultModel.thumb,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.thumbnail,
} as VModelFieldDescriptor<string>,
//
// Path ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.path,
value: this.defaultModel.path,
label: this.AppResourceIDs.path,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// ThumbPath ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.thumbPath,
type: this.ModelFieldTypes.String,
value: this.defaultModel.thumbPath,
label: this.AppResourceIDs.thumbnail_path,
} as VModelFieldDescriptor<string>,
//
// UploadedOn ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.uploadedOn,
type: this.ModelFieldTypes.Date,
value: this.defaultModel.uploadedOn,
label: this.AppResourceIDs.uploaded_on,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
formControlConfig: {
index: 2,
propName: this.FieldNames.uploadedOn,
type: {
type: this.FormControlTypes.Date,
config: {
iconColor: this.ColorNames.Tertiary,
toggleIcon: this.IconNames.calendar,
applyStateColor: true,
datePickerPosition: XFormDatePickerControlPickerPosition.Prefix,
} as XFormDatePickerControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.uploaded_on,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.calendar,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<Date>,
//
// FileName ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.fileName,
value: this.defaultModel.fileName,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.file_name,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<string>,
//
// Owner ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.ownerId,
value: this.defaultModel.ownerId,
type: this.ModelFieldTypes.String,
label: this.AppResourceIDs.author_id,
} as VModelFieldDescriptor<string>,
//
// Owner ...
{
hide: false,
hideLabel: false,
showContentBelowLabel: true,
key: this.FieldNames.owner,
value: this.defaultModel.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
hasCustomTemplate: true,
} as VModelFieldDescriptor<XPersonDto>,
//
// References ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.references,
value: this.defaultModel.references,
label: this.AppResourceIDs.references,
type: this.ModelFieldTypes.Array,
showContentBelowLabel: true,
pushArrayChildsOnceToTemplate: true,
hasCustomTemplate: true,
} as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
@ViewChild('ownerRef')
ownerRef!: TemplateRef<any>;
/**
* Owner Present Type ...
*/
@Input()
ownerPersentType: PersonPresentTypeIdentifier = PersonPresentType.AsCard;
@ViewChild('referencesRef')
referencesRef!: TemplateRef<any>;
/**
* References List Action Provider ...
*/
referencesListActionProvider = new Subject<XListActionModel>();
/**
* Specifie Reference Present Type ...
*/
@Input()
referencePresentType: VReferencePresentTypeIdentifier =
VReferencePresentType.AsView;
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleAttachTemplates();
// this.handleCanAttachTagState();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// PresentType ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (
isNullOrEmptyString(this.presentType) ||
!isVFilePresentType(this.presentType)
) {
//
applyChanges = true;
this.presentType = VFilePresentType.AsView;
}
}
//
// Owner Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'ownerPersentType'));
if (isChanged) {
//
if (!isPersonPresentType(this.ownerPersentType)) {
//
applyChanges = true;
this.ownerPersentType = PersonPresentType.AsCard;
}
}
//
// Reference Present Type ...
isChanged = changeKeys.includes(
nameofInstance(this, 'referencePresentType'),
);
if (isChanged) {
//
if (!isVReferencePresentType(this.referencePresentType)) {
//
applyChanges = true;
this.referencePresentType = VReferencePresentType.AsView;
}
}
//
if (applyChanges) {
this.detectChanges();
}
//
this.handleAttachTemplates();
}
//#endregion
//
//#region AsMedia ...
@ViewChild('mediaPresentTypeRef')
mediaPresentTypeRef!: TemplateRef<any>;
/**
* Provided Actions which Used only in Card Present Type ...
*/
@Input()
providedMediaActions: Array<XSimpleListItemSlideOption> = [
//
// Download ...
{
...defaultDownloadSlideOption,
onlyIcon: false,
handler: (info) => {
console.log('handle remove: ', info);
},
},
//
// Edit ...
{
...defaultEditSlideOption,
onlyIcon: false,
},
];
//#endregion
//
//#region AsFileUpload ...
/**
* File Upload Present Type Templat ...
*/
@ViewChild('fileUploadPresentTypeRef')
fileUploadPresentTypeRef!: TemplateRef<any>;
/**
* File Upload Color Handler ...
*/
@Input()
fileUploadColor: XColorIdentifier = this.ColorNames.Light;
/**
* File Upload Foreground Color ...
*/
@Input()
fileUploadForegroundColor: boolean = false;
/**
* number of files
*/
@Input()
fileUploadFilesCount: number;
//
//#region File Upload Drop Area ...
@Input()
showFileUploadDropArea: boolean = true;
@Input()
fileUploadDropAreaColor: XColorIdentifier = this.ColorNames.Light;
@Input()
fileUploadDropAreaIcon: XIconIdentifier = this.IconNames.arrow_dropdown;
@Input()
fileUploadDropAreaMessage: XResourceIdentifier =
this.ResourceIDs.drag_drop_here;
@Input()
fileUploadActionBarHideActions: Array<string> = [];
//#endregion
//
//#region File Upload Property / Validations ...
@Input()
fileUploadEmptyFileException: XExceptionIdentifier;
@Input()
fileUploadNotAllowedFileException: XExceptionIdentifier;
@Input()
fileUploadMaxAllowedSizeException: XExceptionIdentifier;
@Input()
fileUploadMaxAllowedFilesException: XExceptionIdentifier;
@Input()
fileUploadMinAllowedFileSizeException: XExceptionIdentifier;
@Input()
fileUploadMaxAllowedFileSizeException: XExceptionIdentifier;
@Input()
fileUploadAllowedExtensions: Array<string> = [];
@Input()
fileUploadMaxAllowedFiles: number = this.config.maxUploadFiles;
@Input()
fileUploadMaxAllowedSize: number = this.config.maxAllowedSize;
@Input()
fileUploadMinAllowedFileSize: number = this.config.minAllowedFileSize;
@Input()
fileUploadMaxAllowedFileSize: number = this.config.maxAllowedFileSize;
//#endregion
//
fileUploadFiles: Array<File> = [];
//
@Output()
fileUploadFilesChanged = new EventEmitter<Array<File>>();
//
@Output()
fileUploadActionFired = new EventEmitter<XActionBarActionFiredModel>();
/**
* Handle File Upload Files Changed ...
*
* @param event
*/
async handleFileUploadFileChanges(
event: XOneOrManyType<File | XFileBlob | any>,
) {
//
// Validate ...
if (isNullOrUndefined(event)) {
return;
}
//
// Here we have to do File Checking ...
const recieveds = toArray(event);
if (this.hasChild(recieveds)) {
//
// Here we Have to Validate File and Files ...
const filteredFiles = [...recieveds.filter((r) => r instanceof File)];
if (this.hasChild(filteredFiles)) {
//
for (let file of filteredFiles) {
//
const state = getFileStatus(
file,
this.fileUploadAllowedExtensions,
this.fileUploadMinAllowedFileSize,
this.fileUploadMaxAllowedFileSize,
);
//
// Check File State Valids ...
let isValid = state === XFileStatus.OK;
if (!isValid) {
//
let execptionId = this.getFileStatusExceptionId(state);
const exception = getErrorException(execptionId);
await this.managerService.notificationService.presentErrorNotification(
{
exception,
dissmissable: true,
},
);
throwException(exception);
}
//
isValid =
getCanAddFileStatusByFilesCount(
file,
this.fileUploadFilesCount,
this.fileUploadMaxAllowedFiles,
) === XFileStatus.OK;
if (!isValid) {
//
let execptionId = this.getFileStatusExceptionId(state);
const exception = getErrorException(execptionId);
await this.managerService.notificationService.presentErrorNotification(
{
exception,
dissmissable: true,
},
);
throwException(exception);
}
//
this.fileUploadFiles.push(file);
}
} else {
this.fileUploadFiles = [];
}
} else {
this.fileUploadFiles = [];
}
//
// Notify Locking ...
this.lockNotifier.next({
id: this.uuid,
state: this.hasChild(this.fileUploadFiles),
});
//
// Notify Changes ...
this.fileUploadFilesChanged.emit(this.fileUploadFiles);
}
/**
* Handle File Upload Action Fired ...
* @param event
*/
handleFileUploadActionBarActionFired(event: XActionBarActionFiredModel) {
//
// Validate ...
if (isNullOrUndefined(event) || isNullOrEmptyString(event.action)) {
return;
}
//
this.fileUploadActionFired.emit(event);
}
//#endregion
//
//#region Providers ...
referencesListItems: Array<XSimpleListItem<any>> = [];
getReferencesListItems(references: Array<any>) {
//
if (this.hasChild(this.referencesListItems)) {
return this.referencesListItems;
}
//
this.referencesListItems = [
...references.map((r) => {
return {
data: { ...r },
} as XSimpleListItem<any>;
}),
];
//
this.referencesListActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() =>
this.referencesListActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() =>
this.referencesListActionProvider.next({ action: XListAction.Refresh }),
500,
);
//
return this.referencesListItems;
}
/**
* Select Custom Present Types Template ...
*
* @param presentType
* @returns
*/
getCustomPresentTypeTemplate(presentType: string): TemplateRef<any> {
//
if (
(presentType === VFilePresentType.AsItem ||
presentType === VFilePresentType.AsCard ||
presentType === VFilePresentType.AsMedia ||
presentType === VFilePresentType.AsThumbnail) &&
!isNullOrUndefined(this.mediaPresentTypeRef)
) {
return this.mediaPresentTypeRef;
}
//
if (
presentType === VFilePresentType.AsFileUpload &&
!isNullOrUndefined(this.fileUploadPresentTypeRef)
) {
return this.fileUploadPresentTypeRef;
}
//
return null;
}
//#endregion
//
//#region Abstraction ...
/**
* Prepare Model ...
*
* @param model
*/
prepare(model?: XFileDto): XFileDto {
return prepareFileDtoFields(model);
}
/**
* Validate Model ...
*
* @param model
*/
validate(model?: Partial<XFileDto>): VModelValidationResult<XFileDto> {
//
let result: VModelValidationResult<XFileDto> = undefined;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// ID ...
//
// Type ...
isValid = !isNullOrUndefined(normalized.type);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.type,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Name ...
isValid = !isNullOrEmptyString(normalized.name);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.name,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Path ...
isValid = !isNullOrEmptyString(normalized.path);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.path,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// UploadedOn ...
isValid = !isNullOrUndefined(normalized.uploadedOn);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.uploaded_on,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd = isNullOrEmptyString(normalized.id);
const isValidForUpdate = !isNullOrEmptyString(normalized.id);
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
//#endregion
//
//#region Private ...
/**
* Extract Customized ExceptionIdentifier based on file Status ...
* @param state
* @returns
*/
private getFileStatusExceptionId(state: XFileStatus) {
//
let result: XExceptionIdentifier = undefined;
//
switch (state) {
//
case XFileStatus.EMPTY:
result = this.fileUploadEmptyFileException;
break;
//
case XFileStatus.NOT_ALLOWED_TYPE:
result = this.fileUploadNotAllowedFileException;
break;
//
case XFileStatus.MIN_SIZE_NOT_REACHED:
result = this.fileUploadMinAllowedFileSizeException;
break;
//
case XFileStatus.MAX_ALLOWED_SIZE_REACHED:
result = this.fileUploadMaxAllowedFileSizeException;
break;
//
case XFileStatus.MAX_ALLOWED_FILES_REACHED:
result = this.fileUploadMaxAllowedFilesException;
break;
//
case XFileStatus.SIZE_EXCEEDED:
result = this.fileUploadMaxAllowedSizeException;
break;
//
}
//
return result;
}
/**
* Handle Attach Templates to Field Descriptors ...
*
* @returns
*/
private handleAttachTemplates() {
//
let applyChanges = false;
//
// Check Owner Field Has Template or not ...
const hasOwnerTemplate = !isNullOrUndefined(
this.fieldDescriptors.find(
(fd) =>
fd.key === this.FieldNames.owner &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
//
// Check References Field Has Template or not ...
const hasReferencesTemplate = !isNullOrUndefined(
this.fieldDescriptors.find(
(fd) =>
fd.key === this.FieldNames.references &&
!!fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template),
),
);
//
// if all required fields has template ....
if (hasOwnerTemplate && hasReferencesTemplate) {
return;
}
//
// Set Owner Template ...
if (!hasOwnerTemplate && !isNullOrUndefined(this.ownerRef)) {
//
applyChanges = true;
//
this.defaultFieldDescriptors = this.fieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.owner &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.ownerRef
: fd.template,
};
});
//
if (!isNullOrUndefined(this.providedFieldDescriptors)) {
//
this.providedFieldDescriptors = this.providedFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.owner &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.ownerRef
: fd.template,
};
},
);
}
}
//
// Set References Template ...
if (!hasReferencesTemplate && !isNullOrUndefined(this.referencesRef)) {
//
applyChanges = true;
//
this.defaultFieldDescriptors = this.fieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.references &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.referencesRef
: fd.template,
};
});
//
if (!isNullOrUndefined(this.providedFieldDescriptors)) {
//
this.providedFieldDescriptors = this.providedFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.references &&
!!fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.referencesRef
: fd.template,
};
},
);
}
}
//
if (applyChanges) {
//
this.resetFieldDescriptors();
this.detectChanges();
}
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file\v-file.typings.ts
import { isValueInEnum } from '../../tools/v-base.tools';
//
//#region Present Type ...
export enum VFilePresentType {
//
AsNone = 'as_none',
AsView = 'as_view',
AsItem = 'as_item',
AsCard = 'as_card',
AsMedia = 'as_media',
AsThumbnail = 'as_thumbnail',
AsFileUpload = 'as_fileupload',
}
export type VFilePresentTypeIdentifier = VFilePresentType | string;
export function isVFilePresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VFilePresentType));
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file-input\v-file-input.component.html
<section
#vBaseDialogContainer
class="v-base-dialog"
[isForeground]="false"
[xColor]="getValue(backgroundColor) | async"
>
<!-- Title Toolbar -->
<section #vBaseDialogToolbar>
<!-- Toolbar -->
<ion-toolbar
#vToolbar
class="v-base-dialog-toolbar"
[xColor]="getValue(toolbarColor) | async"
>
<!-- Slots -->
<div class="v-base-dialog-toolbar-area">
<x-slotter>
<!-- Start -->
<x-slot
[name]="SlotNames.START"
[cssClass]="['ion-text-start', 'ion-float-start']"
>
<div
matRipple
(click)="handleBackPressed()"
[matTooltip]="getResourceValue(ResourceIDs.back) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="backIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
<!-- Center -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<ion-title class="v-base-dialog-toolbar-item center-vertical-flex">
<h3>{{ getToolbarTitle() }}</h3>
</ion-title>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.END"
[cssClass]="['ion-text-end', 'ion-float-end']"
>
<div
matRipple
(click)="handleSelectPressed()"
[matTooltip]="getResourceValue(ResourceIDs.select) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="selectIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
</x-slotter>
</div>
</ion-toolbar>
<!-- Progressbar -->
<ion-progress-bar
type="indeterminate"
*ngIf="getValue(showProgrssbar) | async"
[color]="getValue(progressbarColor) | async"
></ion-progress-bar>
</section>
<!-- Components -->
<section class="v-base-dialog-content">
<!-- Content -->
<ng-container *ngTemplateOutlet="contentRef"></ng-container>
</section>
</section>
<!-- -->
<!-- Template -->
<!-- -->
<!-- Content Template -->
<ng-template #contentRef>
<div class="v-container v-file-container">
<v-file
#vFile
[isInPage]="true"
[model]="fileModel"
[wrapWithCard]="false"
[presentType]="filePresentType"
[actionProvider]="fileActionProvider"
[fileUploadFilesCount]="originalFiles.length"
(fileUploadFilesChanged)="handleFileUploadFileChanges($event)"
[fileUploadActionBarHideActions]="fileUploadActionBarHideActions"
></v-file>
</div>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file-input\v-file-input.component.scss
@import "../../../../theme/theme.scss";
:host {
--dialog-toolbar-height: 0px;
--background: var(--x-color-light-shade);
}
::ng-deep.fullscreen {
--width: 100% !important;
--height: 100% !important;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 !important;
}
.v-base-dialog {
width: 100%;
height: 100%;
// height: calc(100% - var(--dialog-toolbar-height));
// margin-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-content {
height: 100%;
//
padding-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-toolbar-area {
margin: 8px;
}
.v-base-dialog-toolbar-item {
width: auto;
height: 48px;
}
.v-base-dialog-toolbar-button {
width: 32px;
margin: {
left: 4px;
right: 4px;
}
}
.v-webinar-screen {
padding: 16px;
}
//
// Non Global ...
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
.v-container {
height: 100%;
padding: 16px;
overflow: scroll;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file-input\v-file-input.component.ts
import {
Input,
Component,
ViewChild,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
isSameArray,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import { Subject } from 'rxjs';
import {
XSlotName,
XIconNames,
XSlotLayout,
XSlotterActionModel,
DefaultActionBarItemIds,
} from 'x-framework-components';
import {
VFileInputFilePrperties,
VFileInputModalContext,
VFileInputModalContextIdentifier,
VFileInputReturnActionIdentifier,
} from './v-file-input.typings';
import {
VBaseDtoAction,
VBaseDtoActionModel,
VBaseDtoPresentType,
} from '../../v-dto/v-dto.typings';
import { VFilePresentType } from '../v-file/v-file.typings';
import { VFileComponent } from '../v-file/v-file.component';
import { defaultFileDto, XFileDto } from 'x-saherelm-api-sdk';
import { VBaseComponent } from '../../classes/v-base.component';
@Component({
selector: 'v-file-input',
templateUrl: './v-file-input.component.html',
styleUrls: ['./v-file-input.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VFileInputComponent extends VBaseComponent {
//
//#region Properties ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
/**
* retrieve Toolbar container ViewChild
* to Calculate Toolbar Height ...
*/
@ViewChild('vBaseDialogToolbar')
vBaseDialogToolbarContainer!: ElementRef;
@ViewChild('vFile')
vFile!: VFileComponent;
/**
* Specified Modal Type ...
*/
@Input()
context: VFileInputModalContextIdentifier;
@Input()
originalFiles: Array<XFileDto | File> = [];
editedFiles: Array<XFileDto | File> = [];
@Input()
fileProperties: VFileInputFilePrperties;
//
fileModel: XFileDto = {
...defaultFileDto,
};
filePresentType = VFilePresentType.AsFileUpload;
fileUploadActionBarHideActions: Array<string> = [
DefaultActionBarItemIds.Upload,
];
fileActionProvider = new Subject<VBaseDtoActionModel>();
@Input()
presentType: VBaseDtoPresentType;
/**
* Background Color of Component ...
*/
@Input()
backgroundColor: XColorIdentifier = XColorWithBrightness.Light;
@Input()
toolbarTitle: XResourceIdentifier = '';
/**
* Toolbar Color ...
*/
@Input()
toolbarColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
/**
* Progressbar Color ...
*/
@Input()
progressbarColor: XColorIdentifier = XColorWithBrightness.Warning;
showProgrssbar = false;
/**
* Action Icon Names Specifiecations ...
*/
@Input()
cancelIcon = XIconNames.close;
@Input()
backIcon = XIconNames.arrow_back;
@Input()
selectIcon = XIconNames.checked;
//
slotterActionProvider = new Subject<XSlotterActionModel>();
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleFVFilePropertiesSet();
this.fileActionProvider.next({ action: VBaseDtoAction.Refresh });
}
//#endregion
//
//#region Provider ...
getToolbarTitle() {
//
let result = '';
//
if (!isNullOrUndefined(this.toolbarTitle)) {
result = this.resourceProvider(this.toolbarTitle);
}
//
return result;
}
//#endregion
//
//#region Handlers ...
/**
* Handle File Upload Files Changed ...
*
* @param event
*/
handleFileUploadFileChanges(event: Array<File>) {
this.editedFiles = [...event];
}
/**
* Handle Select Pressed ...
*
* @returns
*/
async handleSelectPressed() {
//
if (
isNullOrUndefined(this.editedFiles) ||
isSameArray(this.originalFiles, this.editedFiles)
) {
return;
}
//
await this.handleCloseDialog([...this.editedFiles]);
}
/**
* Use for Handle Modela Closing Action based on Context ...
* @returns
*/
async handleBackPressed(action?: VFileInputReturnActionIdentifier) {
//
if (
this.loading ||
this.uiDisabled ||
!isNullOrUndefined(this.error) ||
isNullOrUndefined(this.context) ||
isNullOrEmptyString(this.context)
) {
return;
}
//
await this.handleCloseDialog(action);
}
/**
* Use for Handle Modal Closing Action based on Context ...
*
* @returns
*/
private async handleCloseDialog(
action?: VFileInputReturnActionIdentifier | any,
) {
//
if (isNullOrUndefined(this.context) || isNullOrEmptyString(this.context)) {
return;
}
//
switch (this.context) {
//
case VFileInputModalContext.Modal:
await this.managerService.dialogService.modalController.dismiss(action);
break;
//
case VFileInputModalContext.PopOver:
await this.managerService.dialogService.popoverController.dismiss(
action,
);
break;
}
}
/**
* Handle Set File Properties ...
*
* @returns
*/
private handleFVFilePropertiesSet() {
//
// Validate ...
if (
isNullOrUndefined(this.vFile) ||
isNullOrUndefined(this.fileProperties)
) {
return;
}
//
let isChanged = false;
Object.keys(this.fileProperties).forEach((key) => {
//
const value = (this.fileProperties as any)[key];
if (!isNullOrUndefined(value)) {
//
isChanged = true;
(this.vFile as any)[key] = value;
}
});
//
if (isChanged) {
//
this.vFile.detectChanges();
this.fileActionProvider.next({ action: VBaseDtoAction.Refresh });
}
//
this.detectChanges();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-file-input\v-file-input.typings.ts
import { VFileComponent } from '../v-file/v-file.component';
export enum VFileInputModalContext {
Modal = 'modal',
PopOver = 'pop_over',
}
export type VFileInputModalContextIdentifier = VFileInputModalContext | string;
export enum VFileInputCssVar {
DialogToolbarHeight = 'dialog-toolbar-height',
}
export enum VFileInputReturnAction {}
export type VFileInputReturnActionIdentifier = VFileInputReturnAction | string;
export type VFileInputFilePrperties = Pick<
Partial<VFileComponent>,
| 'fileUploadAllowedExtensions'
| 'fileUploadColor'
| 'fileUploadDropAreaColor'
| 'fileUploadDropAreaIcon'
| 'fileUploadDropAreaMessage'
| 'fileUploadForegroundColor'
| 'fileUploadMaxAllowedFileSize'
| 'fileUploadMaxAllowedFiles'
| 'fileUploadMaxAllowedSize'
| 'fileUploadMinAllowedFileSize'
| 'fileUploadEmptyFileException'
| 'fileUploadNotAllowedFileException'
| 'fileUploadMaxAllowedSizeException'
| 'fileUploadMaxAllowedFilesException'
| 'fileUploadMaxAllowedFileSizeException'
| 'fileUploadMinAllowedFileSizeException'
>;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-files-presenter\v-files-presenter.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[loading]="loading"
[uiDisabled]="uiDisabled"
[class.v-no-margin-card]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
class="v-card v-tags-card v-component-card"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
*ngIf="canPresent()"
[ngClass]="getArrayValue(cssClass) | async"
class="v-tags-container v-component-wrapper unselecteable"
>
<!-- Title -->
<ng-container *ngIf="showLabel && notNullOrUndefinedValue(title) | async">
<ng-container *ngTemplateOutlet="titleRef"></ng-container>
</ng-container>
<!-- Attach -->
<ng-container *ngIf="canAttach">
<ng-container *ngTemplateOutlet="fileAttachRef"></ng-container>
</ng-container>
<!-- Handle Different Present Types -->
<ng-container
*ngTemplateOutlet="
presentType === FilesPresenterPresentTypes.AsList
? asListPresentTypeRef
: presentType === FilesPresenterPresentTypes.AsMedias
? asMediasPresentTypeRef
: presentType === FilesPresenterPresentTypes.AsThumbnails
? asThumbnailsPresentTypeRef
: nothingPresentRef
"
></ng-container>
</section>
</ng-template>
<!-- -->
<!-- Present Templates -->
<!-- -->
<!-- Nothing Template -->
<ng-template #nothingPresentRef></ng-template>
<!-- AsList Template -->
<ng-template #asListPresentTypeRef>
<section
class="v-files-as-list-wrapper v-present-type-wrapper"
*ngIf="
(hasChildValue(files) | async) && (hasChildValue(filesListItems) | async)
"
>
<x-list
[loading]="loading"
[items]="filesListItems"
[color]="fileBackgroundColor"
[itemTemplate]="listItemTemplate"
[uiDisabled]="loading || uiDisabled"
[actionProvider]="listActionProvider"
[enableReordering]="filesListItems.length > 1"
(orderChanged)="handleListItemOrdersChanged()"
(optionSelected)="handleListItemsOptionSelected($event)"
></x-list>
</section>
</ng-template>
<!-- AsMedias Template -->
<ng-template #asMediasPresentTypeRef>As Medias</ng-template>
<!-- AsThumbnails Template -->
<ng-template #asThumbnailsPresentTypeRef>As Thumbnails</ng-template>
<!-- -->
<!-- Other Templates -->
<!-- -->
<!-- Title Template -->
<ng-template #titleRef>
<span
class="v-files-label"
[isForeground]="true"
[xColor]="titleColor"
*ngIf="showLabel && notNullOrUndefinedValue(title) | async"
>
{{ getResourceValue(title) | async }}
</span>
</ng-template>
<!-- Attach Ref -->
<ng-template #fileAttachRef>
<x-action-bar [actions]="actionBarItems"></x-action-bar>
</ng-template>
<!-- List Item Template -->
<ng-template #listItemTemplate let-item="item">
<section class="v-file-list-item">
<ng-container
*ngTemplateOutlet="fileRef; context: { file: item.data }"
></ng-container>
</section>
</ng-template>
<!-- File Ref -->
<ng-template #fileRef let-file="file">
<!-- Present File as Item -->
<v-media
[src]="file"
[presentType]="FilePresentTypes.AsItem"
*ngIf="notNullOrUndefinedValue(file) | async"
></v-media>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-files-presenter\v-files-presenter.component.scss
.v-files-label {
//
&::after {
content: ":";
}
//
padding: {
top: 3px;
bottom: 3px;
}
}
.v-component-wrapper {
padding: {
bottom: 6px;
}
}
.v-present-type-wrapper {
padding: {
top: 6px;
bottom: 6px;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-files-presenter\v-files-presenter.component.ts
import {
Input,
Output,
Component,
EventEmitter,
ChangeDetectionStrategy,
} from '@angular/core';
import {
isNumber,
toNumber,
isFunction,
nameofInstance,
XColorIdentifier,
isNullOrUndefined,
XResourceIdentifier,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XListItem,
XListAction,
XListActionModel,
XActionBarItemSlot,
XListItemOptionSelectedEvent,
} from 'x-framework-components';
import {
VFilesPresenterAction,
isVFilesPresenterAction,
VFilesPresenterModelState,
VFilesPresenterActionModel,
VFilesPresenterPresentType,
isVFilesPresenterPeresentType,
VFilesPresenterPresentTypeIdentfier,
} from './v-files-presenter.typings';
import { Observable, Subject } from 'rxjs';
import {
VFileInputFilePrperties,
VFileInputModalContext,
} from '../v-file-input/v-file-input.typings';
import { isXFileDto, XFileDto } from 'x-saherelm-api-sdk';
import { VFilePresentType } from '../v-file/v-file.typings';
import { VBaseInCardComponent } from '../../classes/v-base.component';
import { VFileInputComponent } from '../v-file-input/v-file-input.component';
import { XSimpleActionBarItem } from '../../typings/simple.actionbar.typings';
import { defaultRemoveSlideOption } from '../../constants/slide.options.defaults';
import { XSimpleListItemSlideOption } from '../../typings/simple.list-item.typings';
@Component({
selector: 'v-files-presenter',
templateUrl: './v-files-presenter.component.html',
styleUrls: ['./v-files-presenter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VFilesPresenterComponent extends VBaseInCardComponent {
//
//#region Props ...
readonly FilesPresenterPresentTypes = Object.assign(
{},
VFilesPresenterPresentType,
);
readonly FilePresentTypes = Object.assign({}, VFilePresentType);
@Input()
showLabel: boolean = true;
@Input()
cssClass: Array<string> = [];
@Input()
title: XResourceIdentifier = this.AppResourceIDs.files;
@Input()
titleColor: XColorIdentifier = this.ColorNames.Tertiary;
@Input()
presentType: VFilesPresenterPresentTypeIdentfier =
VFilesPresenterPresentType.AsList;
@Input()
fileBackgroundColor: XColorIdentifier = this.ColorNames.LightShade;
@Input()
filesReader: Observable<Array<XFileDto>> = undefined;
@Input()
fileProperties: VFileInputFilePrperties;
@Input()
fileActionsProvider: (
file: XFileDto | File,
) => Array<XSimpleListItemSlideOption> = (f) => {
return [
//
// Remove ...
{
...defaultRemoveSlideOption,
icon: this.IconNames.clear,
},
];
};
@Input()
actionProvider = new Subject<VFilesPresenterActionModel>();
@Input()
canAttach: boolean = false;
actionBarItems: Array<XSimpleActionBarItem> = [];
/**
* Model State Changed Notifier ...
*/
@Output()
modelStateChanged = new EventEmitter<VFilesPresenterModelState>();
//
files: Array<XFileDto | File> = [];
originalFiles: Array<XFileDto> = [];
listActionProvider = new Subject<XListActionModel>();
filesListItems: Array<XListItem<XFileDto | File>> = [];
//#endregion
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
super.afterViewInit();
//
await this.handleReadingFiles();
this.handleRegisterActionProvider();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Files Reader ...
isChanged = changeKeys.includes(nameofInstance(this, 'filesReader'));
if (isChanged) {
//
if (!isNullOrUndefined(this.filesReader)) {
await this.handleReadingFiles();
}
}
//
// Can Attach ...
isChanged = changeKeys.includes(nameofInstance(this, 'canAttach'));
if (isChanged) {
//
if (this.canAttach) {
//
applyChanges = true;
this.prepareActionBarItems();
}
}
//
// Tag Action Provider ...
isChanged = changeKeys.includes(
nameofInstance(this, 'fileActionsProvider'),
);
if (isChanged) {
//
if (
isNullOrUndefined(this.fileActionsProvider) ||
!isFunction(this.fileActionsProvider)
) {
//
applyChanges = true;
this.fileActionsProvider = (f) => {
return [
//
// Remove ...
{
...defaultRemoveSlideOption,
icon: this.IconNames.clear,
},
];
};
}
}
//
// Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (
isNullOrUndefined(this.presentType) &&
!isVFilesPresenterPeresentType(this.presentType)
) {
this.presentType = VFilesPresenterPresentType.AsList;
}
//
await this.handlePresentTypeChanged();
}
//
// Action Provider ...
isChanged = changeKeys.includes(nameofInstance(this, 'actionProvider'));
if (isChanged) {
//
if (isNullOrUndefined(this.actionProvider)) {
//
applyChanges = true;
this.actionProvider = new Subject<VFilesPresenterActionModel>();
}
//
this.handleRegisterActionProvider();
}
//
// Title Color ...
isChanged = changeKeys.includes(nameofInstance(this, 'titleColor'));
if (isChanged) {
//
if (isNullOrEmptyString(this.titleColor)) {
//
applyChanges = true;
this.titleColor = this.ColorNames.Tertiary;
}
}
//
// File Background Color ...
isChanged = changeKeys.includes(
nameofInstance(this, 'fileBackgroundColor'),
);
if (isChanged) {
//
if (isNullOrEmptyString(this.fileBackgroundColor)) {
//
applyChanges = true;
this.fileBackgroundColor = this.ColorNames.LightShade;
}
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region Providers ...
/**
* Check Can Present Files or not ...
*
* @returns
*/
canPresent() {
return (
!isNullOrUndefined(this.presentType) &&
isVFilesPresenterPeresentType(this.presentType)
);
}
/**
* Check two args are same or not ...
*
* @param arg1
* @param arg2
* @returns
*/
isSame(arg1: XFileDto | File, arg2: XFileDto | File) {
//
let result = false;
//
// Validate ...
result = !isNullOrUndefined(arg1) || !isNullOrUndefined(arg2);
if (!result) {
return result;
}
//
// Validate Type ...
const isArg1FileDto = isXFileDto(arg1);
const isArg2FileDto = isXFileDto(arg2);
result =
(isArg1FileDto && isArg2FileDto) || (!isArg1FileDto && !isArg2FileDto);
if (!result) {
return result;
}
//
// Check Same ...
if (isArg1FileDto && isArg2FileDto) {
//
const arg1Dto = arg1 as XFileDto;
const arg2Dto = arg2 as XFileDto;
//
result = arg1Dto.id === arg2Dto.id;
} else if (!isArg1FileDto && !isArg2FileDto) {
//
const arg1File = arg1 as File;
const arg2File = arg2 as File;
//
result = arg1File === arg2File;
} else {
result = false;
}
//
return result;
}
//#endregion
//
//#region Handlers ...
/**
* Reading Files List ...
*
* @returns
*/
async handleReadingFiles() {
//
if (isNullOrUndefined(this.filesReader)) {
return;
}
//
const files = await this.getValueAsync(this.filesReader);
if (!isNullOrUndefined(files) && this.hasChild(files)) {
//
this.files = [...files];
this.originalFiles = [...files];
//
await this.handlePresentTypeChanged();
}
}
/**
* Handle Present Type Changed ...
*
* @returns
*/
async handlePresentTypeChanged() {
//
this.filesListItems = [];
//
if (
!this.hasChild(this.files) ||
isNullOrUndefined(this.presentType) ||
!isVFilesPresenterPeresentType(this.presentType) ||
this.presentType === this.FilesPresenterPresentTypes.AsNone
) {
return;
}
//
if (this.presentType === this.FilesPresenterPresentTypes.AsList) {
//
this.filesListItems = [
...this.files.map((t) => {
return {
data: t,
slideOptions: this.fileActionsProvider(t),
} as XListItem<XFileDto | File>;
}),
];
//
this.detectChanges();
this.listActionProvider.next({ action: XListAction.Refresh });
}
}
/**
* Handle Actions ...
*
* @param tag
* @param action
*/
handleActionClicked(
file: XFileDto | File,
action: XSimpleListItemSlideOption,
) {
//
// Validate ...
if (
this.loading ||
this.uiDisabled ||
isNullOrUndefined(file) ||
isNullOrUndefined(action) ||
isNullOrEmptyString(action.id)
) {
return;
}
//
// Remove ...
if (action.id === defaultRemoveSlideOption.id) {
//
let idx =
!isNullOrUndefined(this.files) && this.hasChild(this.files)
? this.files.findIndex((f) => this.isSame(file, f))
: -1;
if (idx >= 0) {
//
this.files = [...this.files.filter((f) => !this.isSame(file, f))];
//
// Checking in List Items ...
let idx = this.filesListItems.findIndex((fli) =>
this.isSame(file, fli.data),
);
if (idx >= 0) {
//
this.filesListItems = [
...this.filesListItems.filter(
(fli) => !this.isSame(file, fli.data),
),
];
//
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
}
//
this.detectChanges();
this.handlePrepareFilesState();
}
}
}
/**
* Prepare and Notify Files State ...
*
* @returns
*/
handlePrepareFilesState() {
//
const hasOriginals =
!isNullOrUndefined(this.originalFiles) &&
this.hasChild(this.originalFiles);
//
// Detecting News ...
let hasNews = false;
let news: Array<XFileDto | File> = [];
if (this.hasChild(this.files)) {
//
news = this.files.filter(
(f) =>
!this.hasChild(this.originalFiles) ||
(this.hasChild(this.originalFiles) &&
this.originalFiles.findIndex((of) => {
//
if (isXFileDto(f) && isXFileDto(of)) {
return of.id === f.id;
} else if (f instanceof File && of instanceof File) {
return (of as File).name === f.name;
} else {
return false;
}
}) < 0),
);
//
hasNews = this.hasChild(news);
}
//
// Detecting Removeds ...
let hasRemoveds = false;
let removeds: Array<XFileDto | File> = [];
if (hasOriginals) {
//
removeds = this.originalFiles.filter(
(of) =>
this.files.findIndex((f) => {
//
if (isXFileDto(f) && isXFileDto(of)) {
return f.id === of.id;
} else {
return false;
}
}) < 0,
);
//
hasRemoveds = this.hasChild(removeds);
}
//
let result: VFilesPresenterModelState = {
hasNews,
hasRemoveds,
hasOriginals,
//
news,
removeds,
originals: [...this.originalFiles],
};
//
this.modelStateChanged.emit(result);
this.listActionProvider.next({ action: XListAction.Refresh });
//
return result;
}
/**
* Handle List Item Options Clicked ...
*
* @param event
* @returns
*/
handleListItemsOptionSelected(event: XListItemOptionSelectedEvent) {
//
if (isNullOrUndefined(event)) {
return;
}
//
if (event.id === defaultRemoveSlideOption.id) {
this.handleActionClicked(event.data.data, defaultRemoveSlideOption);
}
}
/**
* Handle List Items Order Changed ...
*/
handleListItemOrdersChanged() {
//
const orderFiles = this.filesListItems.map((fli) => fli.data);
this.files = [...orderFiles];
this.handlePrepareFilesState();
}
//#endregion
//
//#region Private ...
/**
* Preparing Action Bar ...
*/
private prepareActionBarItems() {
//
this.actionBarItems = [
//
// Attach ...
{
index: 0,
id: 'Add',
onlyIcon: true,
icon: this.IconNames.add,
title: this.ResourceIDs.add,
slot: XActionBarItemSlot.START,
iconColor: this.ColorNames.SuccessShade,
handler: async () => {
//
let modal = await this.managerService.dialogService.presentModal({
autoHeight: true,
fullScreen: false,
backdropDismiss: false,
component: VFileInputComponent,
componentProps: {
fileProperties: this.fileProperties,
context: VFileInputModalContext.Modal,
originalFiles: [...this.originalFiles],
toolbarTitle: this.AppResourceIDs.file,
presentType: VFilePresentType.AsFileUpload,
},
});
/**
* wait for Model Result ...
*/
const data = await modal.onDidDismiss();
modal.remove();
modal.dismiss();
/**
* Handle Retrieve Data ...
*/
if (!isNullOrUndefined(data.data)) {
this.handleFileInputDialogResult(data.data);
}
},
disabled: this.loading || this.uiDisabled || !this.canAttach,
} as XSimpleActionBarItem,
];
}
/**
* handle Add Files Dialog Result ...
*
* @param result
*/
private handleFileInputDialogResult(result: Array<XFileDto | File>) {
//
this.files.push(...result);
//
if (this.presentType === this.FilesPresenterPresentTypes.AsList) {
//
this.filesListItems.push(
...result.map((f) => {
//
return {
data: f,
slideOptions: this.fileActionsProvider(f),
} as XListItem<XFileDto | File>;
}),
);
//
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
}
//
this.detectChanges();
this.handlePrepareFilesState();
}
//
//#region Action Provider ...
/**
* Handle Action Provider Registration ...
*/
private handleRegisterActionProvider() {
//
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.applyTakeUntilWrapper(this.actionProvider.asObservable()).subscribe(
async (event) => {
//
// Validate ...
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVFilesPresenterAction(event.action)
) {
return;
}
//
await this.handleAction(event.action, event.payload);
},
);
}
/**
* Send Specified Action through Action provider ...
*
* @param action
* @param payload
* @returns
*/
private sendAction(action: VFilesPresenterAction, payload?: any) {
//
if (
isNullOrUndefined(this.actionProvider) ||
isNullOrEmptyString(action) ||
!isVFilesPresenterAction(action)
) {
return;
}
//
this.actionProvider.next({ action, payload });
}
/**
* Handle Specified Actions ...
*
* @param action
* @param payload
*/
private async handleAction(action: VFilesPresenterAction, payload?: any) {
//
// Refresh ...
if (action === VFilesPresenterAction.Refresh) {
//
if (isNullOrUndefined(payload)) {
this.detectChanges();
} else if (!isNullOrUndefined(payload) && isNumber(payload)) {
//
this.detectChanges();
setTimeout(() => this.detectChanges(), toNumber(payload));
}
}
//
// Reset ...
else if (action === VFilesPresenterAction.Reset) {
//
this.files = [...this.originalFiles];
//
this.filesListItems = [
...this.originalFiles.map((of) => {
return {
data: of,
slideOptions: this.fileActionsProvider(of),
} as XListItem<XFileDto | File>;
}),
];
//
this.detectChanges();
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
this.handlePrepareFilesState();
}
//
// ReState ...
else if (action === VFilesPresenterAction.ReState) {
this.handlePrepareFilesState();
}
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-files-presenter\v-files-presenter.typings.ts
import { XFileDto } from 'x-saherelm-api-sdk';
import { XBaseActionModel } from 'x-framework-components';
import { isValueInEnum } from '../../tools/v-base.tools';
//
//#region Present Type ...
export enum VFilesPresenterPresentType {
AsNone = 'as_none',
AsList = 'as_list',
AsMedias = 'as_medias',
AsThumbnails = 'as_thumbnails',
}
export type VFilesPresenterPresentTypeIdentfier =
| VFilesPresenterPresentType
| string;
export function isVFilesPresenterPeresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VFilesPresenterPresentType));
}
//#endregion
//
//#region Action Provider ...
export enum VFilesPresenterAction {
Reset = 'Reset',
Refresh = 'Refresh',
ReState = 'ReState',
}
export type VFilesPresenterActionIdentifier = VFilesPresenterAction | string;
export interface VFilesPresenterActionModel extends XBaseActionModel<VFilesPresenterAction> {}
export function isVFilesPresenterAction(value: string) {
return isValueInEnum(value, Object.assign({}, VFilesPresenterAction));
}
//#endregion
//
//#region Files State ...
/**
* an Interface to Notify Files Changed ...
*/
export interface VFilesPresenterModelState {
hasOriginals: boolean;
hasNews: boolean;
hasRemoveds: boolean;
originals?: Array<XFileDto | File>;
news?: Array<XFileDto | File>;
removeds?: Array<XFileDto | File>;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-media\v-media.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[loading]="loading"
[uiDisabled]="uiDisabled"
class="v-card v-component-card"
[class.v-no-margin-card]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
*ngIf="canPresent()"
[ngClass]="getArrayValue(cssClass) | async"
class="v-media-container v-component-wrapper unselecteable"
>
<!-- Handle Different Present Types -->
<ng-container
*ngTemplateOutlet="
presentType === MediaPresentTypes.AsItem
? asItemPresentRef
: presentType === MediaPresentTypes.AsCard
? asCardPresentRef
: presentType === MediaPresentTypes.AsMedia
? asMediaPresentRef
: presentType === MediaPresentTypes.AsThumbnail
? asThumbnailPresentRef
: nothingPresentRef
"
></ng-container>
</section>
</ng-template>
<!-- -->
<!-- Present Templates -->
<!-- -->
<!-- Nothing Template -->
<ng-template #nothingPresentRef></ng-template>
<!-- AsItem Template -->
<ng-template #asItemPresentRef>
<div class="v-media-item-present">
<x-slotter
[loading]="loading"
[hasCenterSlot]="false"
[layout]="SlotLayouts.HORIZONTAL"
class="v-media-item-present-slotter"
[uiDisabled]="loading || uiDisabled"
[startTopSlotCssClass]="'x-dynamic-slot'"
>
<!-- Start -->
<!-- Thumbnail -->
<x-slot
[name]="SlotNames.START"
[cssClass]="'v-media-item-thumbnail-slot ion-text-center '"
>
<ng-container *ngTemplateOutlet="asThumbnailPresentRef"></ng-container>
</x-slot>
<!-- End -->
<!-- Info -->
<x-slot
[name]="SlotNames.END"
[cssClass]="'v-media-item-info-slot ion-text-start'"
>
<span class="v-media-info">
{{ info }}
</span>
</x-slot>
</x-slotter>
</div>
</ng-template>
<!-- AsCard Template -->
<ng-template #asCardPresentRef>
<section class="v-media-card-present">
<x-slotter
[loading]="loading"
[layout]="SlotLayouts.VERTICAL"
class="v-media-card-present-slotter"
[uiDisabled]="loading || uiDisabled"
>
<!-- Start -->
<!-- Thumbnail -->
<x-slot
[name]="SlotNames.TOP"
[cssClass]="'v-media-card-top-slot ion-text-center'"
>
<ng-container *ngTemplateOutlet="asThumbnailPresentRef"></ng-container>
</x-slot>
<!-- Center -->
<!-- Title -->
<x-slot
[name]="SlotNames.END"
[cssClass]="'v-media-card-centet-slot ion-text-center'"
>
<section class="v-media-card-info">
{{ resourceProvider(thumbnailTitle) }}
</section>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.BOTTOM"
[cssClass]="'v-media-card-bottom-slot ion-text-center'"
>
<!-- Check Has Actions -->
<ng-container *ngIf="hasActions()">
<section class="v-media-card-actions">
<!-- Loop through Actions -->
<ng-container *ngFor="let action of providedActions">
<!-- Presenting Action -->
<ng-container
*ngTemplateOutlet="actionRef; context: { action: action }"
></ng-container>
</ng-container>
</section>
</ng-container>
</x-slot>
</x-slotter>
</section>
</ng-template>
<!-- AsMedia Template -->
<ng-template #asMediaPresentRef>Media As Media ...</ng-template>
<!-- AsThumbnail Template -->
<ng-template #asThumbnailPresentRef>
<!-- Presenter -->
<section
*ngIf="canShowAsThumbnail()"
class="v-media-thumbnail-present"
matTooltip="{{ resourceProvider(thumbnailTitle) }}"
>
<!-- if Thumbnail URL Presenter -->
<img
[src]="thumbnailUrl"
class="v-media-thumbnail v-media-thumbnail-image"
*ngIf="notNullOrUndefinedValue(thumbnailUrl) | async"
/>
<!-- if Thumbnail SafeUrl Presenter -->
<img
[src]="thumbnailSafeUrl"
class="v-media-thumbnail v-media-thumbnail-image"
*ngIf="notNullOrUndefinedValue(thumbnailSafeUrl) | async"
/>
<!-- if General Thumbnail Provided -->
<x-icon
[name]="thumbnailIcon"
[color]="thumbnailIconColor"
class="v-media-thumbnail v-media-thumbnail-icon"
*ngIf="
!(notNullOrUndefinedValue(thumbnailUrl) | async) &&
(notNullOrUndefinedValue(thumbnailIcon) | async)
"
></x-icon>
</section>
</ng-template>
<!-- Action Template -->
<ng-template #actionRef let-action="action">
<ng-container *ngIf="notNullOrUndefinedValue(action) | async">
<!-- Present SlideOptions as Button -->
<x-button
[icon]="action.icon"
[title]="action.title"
[color]="action.color"
class="v-media-action"
[iconColor]="action.color"
(clicked)="handleCallingActionHandler(action)"
[type]="action.onlyIcon ? ButtonTypes.Icon : ButtonTypes.Raised"
></x-button>
</ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-media\v-media.component.scss
:host {
--thumbnail-size: 32px;
}
.v-media-info {
font-size: small;
width: calc(90%);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.v-media-thumbnail,
.v-media-thumbnail-present {
width: var(--thumbnail-size);
height: var(--thumbnail-size);
}
.v-media-card-present {
padding: 8px;
--thumbnail-size: 128px;
}
.v-media-action,
.v-media-card-info,
.v-media-card-actions {
padding: 3px;
}
:host ::ng-deep .v-media-item-info-slot {
//
width: 100%;
height: 100%;
//
padding-left: 3px;
padding-right: 3px;
//
display: flex;
align-items: center;
}
:host ::ng-deep .v-media-thumbnail {
.x-icon {
--size: var(--thumbnail-size) !important;
}
}
:host ::ng-deep .v-media-card-top-slot {
.v-media-thumbnail-present {
margin-left: auto;
margin-right: auto;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-media\v-media.component.ts
import {
isImage,
isVideo,
isAudio,
XFileBlob,
isValidURL,
isDocument,
isFunction,
isXFileBlob,
nameofInstance,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
XColorIdentifier,
} from 'x-framework-core';
import {
MediaType,
toMediaType,
MediaSourceType,
isMediaTypePassed,
VMediaPresentType,
getMediaSourceType,
isVMediaPresentType,
MediaSourceInputType,
isVMediaPresentTypePassed,
VMediaPresentTypeIdentifier,
} from './v-media.typings';
import {
XSlotName,
XIconNames,
XSlotLayout,
XButtonType,
XIconIdentifier,
} from 'x-framework-components';
import { XFileDto } from 'x-saherelm-api-sdk';
import { Component, Input } from '@angular/core';
import { SafeUrl } from '@angular/platform-browser';
import { FileLocalizationsResourceIDs } from '../v-file.configs';
import { VBaseInCardComponent } from '../../classes/v-base.component';
import { XSimpleListItemSlideOption } from '../../typings/simple.list-item.typings';
@Component({
selector: 'v-media',
templateUrl: './v-media.component.html',
styleUrls: ['./v-media.component.scss'],
})
export class VMediaComponent extends VBaseInCardComponent {
//
//#region Props ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly ButtonTypes = Object.assign({}, XButtonType);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly MediaPresentTypes = Object.assign({}, VMediaPresentType);
/**
* Provided Source for Presenting ...
*/
@Input()
src: MediaSourceInputType = undefined;
/**
* CSS Classes names to Attach ...
*/
@Input()
cssClass: Array<string> = [];
/**
* Force to Use Thumbnails Paths if Provided XFileDto source ...
*/
@Input()
useThumbnailsIfProvided: boolean = true;
/**
* Specify How to Represent Component ...
*/
@Input()
presentType: VMediaPresentTypeIdentifier = VMediaPresentType.AsCard;
/**
* Thumbnail Provided Icon's Color ...
*/
@Input()
thumbnailIconColor: XColorIdentifier = this.ColorNames.TertiaryShade;
/**
* Provides Thumbnails Icon for Use if necessary ...
*
* @param type
* @returns
*/
@Input()
thumbnailIconProvider: (type: MediaType) => XIconIdentifier = (type) => {
//
let result: XIconIdentifier = undefined;
//
if (isMediaTypePassed(type)) {
//
switch (type) {
//
case MediaType.Image:
result = XIconNames.image;
break;
//
case MediaType.Video:
result = XIconNames.video;
break;
//
case MediaType.Audio:
result = XIconNames.audio;
break;
//
case MediaType.Document:
result = XIconNames.document;
break;
}
}
//
return result;
};
/**
* Provides Thumbnail Title for Use if necessary ...
* @param type
* @returns
*/
@Input()
thumbnailTitleProvider: (type: MediaType) => XResourceIdentifier = (type) => {
//
let result: XResourceIdentifier = undefined;
//
if (isMediaTypePassed(type)) {
//
switch (type) {
//
case MediaType.Image:
result = FileLocalizationsResourceIDs.image;
break;
//
case MediaType.Video:
result = FileLocalizationsResourceIDs.video;
break;
//
case MediaType.Audio:
result = FileLocalizationsResourceIDs.audio;
break;
//
case MediaType.Document:
result = FileLocalizationsResourceIDs.document;
break;
}
}
//
return result;
};
/**
* Provided Actions which Used only in Card Present Type ...
*/
@Input()
providedActions: Array<XSimpleListItemSlideOption> = [];
//
// Required Fields For Describe Media ...
//
url: string = undefined;
info: string = undefined;
type: MediaType = undefined;
safeUrl: SafeUrl = undefined;
inputType: MediaSourceType = undefined;
//
thumbnailUrl: string = undefined;
thumbnailSafeUrl: SafeUrl = undefined;
thumbnailIcon: XIconIdentifier = undefined;
thumbnailTitle: XResourceIdentifier = undefined;
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handlePrepareSource();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChnages = false;
//
// Providers Change ...
isChanged =
changeKeys.includes(nameofInstance(this, 'thumbnailIconProvider')) ||
changeKeys.includes(nameofInstance(this, 'thumbnailTitleProvider'));
if (isChanged) {
//
// Icon ...
if (
isNullOrUndefined(this.thumbnailIconProvider) ||
!isFunction(this.thumbnailIconProvider)
) {
//
this.thumbnailIconProvider = (type) => {
//
let result: XIconIdentifier = undefined;
//
if (isMediaTypePassed(type)) {
//
switch (type) {
//
case MediaType.Image:
result = XIconNames.image;
break;
//
case MediaType.Video:
result = XIconNames.video;
break;
//
case MediaType.Audio:
result = XIconNames.audio;
break;
//
case MediaType.Document:
result = XIconNames.document;
break;
}
}
//
return result;
};
}
//
// Title ...
if (
isNullOrUndefined(this.thumbnailTitleProvider) ||
!isFunction(this.thumbnailTitleProvider)
) {
//
this.thumbnailTitleProvider = (type) => {
//
let result: XResourceIdentifier = undefined;
//
if (isMediaTypePassed(type)) {
//
switch (type) {
//
case MediaType.Image:
result = FileLocalizationsResourceIDs.image;
break;
//
case MediaType.Video:
result = FileLocalizationsResourceIDs.video;
break;
//
case MediaType.Audio:
result = FileLocalizationsResourceIDs.audio;
break;
//
case MediaType.Document:
result = FileLocalizationsResourceIDs.document;
break;
}
}
//
return result;
};
}
//
applyChnages = true;
this.thumbnailIcon = this.thumbnailIconProvider(this.type);
this.thumbnailTitle = this.thumbnailTitleProvider(this.type);
}
//
// Source Change ...
isChanged = changeKeys.includes(nameofInstance(this, 'src'));
if (isChanged) {
this.handlePrepareSource();
}
//
// PresentType Change ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (
isNullOrUndefined(this.presentType) ||
!isVMediaPresentType(this.presentType)
) {
//
applyChnages = true;
this.presentType = VMediaPresentType.AsCard;
}
//
this.setAttribute(nameofInstance(this, 'presentType'), this.presentType);
}
//
// Provided Actions ...
isChanged = changeKeys.includes(nameofInstance(this, 'providedActions'));
if (isChanged) {
//
if (isNullOrUndefined(this.providedActions)) {
//
applyChnages = true;
this.providedActions = [];
}
}
//
// Thumbnail Icon Color ...
isChanged = changeKeys.includes(nameofInstance(this, 'thumbnailIconColor'));
if (isChanged) {
//
if (isNullOrEmptyString(this.thumbnailIconColor)) {
//
applyChnages = true;
this.thumbnailIconColor = this.ColorNames.TertiaryShade;
}
}
//
if (applyChnages) {
this.detectChanges();
}
}
//#endregion
//
//#region Providers ...
/**
* Check Present Type is Allow Component Presenting ...
*
* @returns
*/
canPresent() {
return isVMediaPresentTypePassed(this.presentType);
}
/**
* Check Conditions For Thumbnail Present Type ...
*/
canShowAsThumbnail() {
return (
!isNullOrUndefined(this.thumbnailIcon) &&
!isNullOrUndefined(this.thumbnailTitle)
);
}
/**
* Check Provided Actions ...
*/
hasActions() {
return (
!isNullOrUndefined(this.providedActions) &&
this.hasChild(this.providedActions)
);
}
//#endregion
//
//#region Handlers ...
/**
* Prepare Source and Parse data ...
*
* @returns
*/
handlePrepareSource() {
//
// Validate ...
if (isNullOrUndefined(this.src)) {
return;
}
//
// Detect Input Type ...
this.inputType = getMediaSourceType(this.src);
switch (this.inputType) {
//
case MediaSourceType.Url:
this.handlePrepareUrlSource();
break;
//
case MediaSourceType.File:
this.handlePrepareFileSource();
break;
//
case MediaSourceType.Blob:
case MediaSourceType.XFileBlob:
this.handlePrepareBlobSource();
break;
//
case MediaSourceType.XFileDto:
this.handlePrepareDtoSource();
break;
}
//
// Preparing Thumbnail Info ...
this.thumbnailIcon = this.thumbnailIconProvider(this.type);
this.thumbnailTitle = this.thumbnailTitleProvider(this.type);
//
// Detect Thumbnail URL ...
if (this.type === MediaType.Image) {
//
if (this.inputType === MediaSourceType.XFileDto) {
//
const value = this.src as XFileDto;
this.thumbnailUrl = !isNullOrEmptyString(value.thumbPath)
? value.thumbPath
: value.path;
} else {
this.thumbnailUrl = this.url;
}
//
if (isNullOrUndefined(this.thumbnailUrl)) {
this.thumbnailUrl = undefined;
}
//
if (!isNullOrUndefined(this.safeUrl) && isNullOrEmptyString(this.url)) {
//
this.thumbnailUrl = undefined;
this.thumbnailSafeUrl = this.safeUrl;
}
}
}
/**
* Calling Action Handler ...
*
* @param action
* @returns
*/
async handleCallingActionHandler(action: XSimpleListItemSlideOption) {
//
// Validate ...
if (isNullOrUndefined(action)) {
return;
}
//
// Validate Handler Exists ...
if (isNullOrUndefined(action.handler) && !isFunction(action.handler)) {
return;
}
//
// Calling Action Handler ...
await this.getValuesAsync(action.handler(this.src));
}
//#endregion
//
//#region Private ...
/**
* Set Thumbnail Size ...
*
* @param size
*/
private handleSetThumbnailSize(size: number) {
//
if (size < 32) {
size = 32;
}
//
const name = 'thumbnail-size';
const value = `${size}px;`;
//
this.setCssVar(name, value);
this.setCssVar(name, value, document.documentElement);
}
/**
* Extract required data when user provides string as source ...
*/
private handlePrepareUrlSource() {
//
let value = this.src as string;
//
// Detecting Type ...
this.detectType(value);
//
// Assign URL if has a Valid Url ...
this.url = value;
//
if (isValidURL(value)) {
//
this.info = value;
}
//
this.safeUrl = undefined;
this.thumbnailSafeUrl = undefined;
}
/**
* Extract required data when user provides XFileDto as source ...
*/
private handlePrepareDtoSource() {
//
let value = this.src as XFileDto;
//
// Validate ...
if (isNullOrUndefined(value) || isNullOrEmptyString(value.id)) {
return;
}
//
// Detecting Type ...
this.detectType(value);
//
// Check Can User Thumbnail ...
if (this.useThumbnailsIfProvided && !isNullOrEmptyString(value.thumbPath)) {
this.url = value.thumbPath;
} else if (!isNullOrEmptyString(value.path)) {
this.url = value.path;
}
//
this.safeUrl = undefined;
this.thumbnailSafeUrl = undefined;
this.info = value.name || value.path || value.thumb || value.thumbPath;
}
/**
* Extract required data when user provides Blob as source ...
*/
private handlePrepareBlobSource() {
//
// Blob ...
if (this.src instanceof Blob) {
//
let value = this.src as Blob;
this.url = undefined;
this.detectType(value);
this.safeUrl =
this.sharedService.managerService.securityService.toSafeURL(
URL.createObjectURL(value),
);
} else if (isXFileBlob(this.src)) {
//
let value = this.src as XFileBlob;
this.detectType(value);
this.url = value.name;
this.info = value.name;
this.safeUrl = undefined;
}
}
/**
* Extract required data when user provides File as source ...
*/
private handlePrepareFileSource() {
//
let value = this.src as File;
//
this.detectType(value);
this.url = undefined;
this.safeUrl = this.sharedService.managerService.securityService.toSafeURL(
URL.createObjectURL(value),
);
this.info = value.name;
}
/**
* Detect Media Type based on Identifier ...
*
* @param identifier
* @returns
*/
private detectType(identifier: XFileDto | File | Blob | XFileBlob | string) {
//
let result: MediaType = MediaType.None;
//
// Validate ...
if (!isNullOrUndefined(identifier)) {
//
if (this.inputType === MediaSourceType.XFileDto) {
this.type = toMediaType((identifier as XFileDto).type);
} else {
//
if (isImage(identifier as any)) {
this.type = MediaType.Image;
} else if (isVideo(identifier as any)) {
this.type = MediaType.Video;
} else if (isAudio(identifier as any)) {
this.type = MediaType.Audio;
} else if (isDocument(identifier as any)) {
this.type = MediaType.Document;
}
}
}
//
return result;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-media\v-media.config.ts
//
//#region Localization Configuration ...
export enum MediaResourceIDs {
media = 'media',
medias = 'medias',
}
//
//#region Single Resources ...
//
//#region media ...
export const media_fa = {
id: 'media',
value: 'رسانه',
};
export const media_en = {
id: 'media',
value: 'Media',
};
//#endregion
//
//#region medias ...
export const medias_fa = {
id: 'medias',
value: 'رسانه ها',
};
export const medias_en = {
id: 'medias',
value: 'Medias',
};
//#endregion
//#endregion
//
export const MediaLocalizationResourceIDs = Object.assign(MediaResourceIDs, {});
//
export const MediaLocalizationResources = {
media: {
fa: { ...media_fa },
en: { ...media_en },
},
medias: {
fa: { ...medias_fa },
en: { ...medias_en },
},
};
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\files\v-media\v-media.typings.ts
import {
isString,
toXString,
XFileBlob,
XFileType,
isXFileBlob,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import { isXFileDto, XFileDto } from 'x-saherelm-api-sdk';
import { XBaseActionModel } from 'x-framework-components';
import { isValueInEnum } from 'src/app/views/tools/v-base.tools';
//
//#region Media Type ...
export enum MediaType {
None = 'none',
Image = 'image',
Video = 'video',
Audio = 'audio',
Document = 'document',
}
export function isMediaType(value: string) {
return isValueInEnum(value, Object.assign({}, MediaType));
}
export function isMediaTypePassed(value: string) {
return isMediaType(value) && value !== MediaType.None;
}
export function toMediaType(type: XFileType | string) {
//
let result = MediaType.None;
//
if (!isNullOrUndefined(type)) {
//
if (type === XFileType.Audio) {
result = MediaType.Audio;
} else if (type === XFileType.Video) {
result = MediaType.Video;
} else if (type === XFileType.Document) {
result = MediaType.Document;
} else if (
type === XFileType.Image ||
type === XFileType.CoverImage ||
type === XFileType.ProfileImage
) {
result = MediaType.Image;
}
}
//
return result;
}
//#endregion
//
//#region Present Type ...
export enum VMediaPresentType {
AsNone = 'as_none',
AsCard = 'as_card',
AsItem = 'as_item',
AsMedia = 'as_media',
AsThumbnail = 'as_thumbnail',
}
export type VMediaPresentTypeIdentifier = VMediaPresentType | string;
export function isVMediaPresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VMediaPresentType));
}
export function isVMediaPresentTypePassed(value: string) {
return isVMediaPresentType(value) && value !== VMediaPresentType.AsNone;
}
//#endregion
//
//#region Media Source Type ...
export enum MediaSourceType {
Url = 'Url',
None = 'none',
File = 'File',
Blob = 'Blob',
XFileDto = 'XFileDto',
XFileBlob = 'XFileBlob',
}
export type MediaSourceTypeIdentifier = MediaSourceType | string;
export type MediaSourceInputType = XFileDto | XFileBlob | File | Blob | string;
export function isMediaSourceType(value: string) {
return isValueInEnum(value, Object.assign({}, MediaSourceType));
}
export function getMediaSourceType(input: MediaSourceInputType) {
//
let result: MediaSourceType = MediaSourceType.None;
//
let isValid = false;
//
// Validate input ...
isValid = !isNullOrUndefined(input);
if (!isValid) {
return result;
}
//
// Check XFile Dto ...
isValid = isXFileDto(input);
if (isValid) {
//
result = MediaSourceType.XFileDto;
return result;
}
//
// Check File ...
isValid = input instanceof File;
if (isValid) {
//
result = MediaSourceType.File;
return result;
}
//
// Check XFileBlob ...
isValid = isXFileBlob(input);
if (isValid) {
//
result = MediaSourceType.XFileBlob;
return result;
}
//
// Check Blob ...
isValid = input instanceof Blob;
if (isValid) {
//
result = MediaSourceType.Blob;
return result;
}
//
// Check Url ...
isValid = isString(input) && !isNullOrEmptyString(toXString(input));
if (isValid) {
//
result = MediaSourceType.Url;
return result;
}
//
return result;
}
//#endregion
//
//#region Action Provider ...
export enum VMediaAction {
Refresh = 'Refresh',
}
export type VMediaActionIdentifier = VMediaAction | string;
export interface VMediaActionModel extends XBaseActionModel<VMediaAction> {}
export function isVMediaAction(value: string) {
return isValueInEnum(value, Object.assign({}, VMediaAction));
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-string.tools.ts
import { XLocale } from 'x-framework-core';
import { AppResourceIDs } from 'src/app/config/localization.config';
export function getLanguageResourceID(locale: XLocale | string) {
//
if (locale === 'en-US') {
return AppResourceIDs.english;
} else if (locale === 'fa-IR') {
return AppResourceIDs.persian;
}
//
return locale;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-string.typings.ts
export enum StringTasks {
Add = 'add_string_resource',
Update = 'update_string_resource',
AddResource = 'add_resource_string',
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-locale\v-locale.component.html
<v-dto #dtoPresenter [dtoContainer]="this"></v-dto>
<!-- -->
<!-- Auto Complete Ref -->
<!-- -->
<!-- Language -->
<ng-template #languageAutoCompleteRef let-item="item" let-index="index">
{{ item.data }}
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-locale\v-locale.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-locale\v-locale.component.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XLocaleResourceDto,
defaultLocaleResourceDto,
prepareLocaleResourceDtoFields,
} from 'x-saherelm-api-sdk';
import {
nthItems,
getFieldNames,
XExceptionIDs,
XStandardType,
toNormalString,
setPropertyValue,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XListItem,
XFormControlType,
XFormControlAppearance,
} from 'x-framework-components';
import { map } from 'rxjs/operators';
import { Validators } from '@angular/forms';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { getLanguageResourceID } from '../v-string.tools';
import { XSimpleFormControlAutoCompleteConfig } from '../../typings/simple.form.typings';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-locale',
templateUrl: './v-locale.component.html',
styleUrls: ['./v-locale.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VLocaleComponent extends VBaseDtoComponent<XLocaleResourceDto> {
//
//#region Props ...
//
// User Push Conection ID ...
connectionId: string;
availableLanguages: Array<string> = [];
//
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
readonly title = this.resourceProvider(this.AppResourceIDs.string_resource);
//
defaultModel: XLocaleResourceDto = {
...defaultLocaleResourceDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
//
languageValue: string = '';
languageAutoCompleteQuery = '';
@ViewChild('languageAutoCompleteRef')
languageAutoCompleteRef!: TemplateRef<any>;
isLanguageAutoCompleteOpened: boolean = false;
languageAutoCompleteItems: XListItem<string>[];
languageAutoCompleteConfig: XSimpleFormControlAutoCompleteConfig<
XListItem<string>
>;
/**
* Describe Model ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Language ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.language,
label: this.ResourceIDs.language,
value: this.defaultModel.language,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.resourceProvider(getLanguageResourceID(value));
},
allowFieldForm: true,
formControlConfig: {
index: 0,
propName: this.FieldNames.language,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {
onBlured: (model) => {},
onFocused: (model) => {},
valueChanged: (event) => {
//
if (this.languageValue === event.value) {
return;
}
//
this.handleFilterAutoCompleteLanguage(event.value);
},
},
appearance: {
hidden: false,
disabled: false,
placeholder: this.ResourceIDs.language,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.language,
color: this.ColorNames.Tertiary,
},
},
autoComplete: {},
},
},
} as VModelFieldDescriptor<string>,
//
// Value ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.value,
value: this.defaultModel.value,
type: this.ModelFieldTypes.String,
label: this.ResourceIDs.translation,
valueProvider: (value) => {
return this.applyLocale(value);
},
allowFieldForm: true,
formControlConfig: {
index: 1,
propName: this.FieldNames.value,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.ResourceIDs.translation,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
//#endregion
//
//#region LifeCycles ...
async onInit(): Promise<void> {
super.onInit();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
var isChanged = false;
//
await this.handleLanguageAutoComplete();
}
/**
* Register Required Instance Subscriptions ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Abstraction Implementation ...
/**
* Validate Model for Actions ...
*
* @param model
*/
validate(
model?: Partial<XLocaleResourceDto>,
): VModelValidationResult<XLocaleResourceDto> {
//
let result: VModelValidationResult<XLocaleResourceDto>;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// Language ...
isValid = !isNullOrEmptyString(normalized.language);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.language,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Translated Value ...
isValid = !isNullOrEmptyString(normalized.value);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.translation,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd = false;
const isValidForUpdate = false;
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
/**
* Prepare Model ...
*
* @param model
*/
prepare(model?: XLocaleResourceDto): XLocaleResourceDto {
return prepareLocaleResourceDtoFields(model);
}
//#endregion
//
//#region Overrides ...
async onFieldFormPresent(key: string) {
//
if (key === this.FieldNames.language) {
await this.handleLanguageAutoComplete();
}
}
//#endregion
//
//#region AutoComplete ...
/**
* Retriev Available Languages ...
*
* @returns
*/
async getAvailableLanguages() {
//
let has =
!this.hasChild(this.availableLanguages) &&
(this.presentType == this.PresentTypes.AsForm ||
(this.presentType == this.PresentTypes.AsView &&
!isNullOrUndefined(this.inFormFieldsFormConfigs) &&
this.hasChild(this.inFormFieldsFormConfigs) &&
this.isFormView(this.FieldNames.language)));
if (!has) {
return;
}
//
const request =
this.sharedService.saherElmAPIService.strings.stringProviderService
.languages()
.pipe(
map((languages) => {
//
this.availableLanguages = [...languages];
//
this.prepareLanguageAutoComplete();
}),
);
//
await this.getValueAsync(request);
}
/**
* Manage Auto Complete Requirement Handling ...
*
* @returns
*/
async handleLanguageAutoComplete() {
//
// Handle State When AsForm Present ...
if (this.presentType === this.PresentTypes.AsForm) {
//
if (!this.hasChild(this.availableLanguages)) {
await this.getAvailableLanguages();
return;
} else {
//
this.prepareLanguageAutoComplete();
return;
}
}
//
// Handle State When AsView ...
else if (
this.showFieldsForm &&
this.presentType === this.PresentTypes.AsView
) {
//
const languageFD = this.fieldDescriptors.find(
(fd) => fd.key == this.FieldNames.language,
);
if (languageFD.allowFieldForm) {
//
if (!this.hasChild(this.availableLanguages)) {
//
await this.getAvailableLanguages();
return;
} else {
this.prepareLanguageAutoComplete();
return;
}
}
}
}
/**
* Prepare Language AutoComplete ...
*
* @returns
*/
prepareLanguageAutoComplete() {
//
if (!this.hasChild(this.availableLanguages)) {
return;
}
//
const currentLang = this.getFieldValue(this.FieldNames.language);
if (
!isNullOrEmptyString(currentLang) &&
this.availableLanguages.findIndex((a) => a == currentLang) >= 0
) {
return;
}
//
let fcIDX = -1;
let canDo = false;
let inField = false;
//
// Field Form View ...
if (this.presentType === this.PresentTypes.AsView) {
//
// Access Field Form Index ...
inField = true;
canDo = this.isFormView(this.FieldNames.language);
fcIDX = this.inFormFieldsFormConfigs.findIndex(
(f) => f.name === this.FieldNames.language,
);
}
//
// Form View ...
else if (this.presentType === this.PresentTypes.AsForm) {
//
// Checking Form Config ...
canDo = !isNullOrUndefined(this.modelFormConfig);
if (canDo) {
//
inField = false;
//
fcIDX = this.modelFormConfig.controls.findIndex(
(f) => f.propName === this.FieldNames.language,
);
if (fcIDX >= 0) {
fcIDX = this.modelFormConfig.controls[fcIDX].index;
}
}
}
//
// Check Validation ...
canDo = fcIDX >= 0;
if (!canDo) {
return;
}
//
// Access Controls ...
let controls = !inField
? this.modelFormConfig.controls
: this.inFormFieldsFormConfigs[fcIDX].controls;
//
// Access Control Appearance ...
let appearance = inField
? controls[0].appearance
: controls[fcIDX].appearance;
//
// Access Event Handlers ...
let eventHandlers = inField
? controls[0].eventHandlers
: controls[fcIDX].eventHandlers;
//
let autoCompleteConfig = appearance.autoComplete;
//
// Update Auto Complete Items ...
this.languageAutoCompleteConfig = {
...autoCompleteConfig,
//
// Opened ...
opened: this.isLanguageAutoCompleteOpened,
//
// Item Template ...
itemTemplate: this.languageAutoCompleteRef,
//
// Color ...
color: this.ColorNames.Tertiary,
//
// Items ...
items: () => this.languageAutoCompleteItems,
//
// On Select ...
onSelect: (item: any) => {
//
this.languageAutoCompleteItems = [];
this.isLanguageAutoCompleteOpened = false;
this.prepareLanguageAutoComplete();
},
//
// CSS Class ...
cssClass: ['x-auto-complete-margin'],
};
//
// Set Auto Complete Config to Controls ...
if (inField) {
//
controls[0] = {
...controls[0],
appearance: {
...appearance,
autoComplete: {
items: this.languageAutoCompleteConfig.items,
color: this.languageAutoCompleteConfig.color,
opened: this.languageAutoCompleteConfig.opened,
onSelect: this.languageAutoCompleteConfig.onSelect,
cssClass: this.languageAutoCompleteConfig.cssClass,
itemTemplate: this.languageAutoCompleteConfig.itemTemplate,
},
},
};
//
// Preparing Model ...
let iModel = this.inFormFieldsFormConfigs[fcIDX].model;
setPropertyValue(iModel, this.FieldNames.language, this.languageValue);
//
this.inFormFieldsFormConfigs[fcIDX] = {
...this.inFormFieldsFormConfigs[fcIDX],
model: iModel,
controls: [...controls],
};
} else {
//
controls[fcIDX] = {
...controls[fcIDX],
appearance: {
...appearance,
autoComplete: {
color: this.languageAutoCompleteConfig.color,
items: this.languageAutoCompleteConfig.items,
opened: this.languageAutoCompleteConfig.opened,
onSelect: this.languageAutoCompleteConfig.onSelect,
cssClass: this.languageAutoCompleteConfig.cssClass,
itemTemplate: this.languageAutoCompleteConfig.itemTemplate,
},
},
};
//
// Preparing Model ...
let iModel = this.modelFormConfig.model;
setPropertyValue(iModel, this.FieldNames.language, this.languageValue);
//
this.modelFormConfig = {
...this.modelFormConfig,
model: iModel,
controls: [...controls],
};
}
}
/**
* Handle Apply Changes On Language Auto Complete when Query Changes ...
*
* @param value
*
* @returns
*/
handleFilterAutoCompleteLanguage(value: string) {
//
// If Values Same only Open or Close ...
if (toNormalString(value) === this.languageAutoCompleteQuery) {
//
// Check HasChild Items or not ...
this.isLanguageAutoCompleteOpened = this.hasChild(
this.languageAutoCompleteItems,
);
//
this.prepareLanguageAutoComplete();
//
return;
}
//
this.languageValue = value;
this.languageAutoCompleteQuery = toNormalString(value);
//
// Filter Items Based on Query ...
const filteredItems = this.availableLanguages.filter(
(i) =>
toNormalString(i).includes(this.languageAutoCompleteQuery) &&
toNormalString(i) !== this.languageAutoCompleteQuery,
);
if (this.hasChild(filteredItems)) {
//
// Select Required Number of Filtered Items ...
const selectedItems = nthItems(0, 5, filteredItems);
if (this.hasChild(selectedItems)) {
//
// Create Result ...
this.languageAutoCompleteItems = selectedItems.map((i) => {
return {
data: i,
} as XListItem<string>;
});
}
}
//
this.isLanguageAutoCompleteOpened = this.hasChild(
this.languageAutoCompleteItems,
);
//
this.prepareLanguageAutoComplete();
}
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-resource\v-resource.component.html
<v-dto #dtoPresenter [dtoContainer]="this"></v-dto>
<!-- -->
<!-- Locale Resource Template -->
<!-- -->
<ng-template
#localeResourceRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<!-- Make off Form Buttons ... -->
<v-locale
[model]="value"
[showFormResetButton]="false"
[showFormCancelButton]="false"
[showFormActionButton]="false"
[presentType]="props.presentType"
(changeNotifier)="handleLocaleChanged($event, key, index, value)"
></v-locale>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-resource\v-resource.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-resource\v-resource.component.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
getFieldNames,
XExceptionIDs,
XStandardType,
isNullOrUndefined,
} from 'x-framework-core';
import {
XResourceDto,
XLocaleResourceDto,
defaultResourceDto,
prepareResourceDtoFields,
} from 'x-saherelm-api-sdk';
import {
XFormControlType,
XFormControlAppearance,
} from 'x-framework-components';
import {
VModelState,
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { Validators } from '@angular/forms';
import { StringTasks } from '../v-string.typings';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-resource',
templateUrl: './v-resource.component.html',
styleUrls: ['./v-resource.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VResourceComponent extends VBaseDtoComponent<XResourceDto> {
//
//#region Props ...
//
// User Push Conection ID ...
connectionId: string;
availableLanguages: Array<string> = [];
//
@ViewChild('localeResourceRef')
localeResourceRef!: TemplateRef<any>;
//
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
readonly title = this.resourceProvider(this.AppResourceIDs.string_resource);
//
defaultModel: XResourceDto = {
...defaultResourceDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
/**
* Describe Model ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Resource ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.resource,
type: this.ModelFieldTypes.String,
value: this.defaultModel.resource,
label: this.AppResourceIDs.resource_title,
allowFieldForm: true,
formControlConfig: {
index: 1,
propName: this.FieldNames.resource,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.resource_title,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Locales ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.locales,
type: this.ModelFieldTypes.Array,
value: this.defaultModel.locales,
label: this.AppResourceIDs.locales,
allowFieldForm: false,
hasCustomTemplate: true,
showContentBelowLabel: true,
template: this.localeResourceRef,
} as VModelFieldDescriptor<any>,
];
//#endregion
//
//#region LifeCycles ...
async onInit(): Promise<void> {
super.onInit();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
var isChanged = false;
//
this.handleAttachLocalesTemplate();
}
/**
* Register Required Instance Subscriptions ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region UI Handlers ...
handleLocaleChanged(
event: VModelState<XLocaleResourceDto>,
key: string,
index: number,
vale: XLocaleResourceDto,
) {
// //
// const fdIDX = this.fieldDescriptors.findIndex((f) => f.key === key);
// const obj =
// index >= 0 ? this.getFieldValue(key)[index] : this.getFieldValue(key);
// const canApply =
// !isNullOrUndefined(event) && !isSameObject(event.model, obj);
// if (!canApply) {
// return;
// }
// //
// let notify = false;
// if (index >= 0) {
// //
// notify = true;
// (this.model as any)[key][index] = event.model;
// //setPropertyValue((this.MODEL as any)[key], toXString(index), event.model);
// } else {
// //
// notify = true;
// (this.MODEL as any)[key] = event.model;
// //setPropertyValue(this.MODEL, key, event.model);
// }
// //
// if (notify) {
// //
// // this.changeSubject.next(this.MODEL);
// //
// const state = this.getState();
// console.log('state: ', state);
// console.log('obj: ', obj);
// console.log('event model: ', event.model);
// }
}
//#endregion
//
//#region Abstraction Implementation ...
/**
* Validate Model for Actions ...
*
* @param model
*/
validate(
model?: Partial<XResourceDto>,
): VModelValidationResult<XResourceDto> {
//
let result: VModelValidationResult<XResourceDto>;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd = false;
const isValidForUpdate = false;
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
/**
* Prepare Model ...
*
* @param model
*/
prepare(model?: XResourceDto): XResourceDto {
return prepareResourceDtoFields(model);
}
//#endregion
//
//#region Overrides ...
//#endregion
//
//#region AutoComplete ...
//#endregion
//
//#region Private ...
handleAttachLocalesTemplate() {
//
// Check Template Exists ...
if (isNullOrUndefined(this.localeResourceRef)) {
return;
}
//
const fdIDX = this.fieldDescriptors.findIndex(
(fd) => fd.key === this.FieldNames.locales,
);
if (fdIDX < 0) {
return;
}
//
// Check Has Template or not ...
if (!isNullOrUndefined(this.fieldDescriptors[fdIDX].template)) {
return;
}
//
// Attach ...
this.fieldDescriptors[fdIDX].template = this.localeResourceRef;
//
this.detectChanges();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-string\v-string.component.html
<v-dto #dtoPresenter [dtoContainer]="this"></v-dto>
<!-- -->
<!-- Auto Complete Ref -->
<!-- -->
<!-- Language -->
<ng-template #languageAutoCompleteRef let-item="item" let-index="index">
{{ item.data }}
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-string\v-string.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\string\v-string\v-string.component.ts
import {
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
nthItems,
getFieldNames,
XExceptionIDs,
XStandardType,
toNormalString,
setPropertyValue,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XStringDto,
defaultStringDto,
prepareStringDtoFields,
} from 'x-saherelm-api-sdk';
import {
XListItem,
XFormControlType,
XFormControlAppearance,
} from 'x-framework-components';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { map } from 'rxjs/operators';
import { Validators } from '@angular/forms';
import { StringTasks } from '../v-string.typings';
import { getLanguageResourceID } from '../v-string.tools';
import { XSimpleFormControlAutoCompleteConfig } from '../../typings/simple.form.typings';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-string',
templateUrl: './v-string.component.html',
styleUrls: ['./v-string.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VStringComponent extends VBaseDtoComponent<XStringDto> {
//
//#region Props ...
//
// User Push Conection ID ...
connectionId: string;
availableLanguages: Array<string> = [];
//
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
readonly title = this.resourceProvider(this.AppResourceIDs.string_resource);
//
defaultModel: XStringDto = {
...defaultStringDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
//
languageValue: string = '';
languageAutoCompleteQuery = '';
@ViewChild('languageAutoCompleteRef')
languageAutoCompleteRef!: TemplateRef<any>;
isLanguageAutoCompleteOpened: boolean = false;
languageAutoCompleteItems: XListItem<string>[];
languageAutoCompleteConfig: XSimpleFormControlAutoCompleteConfig<
XListItem<string>
>;
/**
* Describe Model ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Id ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.id,
label: this.ResourceIDs.id,
value: this.defaultModel.id,
type: this.ModelFieldTypes.Number,
} as VModelFieldDescriptor<number>,
//
// Language ...
{
hide: false,
hideLabel: false,
key: this.FieldNames.language,
label: this.ResourceIDs.language,
value: this.defaultModel.language,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.resourceProvider(getLanguageResourceID(value));
},
allowFieldForm: true,
formControlConfig: {
index: 0,
propName: this.FieldNames.language,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {
onBlured: (model) => {},
onFocused: (model) => {},
valueChanged: (event) => {
//
if (this.languageValue === event.value) {
return;
}
//
this.handleFilterAutoCompleteLanguage(event.value);
},
},
appearance: {
hidden: false,
disabled: false,
placeholder: this.ResourceIDs.language,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.language,
color: this.ColorNames.Tertiary,
},
},
autoComplete: {},
},
},
} as VModelFieldDescriptor<string>,
//
// Resource Title ...
{
hide: false,
hideLabel: false,
type: this.ModelFieldTypes.String,
key: this.FieldNames.resourceTitle,
value: this.defaultModel.resourceTitle,
label: this.AppResourceIDs.resource_title,
allowFieldForm: true,
formControlConfig: {
index: 1,
propName: this.FieldNames.resourceTitle,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.resource_title,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Translated Value ...
{
hide: false,
hideLabel: false,
type: this.ModelFieldTypes.String,
label: this.ResourceIDs.translation,
key: this.FieldNames.translatedValue,
value: this.defaultModel.translatedValue,
valueProvider: (value) => {
return this.applyLocale(value);
},
allowFieldForm: true,
formControlConfig: {
index: 2,
propName: this.FieldNames.translatedValue,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.ResourceIDs.translation,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
];
//#endregion
//
//#region LifeCycles ...
async onInit(): Promise<void> {
super.onInit();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
var isChanged = false;
//
await this.handleLanguageAutoComplete();
}
/**
* Register Required Instance Subscriptions ...
*/
registerInstanceHandlers(): void {
super.registerInstanceHandlers();
}
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Abstraction Implementation ...
/**
* Validate Model for Actions ...
*
* @param model
*/
validate(model?: Partial<XStringDto>): VModelValidationResult<XStringDto> {
//
let result: VModelValidationResult<XStringDto>;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// ID ...
//
// Language ...
isValid = !isNullOrEmptyString(normalized.language);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.language,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Resource Title ...
isValid = !isNullOrEmptyString(normalized.resourceTitle);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.resource_title,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Translated Value ...
isValid = !isNullOrEmptyString(normalized.translatedValue);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.translation,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd =
isNullOrUndefined(normalized.id) || normalized.id <= 0;
const isValidForUpdate =
!isNullOrUndefined(normalized.id) && normalized.id > 0;
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
/**
* Prepare Model ...
*
* @param model
*/
prepare(model?: XStringDto): XStringDto {
return prepareStringDtoFields(model);
}
//#endregion
//
//#region Overrides ...
async onFieldFormPresent(key: string) {
//
if (key === this.FieldNames.language) {
await this.handleLanguageAutoComplete();
}
}
//#endregion
//
//#region AutoComplete ...
/**
* Retriev Available Languages ...
*
* @returns
*/
async getAvailableLanguages() {
//
let has =
!this.hasChild(this.availableLanguages) &&
(this.presentType == this.PresentTypes.AsForm ||
(this.presentType == this.PresentTypes.AsView &&
!isNullOrUndefined(this.inFormFieldsFormConfigs) &&
this.hasChild(this.inFormFieldsFormConfigs) &&
this.isFormView(this.FieldNames.language)));
if (!has) {
return;
}
//
const request =
this.sharedService.saherElmAPIService.strings.stringProviderService
.languages()
.pipe(
map((languages) => {
//
this.availableLanguages = [...languages];
//
this.prepareLanguageAutoComplete();
}),
);
//
await this.getValueAsync(request);
}
/**
* Manage Auto Complete Requirement Handling ...
*
* @returns
*/
async handleLanguageAutoComplete() {
//
// Handle State When AsForm Present ...
if (this.presentType === this.PresentTypes.AsForm) {
//
if (!this.hasChild(this.availableLanguages)) {
await this.getAvailableLanguages();
return;
} else {
//
this.prepareLanguageAutoComplete();
return;
}
}
//
// Handle State When AsView ...
else if (
this.showFieldsForm &&
this.presentType === this.PresentTypes.AsView
) {
//
const languageFD = this.fieldDescriptors.find(
(fd) => fd.key == this.FieldNames.language,
);
if (languageFD.allowFieldForm) {
//
if (!this.hasChild(this.availableLanguages)) {
//
await this.getAvailableLanguages();
return;
} else {
this.prepareLanguageAutoComplete();
return;
}
}
}
}
/**
* Prepare Language AutoComplete ...
*
* @returns
*/
prepareLanguageAutoComplete() {
//
if (!this.hasChild(this.availableLanguages)) {
return;
}
//
const currentLang = this.getFieldValue(this.FieldNames.language);
if (
!isNullOrEmptyString(currentLang) &&
this.availableLanguages.findIndex((a) => a == currentLang) >= 0
) {
return;
}
//
let fcIDX = -1;
let canDo = false;
let inField = false;
//
// Field Form View ...
if (this.presentType === this.PresentTypes.AsView) {
//
// Access Field Form Index ...
inField = true;
canDo = this.isFormView(this.FieldNames.language);
fcIDX = this.inFormFieldsFormConfigs.findIndex(
(f) => f.name === this.FieldNames.language,
);
}
//
// Form View ...
else if (this.presentType === this.PresentTypes.AsForm) {
//
// Checking Form Config ...
canDo = !isNullOrUndefined(this.modelFormConfig);
if (canDo) {
//
inField = false;
//
fcIDX = this.modelFormConfig.controls.findIndex(
(f) => f.propName === this.FieldNames.language,
);
if (fcIDX >= 0) {
fcIDX = this.modelFormConfig.controls[fcIDX].index;
}
}
}
//
// Check Validation ...
canDo = fcIDX >= 0;
if (!canDo) {
return;
}
//
// Access Controls ...
let controls = !inField
? this.modelFormConfig.controls
: this.inFormFieldsFormConfigs[fcIDX].controls;
//
// Access Control Appearance ...
let appearance = inField
? controls[0].appearance
: controls[fcIDX].appearance;
//
// Access Event Handlers ...
let eventHandlers = inField
? controls[0].eventHandlers
: controls[fcIDX].eventHandlers;
//
let autoCompleteConfig = appearance.autoComplete;
//
// Update Auto Complete Items ...
this.languageAutoCompleteConfig = {
...autoCompleteConfig,
//
// Opened ...
opened: this.isLanguageAutoCompleteOpened,
//
// Item Template ...
itemTemplate: this.languageAutoCompleteRef,
//
// Color ...
color: this.ColorNames.Tertiary,
//
// Items ...
items: () => this.languageAutoCompleteItems,
//
// On Select ...
onSelect: (item: any) => {
//
this.languageAutoCompleteItems = [];
this.isLanguageAutoCompleteOpened = false;
this.prepareLanguageAutoComplete();
},
//
// CSS Class ...
cssClass: ['x-auto-complete-margin'],
};
//
// Set Auto Complete Config to Controls ...
if (inField) {
//
controls[0] = {
...controls[0],
appearance: {
...appearance,
autoComplete: {
items: this.languageAutoCompleteConfig.items,
color: this.languageAutoCompleteConfig.color,
opened: this.languageAutoCompleteConfig.opened,
onSelect: this.languageAutoCompleteConfig.onSelect,
cssClass: this.languageAutoCompleteConfig.cssClass,
itemTemplate: this.languageAutoCompleteConfig.itemTemplate,
},
},
};
//
// Preparing Model ...
let iModel = this.inFormFieldsFormConfigs[fcIDX].model;
setPropertyValue(iModel, this.FieldNames.language, this.languageValue);
//
this.inFormFieldsFormConfigs[fcIDX] = {
...this.inFormFieldsFormConfigs[fcIDX],
model: iModel,
controls: [...controls],
};
} else {
//
controls[fcIDX] = {
...controls[fcIDX],
appearance: {
...appearance,
autoComplete: {
color: this.languageAutoCompleteConfig.color,
items: this.languageAutoCompleteConfig.items,
opened: this.languageAutoCompleteConfig.opened,
onSelect: this.languageAutoCompleteConfig.onSelect,
cssClass: this.languageAutoCompleteConfig.cssClass,
itemTemplate: this.languageAutoCompleteConfig.itemTemplate,
},
},
};
//
// Preparing Model ...
let iModel = this.modelFormConfig.model;
setPropertyValue(iModel, this.FieldNames.language, this.languageValue);
//
this.modelFormConfig = {
...this.modelFormConfig,
model: iModel,
controls: [...controls],
};
}
}
/**
* Handle Apply Changes On Language Auto Complete when Query Changes ...
*
* @param value
*
* @returns
*/
handleFilterAutoCompleteLanguage(value: string) {
//
// If Values Same only Open or Close ...
if (toNormalString(value) === this.languageAutoCompleteQuery) {
//
// Check HasChild Items or not ...
this.isLanguageAutoCompleteOpened = this.hasChild(
this.languageAutoCompleteItems,
);
//
this.prepareLanguageAutoComplete();
//
return;
}
//
this.languageValue = value;
this.languageAutoCompleteQuery = toNormalString(value);
//
// Filter Items Based on Query ...
const filteredItems = this.availableLanguages.filter(
(i) =>
toNormalString(i).includes(this.languageAutoCompleteQuery) &&
toNormalString(i) !== this.languageAutoCompleteQuery,
);
if (this.hasChild(filteredItems)) {
//
// Select Required Number of Filtered Items ...
const selectedItems = nthItems(0, 5, filteredItems);
if (this.hasChild(selectedItems)) {
//
// Create Result ...
this.languageAutoCompleteItems = selectedItems.map((i) => {
return {
data: i,
} as XListItem<string>;
});
}
}
//
this.isLanguageAutoCompleteOpened = this.hasChild(
this.languageAutoCompleteItems,
);
//
this.prepareLanguageAutoComplete();
}
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag.configs.ts
import { XIconNames } from 'x-framework-components';
import { XSimplePage } from '../typings/simple.page.typings';
//
//#region Localization Configuration ...
export enum TagResourceIDs {
tag = 'tag',
tags = 'tags',
tags_page_title = 'tags_page_title',
tags_page_description = 'tags_page_description',
tags_management_page_title = 'tags_management_page_title',
tags_management_page_description = 'tags_management_page_description',
tags_explorer_page_title = 'tags_explorer_page_title',
tags_explorer_page_description = 'tags_explorer_page_description',
}
//
//#region Single Resources ...
//
//#region tag ...
export const tag_fa = {
id: 'tag',
value: 'برچسب',
};
export const tag_en = {
id: 'tag',
value: 'Tag',
};
//#endregion
//
//#region tags ...
export const tags_fa = {
id: 'tags',
value: 'برچسب ها',
};
export const tags_en = {
id: 'tags',
value: 'Tags',
};
//#endregion
//
//#region tags_page_title ...
export const tags_page_title_fa = {
id: 'tags_page_title',
value: 'برچسب',
};
export const tags_page_title_en = {
id: 'tags_page_title',
value: 'Tag',
};
//#endregion
//
//#region tags_page_description ...
export const tags_page_description_fa = {
id: 'tags_page_description',
value: 'ابزارهای مدیریت برچسب ها',
};
export const tags_page_description_en = {
id: 'tags_page_description',
value: 'Tools for Tags',
};
//#endregion
//
//#region tags_management_page_title ...
export const tags_management_page_title_fa = {
id: 'tags_management_page_title',
value: 'مدیریت برچسب ها',
};
export const tags_management_page_title_en = {
id: 'tags_management_page_title',
value: 'Tags Management',
};
//#endregion
//
//#region tags_management_page_description ...
export const tags_management_page_description_fa = {
id: 'tags_management_page_description',
value: 'مدیریت برچسب ها',
};
export const tags_management_page_description_en = {
id: 'tags_management_page_description',
value: 'Tags Management',
};
//#endregion
//
//#region tags_explorer_page_title ...
export const tags_explorer_page_title_fa = {
id: 'tags_explorer_page_title',
value: 'برچسب ها',
};
export const tags_explorer_page_title_en = {
id: 'tags_explorer_page_title',
value: 'Tags',
};
//#endregion
//
//#region tags_explorer_page_description ...
export const tags_explorer_page_description_fa = {
id: 'tags_explorer_page_description',
value: 'مرورگر برچسب',
};
export const tags_explorer_page_description_en = {
id: 'tags_explorer_page_description',
value: 'Tag Explorer',
};
//#endregion
//#endregion
//
export const TagLocalizationResourceIDs = Object.assign({}, TagResourceIDs);
//
export const TagLocalizationResources = {
//
tag: {
fa: { ...tag_fa },
en: { ...tag_en },
},
tags: {
fa: { ...tags_fa },
en: { ...tags_en },
},
tags_page_title: {
fa: { ...tags_page_title_fa },
en: { ...tags_page_title_en },
},
tags_page_description: {
fa: { ...tags_page_description_fa },
en: { ...tags_page_description_en },
},
tags_management_page_title: {
fa: { ...tags_management_page_title_fa },
en: { ...tags_management_page_title_en },
},
tags_management_page_description: {
fa: { ...tags_management_page_description_fa },
en: { ...tags_management_page_description_en },
},
tags_explorer_page_title: {
fa: { ...tags_explorer_page_title_fa },
en: { ...tags_explorer_page_title_en },
},
tags_explorer_page_description: {
fa: { ...tags_explorer_page_description_fa },
en: { ...tags_explorer_page_description_en },
},
};
//#endregion
//
//#region Page Configuration ...
//
export enum TagRoutes {
Default = 'tags',
TagsExplorer = 'tags-explorer',
TagsManagement = 'tags-management',
}
//
export const Tags: XSimplePage = {
id: `${TagRoutes.Default}`,
name: `${TagRoutes.Default}`,
title: TagResourceIDs.tags_page_title,
description: TagResourceIDs.tags_page_description,
baseRoute: `${TagRoutes.Default}`,
route: ['/', `${TagRoutes.Default}`],
icon: XIconNames.collapse,
childs: [
//
//#region Tags Management ...
{
id: `${TagRoutes.TagsManagement}`,
name: `${TagRoutes.TagsManagement}`,
title: TagResourceIDs.tags_management_page_title,
description: TagResourceIDs.tags_management_page_description,
baseRoute: `${TagRoutes.TagsManagement}`,
route: ['/', `${TagRoutes.Default}`, `${TagRoutes.TagsManagement}`],
icon: XIconNames.live_tv,
},
//#endregion
//
//#region Tags Explorer ...
{
id: `${TagRoutes.TagsExplorer}`,
name: `${TagRoutes.TagsExplorer}`,
title: TagResourceIDs.tags_explorer_page_title,
description: TagResourceIDs.tags_explorer_page_description,
baseRoute: `${TagRoutes.TagsExplorer}`,
route: ['/', `${TagRoutes.Default}`, `${TagRoutes.TagsExplorer}`],
icon: XIconNames.live_tv,
},
//#endregion
],
};
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag.tools.ts
import {
nameof,
isArray,
isString,
toNumber,
toXString,
isNullOrUndefined,
} from 'x-framework-core';
import { parseXReferences, XTagDto } from 'x-mabsut-api-sdk';
export function toXTagDto(value: any) {
//
let result: XTagDto = undefined;
//
let isPassed = false;
isPassed =
!isNullOrUndefined(value) &&
Object.keys(value).includes(nameof<XTagDto>('id')) &&
Object.keys(value).includes(nameof<XTagDto>('tag')) &&
Object.keys(value).includes(nameof<XTagDto>('references'));
if (isPassed) {
//
result = {
id: toNumber(value.id),
tag: toXString(value.tag),
references: isNullOrUndefined(value.references)
? []
: isString(value.references)
? parseXReferences<string>(value.references)
: isArray(value.references)
? [...value.references]
: [],
};
}
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag\v-tag.component.html
<v-dto #dtoPresenter *ngIf="showComponent" [dtoContainer]="this">
<!-- Content Injections -->
<!-- Card Actions -->
<section
actions
*ngIf="
(getValue(wrapWithCard) | async) && (getValue(cardShowActions) | async)
"
>
<ng-content select="[actions]"></ng-content>
</section>
</v-dto>
<!-- -->
<!-- Templates -->
<!-- -->
<!-- References Template -->
<ng-template
#referencesRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<ng-container
*ngIf="
(notNullOrUndefinedValue(value) | async) &&
(notNullOrUndefinedValue(getReferencesListItems(value)) | async)
"
>
<x-list
[showEmpty]="true"
class="v-references-list"
[emptyHasRefresh]="false"
[itemTemplate]="referencesListItemRef"
[items]="getReferencesListItems(value)"
[actionProvider]="referencesListActionProvider"
>
</x-list>
</ng-container>
</ng-template>
<!-- References List Item Template -->
<ng-template #referencesListItemRef let-item="item">
<v-reference
[model]="item.data"
[presentType]="referencePresentType"
*ngIf="notNullOrUndefinedValue(item) | async"
></v-reference>
</ng-template>
<!-- As Item Present Type Ref -->
<ng-template #asItemTemplateRef>
<section
class="v-tag-item"
[xColor]="valueColor"
[isForeground]="true"
*ngIf="notNullOrUndefinedValue(model) | async"
>
{{ getFieldValue(FieldNames.tag) }}
</section>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag\v-tag.component.scss
.v-references-list {
padding: {
top: 16px;
left: 6px;
right: 6px;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag\v-tag.component.ts
import {
Input,
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
getFieldNames,
XExceptionIDs,
isNullOrUndefined,
isNullOrEmptyString,
nameofInstance,
} from 'x-framework-core';
import {
XTagDto,
defaultTagDto,
XReferenceDto,
prepareTagDtoFields,
} from 'x-saherelm-api-sdk';
import { Subject } from 'rxjs';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { Validators } from '@angular/forms';
import {
VTagPresentType,
isVTagPresentType,
VTagPresentTypeIdentifier,
} from './v-tag.typings';
import {
VReferencePresentType,
isVReferencePresentType,
VReferencePresentTypeIdentifier,
} from '../../v-reference/v-reference.typings';
import { XListAction, XListActionModel } from 'x-framework-components';
import { XSimpleListItem } from '../../typings/simple.list-item.typings';
@Component({
selector: 'v-tag',
templateUrl: './v-tag.component.html',
styleUrls: ['./v-tag.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VTagComponent extends VBaseDtoComponent<XTagDto> {
//
//#region Props ...
//
// Readonly Fields ...
readonly defaultModel: XTagDto = {
...defaultTagDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
@Input()
presentType: VTagPresentTypeIdentifier = VTagPresentType.AsItem;
/**
* Specifie Reference Present Type ...
*/
@Input()
referencePresentType: VReferencePresentTypeIdentifier =
VReferencePresentType.AsView;
/**
* References List Action Provider ...
*/
referencesListActionProvider = new Subject<XListActionModel>();
/**
* Default Model Field Descriptors ...
*/
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.id,
label: this.ResourceIDs.id,
value: this.defaultModel.id,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<number>,
//
// Tag ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.tag,
value: this.defaultModel.tag,
label: this.AppResourceIDs.tag,
type: this.ModelFieldTypes.String,
valueProvider: (value) => {
return this.applyLocale(value);
},
formControlConfig: {
index: 0,
propName: this.FieldNames.tag,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.tag,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// References ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.references,
value: this.defaultModel.references,
label: this.AppResourceIDs.references,
type: this.ModelFieldTypes.Array,
showContentBelowLabel: true,
pushArrayChildsOnceToTemplate: true,
hasCustomTemplate: true,
} as VModelFieldDescriptor<Array<XReferenceDto<any>>>,
];
@ViewChild('referencesRef')
referencesRef!: TemplateRef<any>;
@ViewChild('asItemTemplateRef')
asItemTemplateRef!: TemplateRef<any>;
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleAttachTemplates();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (!isVTagPresentType(this.presentType)) {
//
applyChanges = true;
this.presentType = VTagPresentType.AsItem;
}
}
//
// Reference Present Type ...
isChanged = changeKeys.includes(
nameofInstance(this, 'referencePresentType'),
);
if (isChanged) {
//
if (!isVReferencePresentType(this.referencePresentType)) {
//
applyChanges = true;
this.referencePresentType = VReferencePresentType.AsView;
}
}
//
if (applyChanges) {
this.detectChanges();
}
//
this.handleAttachTemplates();
}
//#endregion
//
//#region Providers ...
referencesListItems: Array<XSimpleListItem<any>> = [];
getReferencesListItems(references: Array<any>) {
//
if (this.hasChild(this.referencesListItems)) {
return this.referencesListItems;
}
//
this.referencesListItems = [
...references.map((r) => {
return {
data: { ...r },
} as XSimpleListItem<any>;
}),
];
//
this.referencesListActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() =>
this.referencesListActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() =>
this.referencesListActionProvider.next({ action: XListAction.Refresh }),
500,
);
//
return this.referencesListItems;
}
//#endregion
//
//#region Abstraction ...
/**
* Prepare Model Fields ...
*
* @param model
* @returns
*/
prepare(model?: XTagDto): XTagDto {
return prepareTagDtoFields(model);
}
/**
* Handle Model Validation ...
*
* @param model
* @returns
*/
validate(model?: Partial<XTagDto>): VModelValidationResult<XTagDto> {
//
let result: VModelValidationResult<XTagDto> = undefined;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// Tag ...
isValid = !isNullOrEmptyString(normalized.tag);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.tag,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd =
isNullOrUndefined(normalized.id) || normalized.id <= 0;
const isValidForUpdate =
!isNullOrUndefined(normalized.id) && normalized.id > 0;
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
onModelAssigned(): void {
super.onModelAssigned();
//
// Reset List Items if Exists for Renewing ...
let canReset = this.hasChild(this.referencesListItems);
if (canReset) {
this.referencesListItems = [];
}
}
/**
* Attach Custom Present Types ...
*
* @param presentType
* @returns
*/
getCustomPresentTypeTemplate(presentType: string): TemplateRef<any> {
//
let result: TemplateRef<any> = undefined;
//
if (!isNullOrEmptyString(presentType) && isVTagPresentType(presentType)) {
//
switch (presentType) {
//
case VTagPresentType.AsItem:
result = this.asItemTemplateRef;
break;
}
}
//
return result;
}
//#endregion
//
//#region Private ...
private handleAttachTemplates() {
//
if (isNullOrUndefined(this.referencesRef)) {
return;
}
//
this.defaultFieldDescriptors = this.fieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.references &&
fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.referencesRef
: fd.template,
};
});
//
if (!isNullOrUndefined(this.providedFieldDescriptors)) {
//
this.providedFieldDescriptors = this.providedFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.references &&
fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.referencesRef
: fd.template,
};
},
);
}
//
this.resetFieldDescriptors();
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag\v-tag.typings.ts
import { XBaseActionModel } from 'x-framework-components';
import { isValueInEnum } from '../../tools/v-base.tools';
//
//#region Present Type ...
export enum VTagPresentType {
//
AsNone = 'as_none',
AsForm = 'as_form',
AsView = 'as_view',
AsItem = 'as_item',
}
export type VTagPresentTypeIdentifier = VTagPresentType | string;
export function isVTagPresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VTagPresentType));
}
//#endregion
//
//#region Action Provider ...
export enum VTagAction {
Refresh = 'Refresh',
}
export type VTagActionIdentifier = VTagAction | string;
export interface VTagActionModel extends XBaseActionModel<VTagAction> {}
export function isVTagAction(value: string) {
return isValueInEnum(value, Object.assign({}, VTagAction));
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag-input\v-tag-input.component.html
<section
#vBaseDialogContainer
class="v-base-dialog"
[isForeground]="false"
[xColor]="getValue(backgroundColor) | async"
>
<!-- Title Toolbar -->
<section #vBaseDialogToolbar>
<!-- Toolbar -->
<ion-toolbar
#vToolbar
class="v-base-dialog-toolbar"
[xColor]="getValue(toolbarColor) | async"
>
<!-- Slots -->
<div class="v-base-dialog-toolbar-area">
<x-slotter>
<!-- Start -->
<x-slot
[name]="SlotNames.START"
[cssClass]="['ion-text-start', 'ion-float-start']"
>
<div
matRipple
(click)="handleBackPressed()"
[matTooltip]="getResourceValue(ResourceIDs.back) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="backIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
<!-- Center -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<ion-title class="v-base-dialog-toolbar-item center-vertical-flex">
<h3>{{ getToolbarTitle() }}</h3>
</ion-title>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.END"
[cssClass]="['ion-text-end', 'ion-float-end']"
>
</x-slot>
</x-slotter>
</div>
</ion-toolbar>
<!-- Progressbar -->
<ion-progress-bar
type="indeterminate"
*ngIf="getValue(showProgrssbar) | async"
[color]="getValue(progressbarColor) | async"
></ion-progress-bar>
</section>
<!-- Components -->
<section class="v-base-dialog-content">
<!-- Content -->
<ng-container *ngTemplateOutlet="contentRef"></ng-container>
</section>
</section>
<!-- -->
<!-- Template -->
<!-- -->
<!-- Content Template -->
<ng-template #contentRef>
<div class="v-container v-tag-container">
<v-tag
[model]="model"
[isInPage]="true"
[wrapWithCard]="false"
[presentType]="presentType"
(changeNotifier)="handleTagValueChanged($event)"
(dtoModelActionFired)="handleDtoActionFired($event)"
></v-tag>
</div>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag-input\v-tag-input.component.scss
@import "../../../../theme/theme.scss";
:host {
--dialog-toolbar-height: 0px;
--background: var(--x-color-light-shade);
}
::ng-deep.fullscreen {
--width: 100% !important;
--height: 100% !important;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 !important;
}
.v-base-dialog {
width: 100%;
height: 100%;
// height: calc(100% - var(--dialog-toolbar-height));
// margin-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-content {
height: 100%;
//
padding-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-toolbar-area {
margin: 8px;
}
.v-base-dialog-toolbar-item {
width: auto;
height: 48px;
}
.v-base-dialog-toolbar-button {
width: 32px;
margin: {
left: 4px;
right: 4px;
}
}
.v-webinar-screen {
padding: 16px;
}
//
// Non Global ...
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
.v-container {
height: 100%;
padding: 16px;
overflow: scroll;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag-input\v-tag-input.component.ts
import {
Input,
ViewChild,
Component,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
VTagInputModalContext,
VTagInputModalContextIdentifier,
VTagInputReturnActionIdentifier,
} from './v-tag-input.typings';
import {
XSlotName,
XIconNames,
XSlotLayout,
XSlotterActionModel,
} from 'x-framework-components';
import { Subject } from 'rxjs';
import {
VModelState,
VBaseDtoModelAction,
VBaseDtoPresentType,
VBaseDtoModelFiredActionModel,
} from '../../v-dto/v-dto.typings';
import { XTagDto } from 'x-saherelm-api-sdk';
import { VBaseComponent } from '../../classes/v-base.component';
@Component({
selector: 'v-tag-input',
templateUrl: './v-tag-input.component.html',
styleUrls: ['./v-tag-input.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VTagInputComponent extends VBaseComponent {
//
//#region Properties ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
/**
* retrieve Toolbar container ViewChild
* to Calculate Toolbar Height ...
*/
@ViewChild('vBaseDialogToolbar')
vBaseDialogToolbarContainer!: ElementRef;
/**
* Specified Modal Type ...
*/
@Input()
context: VTagInputModalContextIdentifier;
@Input()
model: XTagDto;
edittedModel: XTagDto = undefined;
@Input()
presentType: VBaseDtoPresentType;
/**
* Background Color of Component ...
*/
@Input()
backgroundColor: XColorIdentifier = XColorWithBrightness.Light;
@Input()
toolbarTitle: XResourceIdentifier = '';
/**
* Toolbar Color ...
*/
@Input()
toolbarColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
/**
* Progressbar Color ...
*/
@Input()
progressbarColor: XColorIdentifier = XColorWithBrightness.Warning;
showProgrssbar = false;
/**
* Action Icon Names Specifiecations ...
*/
@Input()
cancelIcon = XIconNames.close;
@Input()
backIcon = XIconNames.arrow_back;
//
slotterActionProvider = new Subject<XSlotterActionModel>();
//#endregion
//
//#region Provider ...
getToolbarTitle() {
//
let result = '';
//
if (!isNullOrUndefined(this.toolbarTitle)) {
result = this.resourceProvider(this.toolbarTitle);
}
//
return result;
}
//#endregion
//
//#region Handlers ...
/**
* Use for Handle Modela Closing Action based on Context ...
* @returns
*/
async handleBackPressed(action?: VTagInputReturnActionIdentifier) {
//
if (
this.loading ||
this.uiDisabled ||
!isNullOrUndefined(this.error) ||
isNullOrUndefined(this.context) ||
isNullOrEmptyString(this.context)
) {
return;
}
//
await this.handleCloseDialog(action);
}
/**
* Handle Model State Changed ...
*
* @param event
*/
handleTagValueChanged(event: VModelState<XTagDto>) {
//
this.edittedModel = {
...(event.model as Required<XTagDto>),
};
}
async handleDtoActionFired(event: VBaseDtoModelFiredActionModel) {
//
if (event.action === VBaseDtoModelAction.AddOrUpdate) {
//
let model = undefined;
if (event.payload.isValidForAdd || event.payload.isValidForUpdate) {
model = {
...this.edittedModel,
};
}
//
await this.handleCloseDialog(model);
} else if (event.action === VBaseDtoModelAction.Cancel) {
//
await this.doLockBreakAction(async () => {
await this.handleBackPressed(undefined);
});
}
}
/**
* Use for Handle Modal Closing Action based on Context ...
*
* @returns
*/
private async handleCloseDialog(
action?: VTagInputReturnActionIdentifier | any,
) {
//
if (isNullOrUndefined(this.context) || isNullOrEmptyString(this.context)) {
return;
}
//
switch (this.context) {
//
case VTagInputModalContext.Modal:
await this.managerService.dialogService.modalController.dismiss(action);
break;
//
case VTagInputModalContext.PopOver:
await this.managerService.dialogService.popoverController.dismiss(
action,
);
break;
}
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tag-input\v-tag-input.typings.ts
export enum VTagInputModalContext {
Modal = 'modal',
PopOver = 'pop_over',
}
export type VTagInputModalContextIdentifier = VTagInputModalContext | string;
export enum VTagInputCssVar {
DialogToolbarHeight = 'dialog-toolbar-height',
}
export enum VTagInputReturnAction {
}
export type VTagInputReturnActionIdentifier = VTagInputReturnAction | string;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tags-presenter\v-tags-presenter.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[loading]="loading"
[uiDisabled]="uiDisabled"
[class.v-no-margin-card]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
class="v-card v-tags-card v-component-card"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
*ngIf="canPresent()"
[ngClass]="getArrayValue(cssClass) | async"
class="v-tags-container v-component-wrapper unselecteable"
>
<!-- Title -->
<ng-container *ngIf="showLabel && notNullOrUndefinedValue(title) | async">
<ng-container *ngTemplateOutlet="titleRef"></ng-container>
</ng-container>
<!-- Attach Tags -->
<ng-container *ngIf="canAttach">
<ng-container *ngTemplateOutlet="tagAttachRef"></ng-container>
</ng-container>
<!-- Handle Different Present Types -->
<ng-container
*ngTemplateOutlet="
presentType === TagsPresenterPresentTypes.AsList
? asListPresentTypeRef
: presentType === TagsPresenterPresentTypes.AsItems
? asItemsPresentTypeRef
: presentType === TagsPresenterPresentTypes.AsChips
? asChipsPresentTypeRef
: nothingPresentRef
"
></ng-container>
</section>
</ng-template>
<!-- -->
<!-- Present Templates -->
<!-- -->
<!-- Nothing Template -->
<ng-template #nothingPresentRef></ng-template>
<!-- AsList Template -->
<ng-template #asListPresentTypeRef>
<section
class="v-tags-as-list-wrapper"
>
<x-list
[loading]="loading"
[items]="tagsListItems"
[emptyHasRefresh]="false"
[color]="tagBackgroundColor"
[itemTemplate]="listItemTemplate"
[uiDisabled]="loading || uiDisabled"
[actionProvider]="listActionProvider"
(optionSelected)="handleListItemsOptionSelected($event)"
></x-list>
</section>
</ng-template>
<!-- AsItems Template -->
<ng-template #asItemsPresentTypeRef>
<section class="v-tags-as-items-wrapper" *ngIf="hasChildValue(tags) | async">
<section
class="v-tag-content"
[xColor]="tagBackgroundColor"
*ngFor="let tag of tags"
>
<!-- Actions -->
<ng-container class="v-tag-actions" *ngIf="hasTagActions(tag)">
<ng-container
*ngTemplateOutlet="
tagActionsRef;
context: { tag: tag, actions: getTagActions(tag) }
"
></ng-container>
</ng-container>
<!-- Tag Title -->
<ng-container
*ngTemplateOutlet="tagTitleRef; context: { tag: tag }"
></ng-container>
</section>
</section>
</ng-template>
<!-- AsChips Template -->
<ng-template #asChipsPresentTypeRef>
<section class="v-tags-as-chips-wrapper" *ngIf="hasChildValue(tags) | async">
<mat-chip-list>
<!-- Presenting Chip -->
<mat-chip [xColor]="tagBackgroundColor" *ngFor="let tag of tags">
<!-- Actions -->
<ng-container class="v-tag-actions" *ngIf="hasTagActions(tag)">
<ng-container
*ngTemplateOutlet="
tagActionsRef;
context: { tag: tag, actions: getTagActions(tag) }
"
></ng-container>
<!-- Tag Title -->
<ng-container
*ngTemplateOutlet="tagTitleRef; context: { tag: tag }"
></ng-container>
</ng-container>
</mat-chip>
</mat-chip-list>
</section>
</ng-template>
<!-- -->
<!-- Other Templates -->
<!-- -->
<!-- Tag Attach Ref -->
<ng-template #tagAttachRef>
<x-action-bar [actions]="actionBarItems"></x-action-bar>
</ng-template>
<!-- actionsRef -->
<ng-template #tagActionsRef let-tag="tag" let-actions="actions">
<!-- Loop Through Actions -->
<ng-container *ngFor="let action of actions">
<x-icon
[clickable]="true"
[loading]="loading"
[name]="action.icon"
[color]="action.color"
[uiDisabled]="uiDisabled"
(clicked)="handleActionClicked(tag, action)"
></x-icon>
</ng-container>
</ng-template>
<!-- List Item Template -->
<ng-template #listItemTemplate let-item="item">
<section class="v-tag-list-item">
<ng-container
*ngTemplateOutlet="tagTitleRef; context: { tag: item.data }"
></ng-container>
</section>
</ng-template>
<!-- Title Template -->
<ng-template #titleRef>
<span
class="v-tags-label"
[isForeground]="true"
[xColor]="titleColor"
*ngIf="showLabel && notNullOrUndefinedValue(title) | async"
>
{{ getResourceValue(title) | async }}
</span>
</ng-template>
<!-- TagTitle Template -->
<ng-template #tagTitleRef let-tag="tag">
<section [xColor]="tagLabelColor" [isForeground]="true">{{ tag }}</section>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tags-presenter\v-tags-presenter.component.scss
.v-tags-label {
//
&::after {
content: ":";
}
//
padding: {
top: 3px;
bottom: 3px;
}
}
.v-component-wrapper {
padding: {
bottom: 6px;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tags-presenter\v-tags-presenter.component.ts
import {
Input,
Output,
Component,
EventEmitter,
ChangeDetectionStrategy,
} from '@angular/core';
import {
isNumber,
toNumber,
isFunction,
nameofInstance,
toNormalString,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
} from 'x-framework-core';
import {
XListItem,
XListAction,
XListActionModel,
XActionBarItemSlot,
XListItemOptionSelectedEvent,
} from 'x-framework-components';
import {
VTagsPresenterAction,
isVTagsPresenterAction,
VTagsPresenterModelState,
VTagsPresenterPresentType,
VTagsPresenterActionModel,
isVTagsPresenterPeresentType,
VTagsPresenterPresentTypeIdentfier,
} from './v-tags-presenter.typings';
import { Observable, Subject } from 'rxjs';
import { defaultTagDto, XTagDto } from 'x-saherelm-api-sdk';
import { VBaseDtoPresentType } from '../../v-dto/v-dto.typings';
import { VBaseInCardComponent } from '../../classes/v-base.component';
import { VTagInputComponent } from '../v-tag-input/v-tag-input.component';
import { VTagInputModalContext } from '../v-tag-input/v-tag-input.typings';
import { XSimpleActionBarItem } from '../../typings/simple.actionbar.typings';
import { defaultRemoveSlideOption } from '../../constants/slide.options.defaults';
import { XSimpleListItemSlideOption } from '../../typings/simple.list-item.typings';
@Component({
selector: 'v-tags-presenter',
templateUrl: './v-tags-presenter.component.html',
styleUrls: ['./v-tags-presenter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VTagsPresenterComponent extends VBaseInCardComponent {
//
//#region Props ...
readonly TagsPresenterPresentTypes = Object.assign(
{},
VTagsPresenterPresentType,
);
readonly TagPresentTypes = Object.assign({}, VBaseDtoPresentType);
@Input()
showLabel: boolean = true;
@Input()
cssClass: Array<string> = [];
@Input()
title: XResourceIdentifier = this.AppResourceIDs.tags;
@Input()
titleColor: XColorIdentifier = this.ColorNames.Tertiary;
@Input()
presentType: VTagsPresenterPresentTypeIdentfier =
VTagsPresenterPresentType.AsChips;
@Input()
tagLabelColor: XColorIdentifier = this.ColorNames.TertiaryShade;
@Input()
tagBackgroundColor: XColorIdentifier = this.ColorNames.LightShade;
@Input()
tagsReader: Observable<Array<string>> = undefined;
@Input()
tagActionsProvider: (tag: string) => Array<XSimpleListItemSlideOption> = (
t,
) => {
return [
//
// Remove ...
{
...defaultRemoveSlideOption,
icon: this.IconNames.clear,
},
];
};
@Input()
actionProvider = new Subject<VTagsPresenterActionModel>();
@Input()
canAttach: boolean = false;
actionBarItems: Array<XSimpleActionBarItem> = [];
/**
* Model State Changed Notifier ...
*/
@Output()
modelStateChanged = new EventEmitter<VTagsPresenterModelState>();
//
tags: Array<string> = [];
originalTags: Array<string> = [];
tagsListItems: Array<XListItem<string>> = [];
listActionProvider = new Subject<XListActionModel>();
//#endregion
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
super.afterViewInit();
//
await this.handleReadingTags();
this.handleRegisterActionProvider();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Tags Reader ...
isChanged = changeKeys.includes(nameofInstance(this, 'tagsReader'));
if (isChanged) {
//
if (!isNullOrUndefined(this.tagsReader)) {
await this.handleReadingTags();
}
}
//
// Can Attach ...
isChanged = changeKeys.includes(nameofInstance(this, 'canAttach'));
if (isChanged) {
//
if (this.canAttach) {
//
applyChanges = true;
this.prepareActionBarItems();
}
}
//
// Tag Action Provider ...
isChanged = changeKeys.includes(nameofInstance(this, 'tagActionsProvider'));
if (isChanged) {
//
if (
isNullOrUndefined(this.tagActionsProvider) ||
!isFunction(this.tagActionsProvider)
) {
//
applyChanges = true;
this.tagActionsProvider = (t) => {
return [
//
// Remove ...
{
...defaultRemoveSlideOption,
icon: this.IconNames.clear,
},
];
};
}
}
//
// Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (
isNullOrUndefined(this.presentType) &&
!isVTagsPresenterPeresentType(this.presentType)
) {
this.presentType = VTagsPresenterPresentType.AsItems;
}
//
await this.handlePresentTypeChanged();
}
//
// Action Provider ...
isChanged = changeKeys.includes(nameofInstance(this, 'actionProvider'));
if (isChanged) {
//
if (isNullOrUndefined(this.actionProvider)) {
//
applyChanges = true;
this.actionProvider = new Subject<VTagsPresenterActionModel>();
}
//
this.handleRegisterActionProvider();
}
//
// Title Color ...
isChanged = changeKeys.includes(nameofInstance(this, 'titleColor'));
if (isChanged) {
//
if (isNullOrEmptyString(this.titleColor)) {
//
applyChanges = true;
this.titleColor = this.ColorNames.Tertiary;
}
}
//
// Tag Label Color ...
isChanged = changeKeys.includes(nameofInstance(this, 'tagLabelColor'));
if (isChanged) {
//
if (isNullOrEmptyString(this.tagLabelColor)) {
//
applyChanges = true;
this.tagLabelColor = this.ColorNames.TertiaryShade;
}
}
//
// Tag Background Color ...
isChanged = changeKeys.includes(nameofInstance(this, 'tagBackgroundColor'));
if (isChanged) {
//
if (isNullOrEmptyString(this.tagBackgroundColor)) {
//
applyChanges = true;
this.tagBackgroundColor = this.ColorNames.LightShade;
}
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region Providers ...
canPresent() {
return (
!isNullOrUndefined(this.presentType) &&
isVTagsPresenterPeresentType(this.presentType)
);
}
hasTagActions(tag: string) {
//
let result = false;
//
if (!isNullOrEmptyString(tag)) {
//
const actions = this.tagActionsProvider(tag);
result = !isNullOrUndefined(actions) && this.hasChild(actions);
}
//
return result;
}
getTagActions(tag: string) {
return this.tagActionsProvider(tag);
}
//#endregion
//
//#region Handlers ...
/**
* Reading Tags List ...
*
* @returns
*/
async handleReadingTags() {
//
if (isNullOrUndefined(this.tagsReader)) {
return;
}
//
const tags = await this.getValueAsync(this.tagsReader);
if (!isNullOrUndefined(tags) && this.hasChild(tags)) {
//
this.tags = [...tags];
this.originalTags = [...tags];
//
await this.handlePresentTypeChanged();
}
}
/**
* Handle Present Type Changed ...
*
* @returns
*/
async handlePresentTypeChanged() {
//
this.tagsListItems = [];
//
if (
!this.hasChild(this.tags) ||
isNullOrUndefined(this.presentType) ||
!isVTagsPresenterPeresentType(this.presentType) ||
this.presentType === this.TagsPresenterPresentTypes.AsNone
) {
return;
}
//
if (this.presentType === this.TagsPresenterPresentTypes.AsList) {
//
this.tagsListItems = [
...this.tags.map((t) => {
return {
data: t,
slideOptions: this.tagActionsProvider(t),
} as XListItem<string>;
}),
];
//
this.detectChanges();
this.listActionProvider.next({ action: XListAction.Refresh });
}
}
/**
* Handle Adding new Tag ...
*
* @param tag
* @returns
*/
handleAddTag(tag: XTagDto) {
//
// Validate ...
if (isNullOrUndefined(tag) && isNullOrEmptyString(tag.tag)) {
return;
}
//
// Check Tag Exists ...
var isExists =
!isNullOrUndefined(this.tags) &&
this.hasChild(this.tags) &&
this.tags.findIndex(
(t) => toNormalString(t) === toNormalString(tag.tag),
) >= 0;
if (isExists) {
return;
}
//
// Update Lists ...
//
this.tags = [...this.tags, tag.tag];
//
if (this.presentType === this.TagsPresenterPresentTypes.AsList) {
//
this.tagsListItems = [
...this.tagsListItems,
{
data: tag.tag,
slideOptions: this.tagActionsProvider(tag.tag),
},
];
//
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
}
//
this.detectChanges();
this.handlePrepareTagsState();
}
/**
* Handle Actions ...
*
* @param tag
* @param action
*/
handleActionClicked(tag: string, action: XSimpleListItemSlideOption) {
//
// Validate ...
if (
this.loading ||
this.uiDisabled ||
isNullOrEmptyString(tag) ||
isNullOrUndefined(action) ||
isNullOrEmptyString(action.id)
) {
return;
}
//
// Remove ...
if (action.id === defaultRemoveSlideOption.id) {
//
let tagIDX =
!isNullOrUndefined(this.tags) && this.hasChild(this.tags)
? this.tags.findIndex(
(t) => toNormalString(t) === toNormalString(tag),
)
: -1;
let isExists = tagIDX >= 0;
if (isExists) {
//
this.tags = [
...this.tags.filter((t) => toNormalString(t) !== toNormalString(tag)),
];
//
// Checking in List Items ...
let tagIDX = this.tagsListItems.findIndex(
(tli) => toNormalString(tli.data) === toNormalString(tag),
);
if (tagIDX >= 0) {
//
this.tagsListItems = [
...this.tagsListItems.filter(
(tli) => toNormalString(tli.data) !== toNormalString(tag),
),
];
//
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
200,
);
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
}
//
this.detectChanges();
this.handlePrepareTagsState();
}
}
}
/**
* Prepare and Notify Tag State ...
*
* @returns
*/
handlePrepareTagsState() {
//
const hasOriginals =
!isNullOrUndefined(this.originalTags) && this.hasChild(this.originalTags);
//
// Detecting News ...
let hasNews = false;
let news: Array<string> = [];
if (this.hasChild(this.tags)) {
//
news = this.tags.filter(
(t) =>
!this.hasChild(this.originalTags) ||
(this.hasChild(this.originalTags) &&
this.originalTags.findIndex(
(ot) => toNormalString(t) === toNormalString(ot),
) < 0),
);
//
hasNews = this.hasChild(news);
}
//
// Detecting Removeds ...
let hasRemoveds = false;
let removeds: Array<string> = [];
if (hasOriginals) {
//
removeds = this.originalTags.filter(
(ot) =>
this.tags.findIndex((t) => toNormalString(ot) === toNormalString(t)) <
0,
);
//
hasRemoveds = this.hasChild(removeds);
}
//
let result: VTagsPresenterModelState = {
hasNews,
hasRemoveds,
hasOriginals,
//
news,
removeds,
originals: [...this.originalTags],
};
//
this.modelStateChanged.emit(result);
this.listActionProvider.next({ action: XListAction.Refresh });
//
return result;
}
/**
* Handle List Item Options Clicked ...
*
* @param event
* @returns
*/
handleListItemsOptionSelected(event: XListItemOptionSelectedEvent) {
//
if (isNullOrUndefined(event)) {
return;
}
//
if (event.id === defaultRemoveSlideOption.id) {
this.handleActionClicked(event.data.data, defaultRemoveSlideOption);
}
}
//#endregion
//
//#region Private ...
/**
* Preparing Action Bar ...
*/
private prepareActionBarItems() {
//
this.actionBarItems = [
//
// Attach ...
{
index: 0,
id: 'Add',
onlyIcon: true,
icon: this.IconNames.add,
title: this.ResourceIDs.add,
slot: XActionBarItemSlot.START,
iconColor: this.ColorNames.SuccessShade,
handler: async () => {
//
let modal = await this.managerService.dialogService.presentModal({
autoHeight: true,
fullScreen: false,
backdropDismiss: false,
component: VTagInputComponent,
componentProps: {
context: VTagInputModalContext.Modal,
model: {
...defaultTagDto,
},
toolbarTitle: this.AppResourceIDs.tag,
presentType: VBaseDtoPresentType.AsForm,
},
});
/**
* wait for Model Result ...
*/
const data = await modal.onDidDismiss();
modal.remove();
modal.dismiss();
/**
* Handle Retrieve Data ...
*/
if (!isNullOrUndefined(data.data)) {
this.handleAddTag(data.data);
}
},
disabled: this.loading || this.uiDisabled || !this.canAttach,
} as XSimpleActionBarItem,
];
}
//
//#region Action Provider ...
/**
* Handle Action Provider Registration ...
*/
private handleRegisterActionProvider() {
//
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.applyTakeUntilWrapper(this.actionProvider.asObservable()).subscribe(
async (event) => {
//
// Validate ...
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVTagsPresenterAction(event.action)
) {
return;
}
//
await this.handleAction(event.action, event.payload);
},
);
}
/**
* Send Specified Action through Action provider ...
*
* @param action
* @param payload
* @returns
*/
private sendAction(action: VTagsPresenterAction, payload?: any) {
//
if (
isNullOrUndefined(this.actionProvider) ||
isNullOrEmptyString(action) ||
!isVTagsPresenterAction(action)
) {
return;
}
//
this.actionProvider.next({ action, payload });
}
/**
* Handle Specified Actions ...
*
* @param action
* @param payload
*/
private async handleAction(action: VTagsPresenterAction, payload?: any) {
//
// Refresh ...
if (action === VTagsPresenterAction.Refresh) {
//
if (isNullOrUndefined(payload)) {
this.detectChanges();
} else if (!isNullOrUndefined(payload) && isNumber(payload)) {
//
this.detectChanges();
setTimeout(() => this.detectChanges(), toNumber(payload));
}
}
//
// Reset ...
else if (action === VTagsPresenterAction.Reset) {
//
this.tags = [...this.originalTags];
//
this.tagsListItems = [
...this.originalTags.map((ot) => {
return {
data: ot,
slideOptions: this.tagActionsProvider(ot),
} as XListItem<string>;
}),
];
//
this.detectChanges();
this.listActionProvider.next({ action: XListAction.Refresh });
setTimeout(
() => this.listActionProvider.next({ action: XListAction.Refresh }),
500,
);
this.handlePrepareTagsState();
}
//
// ReState ...
else if (action === VTagsPresenterAction.ReState) {
this.handlePrepareTagsState();
}
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tags\v-tags-presenter\v-tags-presenter.typings.ts
import { isValueInEnum } from '../../tools/v-base.tools';
import { XBaseActionModel } from 'x-framework-components';
//
//#region Present Type ...
export enum VTagsPresenterPresentType {
AsNone = 'as_none',
AsList = 'as_list',
AsItems = 'as_items',
AsChips = 'as_chips',
}
export type VTagsPresenterPresentTypeIdentfier =
| VTagsPresenterPresentType
| string;
export function isVTagsPresenterPeresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VTagsPresenterPresentType));
}
//#endregion
//
//#region Action Provider ...
export enum VTagsPresenterAction {
Reset = 'Reset',
Refresh = 'Refresh',
ReState = 'ReState',
}
export type VTagsPresenterActionIdentifier = VTagsPresenterAction | string;
export interface VTagsPresenterActionModel
extends XBaseActionModel<VTagsPresenterAction> {}
export function isVTagsPresenterAction(value: string) {
return isValueInEnum(value, Object.assign({}, VTagsPresenterAction));
}
//#endregion
//
//#region Tags State ...
/**
* an Interface to Notify Tags Changed ...
*/
export interface VTagsPresenterModelState {
hasOriginals: boolean;
hasNews: boolean;
hasRemoveds: boolean;
originals?: Array<string>;
news?: Array<string>;
removeds?: Array<string>;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tests\test-video-player.html
<!-- Video Presenter -->
<!-- Sample Video -->
<v-video
[title]="'Salam'"
[isInPage]="true"
[autoPlay]="false"
[showTitle]="true"
[showActions]="true"
[wrapWithCard]="true"
[showVideoTime]="true"
[showSeekingBar]="true"
[continuesPlay]=" false"
[showProgressBar]="true"
[showRewindControl]="true"
[showForwardControl]="true"
[showPlayPauseControl]="true"
[showPlayPauseToggler]="false"
[enableKeyboardEvents]="false"
[showCurrentPlaybackRate]="true"
[showVolumeChangerControl]="true"
[showVolumeTogglerControl]="true"
[showPlaybackRatesControl]="true"
[showFullscreenTogglerControl]="true"
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
></v-video>
<!-- Video Presenter -->
<!-- Sample Audio -->
<v-video
[title]="'Salam'"
[isInPage]="true"
[autoPlay]="false"
[showTitle]="true"
[showActions]="true"
[wrapWithCard]="true"
[showVideoTime]="true"
[showSeekingBar]="true"
[continuesPlay]=" false"
[showProgressBar]="true"
[showRewindControl]="true"
[showForwardControl]="true"
[showPlayPauseControl]="true"
[showPlayPauseToggler]="false"
[enableKeyboardEvents]="false"
[showCurrentPlaybackRate]="true"
[showVolumeChangerControl]="true"
[showVolumeTogglerControl]="true"
[showPlaybackRatesControl]="true"
[showFullscreenTogglerControl]="true"
src="https://storage.googleapis.com/kagglesdsdata/datasets/829978/1417968/harvard.wav?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=databundle-worker-v2%40kaggle-161607.iam.gserviceaccount.com%2F20251120%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20251120T181954Z&X-Goog-Expires=345600&X-Goog-SignedHeaders=host&X-Goog-Signature=770d70a8084ccbfa0ea5bee80842c597ceaabb03f1ff04196e33356f27862fe9344e428448255a64cfb160f0f4a0560837fa6b9e7acc21d0916ed8cb1d8d5da6e2f9f20cbe787debba122902abeddbb408f783ab87ad94022d58d0f812bcf9b85174075592c622c7f0589982e129a64fcbc70b3543a87d695290bbd9009e46661678889213e4fc871ad3b720755be244eb8f1b55643fb680902e1510391b8cbecc9ee73ab7885573fc35eb98be9a09a42308fa5c2488a09f13c07e61adf1c1aa930830573ecb8c018414f43935de41b040e33feb61241612cabd844d915864e25e4297ce6990120c964c2d94cd7d017bfe3dbc207df68c6913f35200bb807cb9"
></v-video>
<!-- Video Presenter -->
<!-- Sample Image -->
<v-video
[title]="'Salam'"
[isInPage]="true"
[autoPlay]="false"
[showTitle]="true"
[showActions]="true"
[wrapWithCard]="true"
[showVideoTime]="true"
[showSeekingBar]="true"
[continuesPlay]=" false"
[showProgressBar]="true"
[showRewindControl]="true"
[showForwardControl]="true"
[showPlayPauseControl]="true"
[showPlayPauseToggler]="false"
[enableKeyboardEvents]="false"
[showCurrentPlaybackRate]="true"
[showVolumeChangerControl]="true"
[showVolumeTogglerControl]="true"
[showPlaybackRatesControl]="true"
[showFullscreenTogglerControl]="true"
poster="https://www.sony.co.jp/en/Products/di_photo-gallery/images/extralarge/1887.JPG"
></v-video>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tests\v-icons-presenter\template.html
<!-- Icons Presenter -->
<v-icons-presenter
[isInPage]="true"
[wrapWithCard]="true"
[cardTitle]="'Icons'"
></v-icons-presenter>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tests\v-icons-presenter\v-icons-presenter.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
class="v-card v-component-card"
[class.v-no-margin-card]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
[ngClass]="getArrayValue(cssClass) | async"
class="v-container v-component-wrapper unselecteable"
>
<!-- Icons -->
<x-list
#listPresenter
[showToolbar]="true"
[stickyToolbar]="true"
[showSearchBar]="true"
[showActionBar]="false"
[clickableItems]="true"
[items]="iconListItems"
[itemTemplate]="iconRef"
[searchBarColor]="ColorNames.Dark"
(itemSelected)="handleIconSelected($event)"
[searchBarSearchInputColor]="ColorNames.Dark"
>
</x-list>
</section>
</ng-template>
<!-- Icon Template -->
<ng-template #iconRef let-item="item">
<div class="cb-inherit ion-text-center">
<x-icon [name]="item.data" [color]="ColorNames.Tertiary"></x-icon>
{{ item.data }}
</div>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tests\v-icons-presenter\v-icons-presenter.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tests\v-icons-presenter\v-icons-presenter.component.ts
import { XIconIdentifier, XListItem } from 'x-framework-components';
import { VBaseInCardComponent } from '../../classes/v-base.component';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'v-icons-presenter',
templateUrl: './v-icons-presenter.component.html',
styleUrls: ['./v-icons-presenter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VIconsPresenterComponent extends VBaseInCardComponent {
//
//#region Props ...
@Input()
cssClass: Array<string>;
iconListItems: XListItem<XIconIdentifier>[] = [];
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handlePrepareIcons();
}
//#endregion
//
//#region Handlers ...
handlePrepareIcons() {
//
let iconNames = Object.keys(this.IconNames);
iconNames = iconNames.sort((a,b) => a.localeCompare(b));
console.log('iconNames: ', iconNames);
//
let items = iconNames.map((i) => {
return {
data: i,
} as XListItem<XIconIdentifier>;
});
//
this.iconListItems = [...items];
this.detectChanges();
}
async handleIconSelected(item: XListItem<string>) {
//
// text: this.resourceProvider(XResourceIDs.dismiss),
const iconText = `&lt;x-icon name='${item.data}'&gt;&lt;/x-icon&gt;`;
const iconCode = `<x-icon name='${item.data}'></x-icon>`;
await this.managerService.notificationService.presentInfoNotification({
message: iconText,
dissmissable: true,
opt: {
duration: 4000,
buttons: [
{
icon: this.IconNames.clipboard,
side: 'end',
role: 'set',
handler: () => {
//
console.log(iconCode);
const isSaved = this.managerService.saveToClipboard(iconCode);
if (isSaved) {
this.managerService.notificationService.presentDefaultSuccessNotification();
}
},
},
{
icon: this.IconNames.close,
side: 'end',
role: 'cancel',
},
],
},
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tools\action-provider.tools.ts
import { Subject } from 'rxjs';
import { XBaseActionModel } from 'x-framework-components';
import { isNullOrEmptyString, isNullOrUndefined } from 'x-framework-core';
export function sendAction(arg: {
provider: Subject<XBaseActionModel<any>>;
action: string;
payload?: any;
tries?: number;
delay?: number;
}) {
//
// Validate ...
let isValid =
!isNullOrUndefined(arg) &&
!isNullOrUndefined(arg.provider) &&
!isNullOrEmptyString(arg.action);
if (!isValid) {
return;
}
//
// Normalize Args ...
//
if (isNullOrUndefined(arg.tries)) {
arg.tries = 3;
}
//
if (isNullOrUndefined(arg.delay)) {
arg.delay = 200;
}
//
arg.provider.next({ action: arg.action, payload: arg.payload });
for (let i = 0; i < arg.tries; i++) {
//
let delay = (i + 1) * arg.delay;
setTimeout(() => {
arg.provider.next({ action: arg.action, payload: arg.payload });
}, delay);
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tools\date.tools.ts
import {
XLocale,
formatDate,
applyLocale,
getErrorDateFormatString,
getRegularDateFormatString,
getDateOfBirthDateFormatString,
} from 'x-framework-core';
import { XManagerService } from 'x-framework-services';
export enum XDateFormat {
ErroDate = 'error',
RegularDate = 'regular',
EventDate = 'event_date',
DateOfBirth = 'dateofbirth',
}
export type XDateFormatIdentifier = XDateFormat | string;
export function getDateFormatter(
type: XDateFormatIdentifier,
locale?: XLocale
) {
//
let result: string = getRegularDateFormatString(locale);
//
switch (type) {
//
case XDateFormat.RegularDate:
result = getRegularDateFormatString(locale);
break;
//
case XDateFormat.ErroDate:
result = getErrorDateFormatString(locale);
break;
//
case XDateFormat.EventDate:
result = getEventDateTimeFromatString(locale);
break;
//
case XDateFormat.DateOfBirth:
result = getDateOfBirthDateFormatString(locale);
break;
}
//
return result;
}
export function applyFormatDate(
value: Date,
format: XDateFormatIdentifier,
managerService: XManagerService,
localized = true,
isUTC = true
) {
//
const locale = managerService.currentLocale;
const formatter = getDateFormatter(format, locale);
let result = formatDate(value, locale, formatter, isUTC);
//
if (!!localized) {
result = applyLocale(result, locale);
}
//
return result;
}
export function getEventDateTimeFromatString(locale?: XLocale) {
//
if (locale === 'fa-IR') {
return 'HH:mm jYYYY/jMM/jDD';
}
//
return 'YYYY-MM-DD HH:mm';
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\tools\v-base.tools.ts
import {
XLocale,
toXString,
isValidEmail,
isNullOrUndefined,
isValidPhoneNumber,
isNullOrEmptyString,
keys,
isString,
} from 'x-framework-core';
/**
* generate an href string for linking to email addresses ...
*
* @param email the propper email address ...
*/
export function getEmailHref(email: string) {
//
if (isNullOrEmptyString(email) || !isValidEmail(email)) {
return '';
}
//
return `mailto:${email}`;
}
/**
* generate an href string for linking to phoneNumber ...
*
* @param phoneNumber the propper phone number ...
*/
export function getPhoneNumberHref(phoneNumber: string) {
//
if (isNullOrEmptyString(phoneNumber) || !isValidPhoneNumber(phoneNumber)) {
return '';
}
//
return `tel:${phoneNumber}`;
}
export function getEventDateFormatString(locale?: XLocale) {
//
if (locale === 'fa-IR') {
return 'HH:mm jYYYY/jMM/jDD';
}
//
return 'YYYY/MM/DD HH:mm';
}
export function getKeys(object: any) {
//
let result: Array<string> = [];
//
keys(object).forEach(key => {
//
if (isString(key)) {
result.push(key);
}
});
//
return result;
}
/**
* Generic Tools Function to Check A Value inside enum Values or not ...
*
* @param value
* @param values
* @param forceExact
*/
export function isValueInEnum<TValue>(
value: TValue,
values: Object,
forceExact: boolean = true
) {
//
let result = false;
//
if (
!isNullOrUndefined(value) &&
!isNullOrUndefined(values) &&
!isNullOrEmptyString(toXString(value))
) {
//
const tVals = Object.values(values).map((tv) => tv as TValue);
//
result = tVals.includes(value);
//
// Check Includes ...
if (!forceExact && !result) {
//
result =
tVals.findIndex((t) => toXString(value).includes(toXString(t))) >= 0;
}
}
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\common.typings.ts
import {
XKey,
isXKeyOf,
nameofType,
getComponentSelector,
} from 'x-framework-core';
import { ComponentRef, InjectionToken, Injector, Type } from '@angular/core';
export const markdownImagePattern = /(?:[!]\[(.*?)\]\(.*?\))/g;
export const X_COMPONENT_PROP_TOKEN = new InjectionToken('x_component_props');
export type TXComponentProps<T = any> = {
[key in keyof T]?: any;
};
/**
* Describe Specific Component ...
*/
export interface XComponentDescriptor<T = any> {
/**
* Use as Identifier ...
*/
payload?: any;
/**
* Component Type ...
*/
type: Type<T>;
/**
* Component Props ...
*/
inputs: TXComponentProps<T>;
/**
* Component Property Injector ...
*/
injector?: Injector;
/**
* Component Ref ...
*/
componentRef?: ComponentRef<T>;
}
//
// Extract a Query Selector of Specified Component which
// has Specified Attribute ...
export function getQuerySelectorOfSpecifiedAttribute<T>(
type: Type<T>,
attributeName: XKey<T> | string,
attributeValue: string
) {
//
let attributeNameString = isXKeyOf(type, attributeName)
? nameofType(type, attributeName as XKey<T>)
: attributeName.toString();
//
let result = `${getComponentSelector(
type
)}[${attributeNameString}="${attributeValue}"]`;
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\dto.common.typings.ts
import { IVBaseDtoContainer } from "../v-dto/v-dto.typings";
/**
* Commonly Used Properties in DTO Views ...
*/
export type VDtoCommonProperties = Partial<
Pick<
IVBaseDtoContainer,
| 'labelColor'
| 'valueColor'
| 'fieldFormButtonPosition'
| 'fieldFormIcon'
| 'fieldFormIconColor'
| 'fieldFormApplyIcon'
| 'fieldFormApplyIconColor'
| 'fieldFormCancelIcon'
| 'fieldFormCancelIconColor'
| 'isInPage'
| 'wrapWithCard'
| 'cardShowFooter'
| 'cardShowActions'
| 'removeCardMargin'
| 'cardForegroundColor'
| 'cardTitle'
| 'cardSubTitle'
| 'cardCssClass'
| 'cardColor'
| 'cardActionProvider'
| 'formAppearance'
| 'formActionProvider'
| 'formRegularColor'
| 'formAccentColor'
| 'formHintColor'
| 'formActiveColor'
| 'formValidColor'
| 'formInvalidColor'
| 'formPendingColor'
| 'formFillColor'
| 'formDisabledColor'
| 'formDisabledFillColor'
| 'showFormCancelButton'
| 'formCancelButtonResource'
| 'formCancelButtonColor'
| 'formResetButtonResource'
| 'formResetButtonColor'
| 'formActionButtonAddResource'
| 'formActionButtonUpdateResource'
| 'formActionButtonColor'
>
>;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\simple.actionbar.typings.ts
import {
XStandardType,
XColorIdentifier,
XResourceIdentifier,
} from 'x-framework-core';
import {
XIconIdentifier,
XActionBarActionFiredModel,
XActionBarItemSlotIdentifier,
} from 'x-framework-components';
export interface XSimpleActionBarItem {
index?: number;
id: string;
hidden?: boolean;
onlyIcon?: boolean;
disabled?: boolean;
icon: XIconIdentifier;
color?: XColorIdentifier;
title?: XResourceIdentifier;
iconColor?: XColorIdentifier;
titleColor?: XColorIdentifier;
cssClass?: Array<string>;
slot: XActionBarItemSlotIdentifier;
handler?: (model?: XActionBarActionFiredModel) => XStandardType<any>;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\simple.fab.typings.ts
import {
hasChild,
toPromise,
XStandardType,
XColorIdentifier,
XRouterDirection,
isNullOrUndefined,
XResourceIdentifier,
XRouterTargetIdentifier,
} from 'x-framework-core';
import {
XFabItem,
XFabButton,
XIconIdentifier,
XFabButtonSideIdentifier,
XFabButtonSizeIdentifier,
XFabVerticalSlotIdentifier,
XFabHorizontalSlotIdentifier,
} from 'x-framework-components';
export interface XSimpleFabButton {
id: string;
index: number;
href?: string;
hidden?: boolean;
download?: string;
disabled?: boolean;
icon: XIconIdentifier;
color: XColorIdentifier;
tooltip?: XResourceIdentifier;
side: XFabButtonSideIdentifier;
size?: XFabButtonSizeIdentifier;
routerDirection?: XRouterDirection;
routerTarget?: XRouterTargetIdentifier;
handler?: (id?: string) => XStandardType<any>;
}
declare type FAB_MAIN_BUTTON = Pick<
XSimpleFabButton,
| 'id'
| 'icon'
| 'size'
| 'color'
| 'hidden'
| 'handler'
| 'tooltip'
| 'disabled'
| 'href'
| 'download'
| 'routerDirection'
| 'routerTarget'
>;
export interface XSimpleFabItem extends FAB_MAIN_BUTTON {
vertical: XFabVerticalSlotIdentifier;
horizontal: XFabHorizontalSlotIdentifier;
fixed?: boolean;
edged?: boolean;
activated?: boolean;
childs?: Array<XSimpleFabButton>;
}
export async function toSimpleFabButton(item: XFabButton) {
//
let result: XSimpleFabButton = undefined;
if (isNullOrUndefined(item)) {
return result;
}
//
result = {
id: item.id,
index: item.index,
handler: item.handler,
icon: await toPromise(item.icon),
side: await toPromise(item.side),
size: await toPromise(item.size),
disabled: await toPromise(item.disabled),
hidden: await toPromise(item.hidden),
color: await toPromise(item.color),
tooltip: await toPromise(item.tooltip),
href: await toPromise(item.href),
download: await toPromise(item.download),
routerDirection: await toPromise(item.routerDirection),
routerTarget: await toPromise(item.routerTarget),
};
//
return result;
}
export async function toSimpleFabItems(
item: XFabItem | XStandardType<XFabItem>
) {
//
let result: XSimpleFabItem = undefined;
if (isNullOrUndefined(item)) {
return result;
}
//
const itemP = await toPromise(item);
if (isNullOrUndefined(itemP) || !hasChild(Object.keys(itemP))) {
return result;
}
//
result = {
handler: itemP.handler,
id: await toPromise(itemP.id),
icon: await toPromise(itemP.icon),
size: await toPromise(itemP.size),
color: await toPromise(itemP.color),
hidden: await toPromise(itemP.hidden),
tooltip: await toPromise(itemP.tooltip),
disabled: await toPromise(itemP.disabled),
href: await toPromise(itemP.href),
download: await toPromise(itemP.download),
routerDirection: await toPromise(itemP.routerDirection),
routerTarget: await toPromise(itemP.routerTarget),
vertical: await toPromise(itemP.vertical),
horizontal: await toPromise(itemP.horizontal),
fixed: await toPromise(itemP.fixed),
edged: await toPromise(itemP.edged),
activated: await toPromise(itemP.activated),
childs: isNullOrUndefined(itemP.childs)
? undefined
: await Promise.all(
(
await toPromise(itemP.childs)
).map(async (i) => await toSimpleFabButton(i))
),
};
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\simple.form.typings.ts
import {
XLocale,
XBaseDto,
XColorIdentifier,
XResourceIdentifier,
} from 'x-framework-core';
import {
XFormInputFormatterIdentifier,
XStringInputFormatTypeIdentifier,
} from 'x-framework-services';
import { Subject } from 'rxjs';
import {
XIconIdentifier,
XFormControlTypeConfig,
XFormUpdateOnIdentifier,
XFormValidatorDescriptor,
XFormControlTypeIdentifier,
XFormControlEventsDescriptor,
XControlIconPositionIdentifier,
XFormControlActionProviderModel,
XFormControlAppearanceIdentifier,
XFormControlLabelPositionIdentifier,
} from 'x-framework-components';
import { TemplateRef } from '@angular/core';
export interface XSimpleFormErrorHandler {
errorName: string;
errorMessage: XResourceIdentifier;
errorDataProvider?: (errorObject: any) => XResourceIdentifier;
}
export interface XSimpleFormControlTypeDescriptor {
type: XFormControlTypeIdentifier;
config?: XFormControlTypeConfig;
}
export interface XSimpleControlIconDescriptor {
handler?: () => void;
name?: XIconIdentifier;
color?: XColorIdentifier;
applyStateColor?: boolean;
tooltip?: XResourceIdentifier;
position?: XControlIconPositionIdentifier;
}
export interface XSimpleFormIconDescriptor
extends Pick<
Partial<XSimpleControlIconDescriptor>,
'handler' | 'name' | 'color' | 'applyStateColor' | 'tooltip'
> {}
export interface XSimpleFormControlIconDescriptor {
suffix?: XSimpleFormIconDescriptor;
prefix?: XSimpleFormIconDescriptor;
}
export interface XSimpleFormControlFormatterConfig {
locale?: XLocale;
seperator?: string;
type: XFormInputFormatterIdentifier;
inputType?: XStringInputFormatTypeIdentifier;
currencyLable?: XResourceIdentifier;
stringFormatter?: Array<number>;
}
export interface XSimpleFormControlProgressbarConfig {
color: XColorIdentifier;
}
export interface XSimpleFormControlAutoCompleteConfig<
T extends XBaseDto = any
> {
opened?: boolean;
color?: XColorIdentifier;
onSelect?: (item: T) => void;
items: () => Array<T>;
itemTemplate: TemplateRef<any>;
cssClass?: Array<string>;
onEscape?: () => void;
onOutsideClicked?: () => void;
}
export interface XSimpleFormControlAppearanceDescriptor {
hidden?: boolean;
disabled?: boolean;
hint?: XResourceIdentifier;
label: XResourceIdentifier;
tooltip?: XResourceIdentifier;
placeholder?: XResourceIdentifier;
cssClass?: Array<string>;
icons?: XSimpleFormControlIconDescriptor;
formatter?: XSimpleFormControlFormatterConfig;
progressBar?: XSimpleFormControlProgressbarConfig;
autoComplete?: XSimpleFormControlAutoCompleteConfig;
appearance?: XFormControlAppearanceIdentifier;
labelPosition?: XFormControlLabelPositionIdentifier;
}
//
//#region Controls Simple Config ...
//
//#region Select ...
export interface XSimpleFormSelectControlOption<T extends XBaseDto = any> {
value: T;
viewValue: string;
}
export interface XSimpleFormSelectControlConfig<T extends XBaseDto = any> {
options?: Array<XSimpleFormSelectControlOption<T>>;
multiple?: boolean;
}
//#endregion
//#endregion
export interface XSimpleFormControlConfig<T extends XBaseDto = any> {
index: number;
propName: keyof Partial<T>;
focusHandler?: Subject<any>;
type: XSimpleFormControlTypeDescriptor;
updateOn?: XFormUpdateOnIdentifier;
validators?: XFormValidatorDescriptor;
actionProvider?: Subject<XFormControlActionProviderModel>;
eventHandlers?: XFormControlEventsDescriptor;
appearance: XSimpleFormControlAppearanceDescriptor;
errorHandlers?: Array<XSimpleFormErrorHandler>;
}
export interface XSimpleFormConfig<T extends XBaseDto = any> {
name: string;
model?: Partial<T>;
updateOn?: XFormUpdateOnIdentifier;
validators?: XFormValidatorDescriptor;
controls: Array<XSimpleFormControlConfig<T>>;
errorHandlers?: Array<XSimpleFormErrorHandler>;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\simple.list-item.typings.ts
import {
toArray,
hasChild,
toPromise,
XStandardType,
XColorIdentifier,
isNullOrUndefined,
XResourceIdentifier,
} from 'x-framework-core';
import {
XListItem,
XIconIdentifier,
XListItemOptionSlot,
XListItemSlideOption,
} from 'x-framework-components';
/**
* Simple List Item ...
*/
export interface XSimpleListItem<T = any> {
data: T;
hide?: boolean;
disabled?: boolean;
isSelected?: boolean;
href?: Array<string>;
showDetailedIcon?: boolean;
slideOptions?: Array<XSimpleListItemSlideOption>;
}
/**
* Simple List Item ...
*/
export interface XSimpleListItemSlideOption {
id: string;
icon?: XIconIdentifier;
title?: XResourceIdentifier;
color?: XColorIdentifier;
onlyIcon?: boolean;
slot: XListItemOptionSlot;
handler?: (info?: any) => XStandardType<void>;
disabled?: boolean;
}
/**
* Converts a ListItemSlideOption to Simple Mode ...
*
* @param option
* @returns
*/
export async function toSimpleListItemSlideOption(
option: XListItemSlideOption
) {
//
let result: XSimpleListItemSlideOption = undefined;
//
if (!isNullOrUndefined(option)) {
//
result = {
id: option.id,
handler: option.handler,
icon: await toPromise(option.icon),
slot: await toPromise(option.slot),
title: await toPromise(option.title),
color: await toPromise(option.color),
disabled: await toPromise(option.disabled),
onlyIcon: await toPromise(option.onlyIcon),
};
}
//
return result;
}
/**
* Converts a ListItem<T> to Simple Mode ...
*
* @param item
* @returns
*/
export async function toSimpleListItem<T = any>(item: XListItem<T>) {
//
let result: XSimpleListItem = undefined;
//
if (!isNullOrUndefined(item)) {
//
result = {
data: await toPromise(item.data),
hide: await toPromise(item.hide),
disabled: await toPromise(item.disabled),
isSelected: await toPromise(item.isSelected),
showDetailedIcon: await toPromise(item.showDetailedIcon),
href: !isNullOrUndefined(item.href) ? toArray(item.href) : undefined,
slideOptions:
isNullOrUndefined(item.slideOptions) || !hasChild(item.slideOptions)
? []
: await Promise.all(
item.slideOptions.map(
async (sl) => await toSimpleListItemSlideOption(sl)
)
),
};
}
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\simple.page.typings.ts
import { XResourceIdentifier } from "x-framework-core";
export interface XSimplePage {
id: string;
name: string;
icon?: string;
baseRoute: string;
route: Array<string>;
childs?: Array<XSimplePage>;
title: XResourceIdentifier;
description?: XResourceIdentifier;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\v-base-form.typings.ts
import { XBaseActionModel } from 'x-framework-components';
export enum VBaseFormViewPresentType {
AsNone = 'as_none',
AsForm = 'as_form',
AsDescriptionList = 'as_description_list',
}
export type VBaseFormViewPresentTypeIdentifier =
| VBaseFormViewPresentType
| string;
export enum VBaseFormViewAction {
//
Refresh = 'refresh',
SetError = 'set_error',
ClearError = 'clear_error',
ClearForm = 'clear_form',
ResetForm = 'reset_form',
}
export type VBaseFormViewActionIdentifier = VBaseFormViewAction | string;
export interface VBaseFormViewActionModel
extends XBaseActionModel<VBaseFormViewActionIdentifier> {}
export interface VBaseFormViewCustomStates {
/**
* State Management Result ...
*/
isBase: boolean;
isValid: boolean;
isDefault: boolean;
isChanged: boolean;
/**
* payload carry changed value ...
*/
payload?: any;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\typings\x.state.enum.ts
export enum XAppState {
UserProfile = 'user_profile',
}
export type AppStateIdentifier = XAppState | string;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-dto\v-dto.component.html
<!-- Component Template -->
<ng-container
*ngIf="getValue(dtoContainer.wrapWithCard) | async; else componentsRef"
>
<x-card
class="v-card v-dto-card unselecteable"
[loading]="loading || dtoContainer.loading"
[actionProvider]="dtoContainer.cardActionProvider"
[color]="getValue(dtoContainer.cardColor) | async"
[isInPage]="getValue(dtoContainer.isInPage) | async"
[showHeader]="
(notEmptyValue(dtoContainer.cardTitle) | async) ||
(notEmptyValue(dtoContainer.cardSubTitle) | async)
"
[cssClass]="getValue(dtoContainer.cardCssClass) | async"
[title]="getResourceValue(dtoContainer.cardTitle) | async"
[showFooter]="getValue(dtoContainer.cardShowFooter) | async"
[showActions]="getValue(dtoContainer.cardShowActions) | async"
[subTitle]="getResourceValue(dtoContainer.cardSubTitle) | async"
*ngIf="dtoContainer.showComponent && dtoContainer.showComponentCard"
[foregroundColor]="getValue(dtoContainer.cardForegroundColor) | async"
[class.v-no-margin-card]="getValue(dtoContainer.removeCardMargin) | async"
[uiDisabled]="
loading || uiDisabled || dtoContainer.loading || dtoContainer.uiDisabled
"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div
actions
class="x-actions"
*ngIf="getValue(dtoContainer.cardShowActions) | async"
>
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(dtoContainer.cardColor) | async"
></div>
<!-- Footer -->
<div
class="x-footer"
footer
*ngIf="getValue(dtoContainer.cardShowFooter) | async"
>
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
(xOnEnter)="handleEnterPressed($event)"
(xOnEscape)="handleEscapePressed($event)"
class="v-dto v-component-wrapper unselecteable"
*ngIf="dtoContainer.showComponent && dtoContainer.showComponentPresentType"
[ngClass]="getArrayValue(dtoContainer.cssClass) | async"
>
<!-- Component Data -->
<ng-container *ngIf="getValue(dtoContainer.presentType) | async as prType">
<ng-container
*ngTemplateOutlet="
prType === PresentTypes.AsView
? viewPresentTypeRef
: prType === PresentTypes.AsForm
? formPresentTypeRef
: handleSelectCustomPresentTypeTemplate(prType)
"
></ng-container>
</ng-container>
<!-- Component Custom Templates -->
<div class="v-dto-custom-content">
<ng-content select="[dtoCustomContent]"></ng-content>
</div>
<!-- Component Validation Erros -->
<section
class="v-dto-validation-errors"
*ngIf="
dtoContainer.canShowValidationErros() &&
(getValue(dtoContainer.showValidationErrors) | async) &&
(notNullOrUndefinedValue(dtoContainer.modelState) | async) &&
hasChildValue((getValue(dtoContainer.modelState) | async).errors)
| async
"
>
<ng-container *ngTemplateOutlet="dtoValidationErrorsRef"></ng-container>
</section>
<!-- Component Actions -->
<section
class="v-dto-actions"
*ngIf="
((getValue(dtoContainer.showFormResetButton) | async) ||
(getValue(dtoContainer.showFormCancelButton) | async) ||
(getValue(dtoContainer.showFormActionButton) | async)) &&
((dtoContainer.canShowDtoActions() | async) ||
(getValue(dtoContainer.presentType) | async) === PresentTypes.AsForm)
"
>
<ng-container *ngTemplateOutlet="dtoActionsRef"></ng-container>
</section>
</section>
</ng-template>
<!-- -->
<!-- Present Types Templates -->
<!-- -->
<!-- View Present Type Template -->
<ng-template #viewPresentTypeRef>
<section
class="v-dto-view"
*ngIf="
dtoContainer.showComponent &&
dtoContainer.showComponentViewPresentType &&
(hasChildValue(dtoContainer.extractDescribedKeys()) | async)
"
>
<ng-container
*ngIf="getArrayValue(dtoContainer.extractDescribedKeys()) | async as keys"
>
<ng-container *ngFor="let key of keys">
<div
[class.v-dto-field-view]="
getValue(dtoContainer.isFieldView(key)) | async
"
[class.v-dto-field-form]="
getValue(dtoContainer.isFormView(key)) | async
"
class="v-dto-field v-dto-field-{{ key }}"
*ngIf="getValue(dtoContainer.canShowField(key)) | async"
>
<!-- Check Specified Key has Template or not -->
<!-- Default Template -->
<ng-container
*ngIf="!(getValue(dtoContainer.hasCustomTemplate(key)) | async)"
>
<!-- Field View -->
<ng-container
*ngIf="getValue(dtoContainer.isFieldView(key)) | async"
>
<!-- Field View Toggel -->
<!-- Position: Before Label -->
<span
*ngIf="
(getValue(dtoContainer.showFieldsForm) | async) &&
(getValue(dtoContainer.canPresentFieldForm(key)) | async) &&
(getValue(dtoContainer.fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeLabel
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView:
getValue(dtoContainer.isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
<!-- Break Line -->
<br *ngIf="getValue(dtoContainer.canBreakContent(key)) | async" />
<!-- Field View Toggel -->
<!-- Position: Before Value -->
<span
*ngIf="
(getValue(dtoContainer.showFieldsForm) | async) &&
(getValue(dtoContainer.canPresentFieldForm(key)) | async) &&
(getValue(dtoContainer.fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeValue
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView:
getValue(dtoContainer.isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Filed Content -->
<span
[isForeground]="true"
[xColor]="dtoContainer.valueColor"
class="v-dto-field-value v-dto-field-value-{{ key }}"
>
{{ dtoContainer.getFieldProvidedValue(key) }}
</span>
<!-- Field View Toggel -->
<!-- Position: After Value -->
<span
*ngIf="
(getValue(dtoContainer.showFieldsForm) | async) &&
(getValue(dtoContainer.canPresentFieldForm(key)) | async) &&
(getValue(dtoContainer.fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.AfterValue
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView:
getValue(dtoContainer.isFieldView(key)) | async
}
"
></ng-container>
</span>
</ng-container>
<!-- Form View -->
<ng-container
*ngIf="getValue(dtoContainer.isFormView(key)) | async"
>
<!-- Field Label -->
<ng-container
*ngIf="getValue(dtoContainer.canBreakContent(key)) | async"
>
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
</ng-container>
<!-- Create an Slotter for Form Field -->
<x-slotter
[layout]="SlotLayouts.HORIZONTAL"
[startTopSlotCssClass]="'x-dynamic-slot'"
[endBottomSlotCssClass]="'x-dynamic-slot'"
[hasStartOrTopSlot]="
!(getValue(dtoContainer.canBreakContent(key)) | async)
"
>
<!-- Start (Field Label) -->
<x-slot
[name]="SlotNames.START"
[cssClass]="'ion-text-start'"
*ngIf="!(getValue(dtoContainer.canBreakContent(key)) | async)"
>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
</x-slot>
<!-- Center (Field Form) -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-start'">
<!-- Field Form Presenter -->
<span
[isForeground]="true"
[xColor]="dtoContainer.valueColor"
[class.rtl]="
getValue(
managerService.settingsService.uiDirection === 'rtl'
)
"
class="v-dto-field-form v-dto-field-form-slot v-dto-field-{{
key
}}-form"
*ngIf="
getValue(dtoContainer.getFieldFormControlConfig(key))
| async as fiedlFormConfig
"
>
<!-- Form Template Present -->
<ng-container
*ngTemplateOutlet="
formPresenterRef;
context: {
formConfig: fiedlFormConfig
}
"
></ng-container>
</span>
</x-slot>
<!-- End (Field Toggle) -->
<x-slot [name]="SlotNames.END">
<!-- Field View Toggel -->
<span
*ngIf="
getValue(dtoContainer.canPresentFieldView(key)) | async
"
class="v-dto-field-pesent-as-view v-dto-field-{{
key
}}-present-as-view"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView:
getValue(dtoContainer.isFieldView(key)) | async
}
"
></ng-container>
</span>
</x-slot>
</x-slotter>
</ng-container>
</ng-container>
<!-- Custom Template -->
<ng-container *ngIf="dtoContainer.canShowCustomTemplate(key) | async">
<!-- Field View Toggel -->
<!-- Position: Before Label -->
<span
*ngIf="
(getValue(dtoContainer.fieldFormButtonPosition) | async) ===
FieldFormButtonPositions.BeforeLabel &&
getValue(dtoContainer.canPresentFieldForm(key)) | async
"
class="v-dto-field-pesent-as-form v-dto-field-{{
key
}}-present-as-form"
>
<ng-container
*ngTemplateOutlet="
fieldPresentToggelrRef;
context: {
key: key,
isFieldView: getValue(dtoContainer.isFieldView(key)) | async
}
"
></ng-container>
</span>
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
<!-- Break Line -->
<br *ngIf="getValue(dtoContainer.canBreakContent(key)) | async" />
<!-- Here we Have to Handle Array Type -->
<ng-container
*ngIf="
getValue(dtoContainer.getFieldType(key)) | async as fieldType
"
>
<!-- Handle Non Array Types -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(fieldType) | async) &&
fieldType !== ModelFieldTypes.Array
"
>
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: 0,
value: dtoContainer.getFieldValue(key)
}
"
></ng-container>
</ng-container>
<!-- Handle Array Types -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(fieldType) | async) &&
fieldType === ModelFieldTypes.Array
"
>
<!-- Push all Childs Once -->
<ng-container *ngIf="dtoContainer.canPushChilds(key)">
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: 0,
value: dtoContainer.getFieldValue(key)
}
"
></ng-container>
</ng-container>
<!-- Push Childs one by one -->
<ng-container *ngIf="!dtoContainer.canPushChilds(key)">
<ng-container
*ngFor="
let item of dtoContainer.getFieldValue(key);
index as idx
"
>
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: idx,
value: item
}
"
></ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</div>
</ng-container>
</ng-container>
</section>
</ng-template>
<!-- Form Present Type Template -->
<ng-template #formPresentTypeRef>
<section
class="v-dto-form"
*ngIf="
dtoContainer.showComponent && dtoContainer.showComponentFormPresentType
"
>
<!-- Form Template Presenter -->
<ng-container
*ngIf="getValue(dtoContainer.getFormModelConfig()) | async as formConfig"
>
<!-- Present Form -->
<ng-container
*ngTemplateOutlet="
formPresenterRef;
context: {
formConfig: formConfig
}
"
></ng-container>
<!-- Detect Custom Templates Fields -->
<ng-container *ngFor="let key of dtoContainer.getCustomTemplateKeys()">
<!-- Check Can Show Custom Template Item -->
<!-- Field Label -->
<ng-container
*ngTemplateOutlet="
fieldLabelPresenterRef;
context: {
key: key
}
"
></ng-container>
<!-- Break Line -->
<br *ngIf="getValue(dtoContainer.canBreakContent(key)) | async" />
<!-- Here we Have to Handle Array Type -->
<ng-container
*ngIf="getValue(dtoContainer.getFieldType(key)) | async as fieldType"
>
<!-- Handle Non Array Types -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(fieldType) | async) &&
fieldType !== ModelFieldTypes.Array
"
>
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: 0,
value: dtoContainer.getFieldValue(key)
}
"
></ng-container>
</ng-container>
<!-- Handle Array Types -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(fieldType) | async) &&
fieldType === ModelFieldTypes.Array
"
>
<!-- Push all Childs Once -->
<ng-container *ngIf="dtoContainer.canPushChilds(key)">
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: 0,
value: dtoContainer.getFieldValue(key)
}
"
></ng-container>
</ng-container>
<!-- Push Childs one by one -->
<ng-container *ngIf="!dtoContainer.canPushChilds(key)">
<ng-container
*ngFor="
let item of dtoContainer.getFieldValue(key);
index as idx
"
>
<ng-container
*ngTemplateOutlet="
customTemplateRef;
context: {
key: key,
index: idx,
value: item
}
"
></ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</ng-container>
</section>
</ng-template>
<!-- Nothing Present Type Template -->
<ng-template #nothingPresentTypeRef></ng-template>
<!-- -->
<!-- Other Templates -->
<!-- -->
<!-- Field Label Presenter Template -->
<ng-template #fieldLabelPresenterRef let-key="key">
<span
[isForeground]="true"
[xColor]="dtoContainer.labelColor"
*ngIf="getValue(dtoContainer.canShowFieldLabel(key)) | async"
class="v-dto-field-label v-dto-field-label-{{ key }}"
>
{{ dtoContainer.getFieldProvidedLabel(key) }}
</span>
</ng-template>
<!-- Field Present Toggler Template -->
<ng-template
let-key="key"
#fieldPresentToggelrRef
let-isFieldView="isFieldView"
>
<!-- Form View Toggler / Form Apply ... -->
<x-button
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[loading]="loading || dtoContainer.loading"
[icon]="
isFieldView
? (getValue(dtoContainer.fieldFormIcon) | async)
: (getValue(dtoContainer.fieldFormApplyIcon) | async)
"
[iconColor]="
isFieldView
? (getValue(dtoContainer.fieldFormIconColor) | async)
: (getValue(dtoContainer.fieldFormApplyIconColor) | async)
"
(clicked)="
dtoContainer.handleFormFieldAction(
key,
isFieldView ? FieldViewActions.ShowForm : FieldViewActions.Apply
)
"
class="v-dto-field-present-toggelr v-dto-field-{{ key }}-present-toggler"
[uiDisabled]="
loading || uiDisabled || dtoContainer.loading || dtoContainer.uiDisabled
"
></x-button>
<!-- Form Cancel -->
<x-button
*ngIf="!isFieldView"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[loading]="loading || dtoContainer.loading"
[icon]="getValue(dtoContainer.fieldFormCancelIcon) | async"
class="v-dto-field-form-cancel v-dto-field-{{ key }}-form-cancel"
[iconColor]="getValue(dtoContainer.fieldFormCancelIconColor) | async"
[uiDisabled]="
loading || uiDisabled || dtoContainer.loading || dtoContainer.uiDisabled
"
(clicked)="dtoContainer.handleFormFieldAction(key, FieldViewActions.Cancel)"
></x-button>
</ng-template>
<!-- Form Presenter Template -->
<ng-template #formPresenterRef let-formConfig="formConfig">
<x-form
[isInPage]="false"
[wrapWithCard]="false"
[formConfig]="formConfig"
[loading]="loading || dtoContainer.loading"
[actionProvider]="dtoContainer.formActionProvider"
[cssClass]="getArrayValue(dtoContainer.cssClass) | async"
[fillColor]="getValue(dtoContainer.formFillColor) | async"
(valueChanged)="dtoContainer.handleFormValueChanged($event)"
[activeColor]="getValue(dtoContainer.formActiveColor) | async"
[regularColor]="getValue(dtoContainer.formRegularColor) | async"
[invalidColor]="getValue(dtoContainer.formInvalidColor) | async"
[pendingColor]="getValue(dtoContainer.formPendingColor) | async"
[disabledColor]="getValue(dtoContainer.formDisabledColor) | async"
[disabledFillColor]="getValue(dtoContainer.formDisabledFillColor) | async"
[uiDisabled]="
loading || uiDisabled || dtoContainer.loading || dtoContainer.uiDisabled
"
></x-form>
</ng-template>
<!-- Actions Template -->
<ng-template #dtoActionsRef>
<!-- All Available DTO Actions -->
<x-slotter
[layout]="SlotLayouts.HORIZONTAL"
[loading]="loading || dtoContainer.loading"
[hasCenterSlot]="getValue(dtoContainer.showFormResetButton) | async"
[hasStartOrTopSlot]="getValue(dtoContainer.showFormActionButton) | async"
[hasEndOrBottomSlot]="getValue(dtoContainer.showFormCancelButton) | async"
[uiDisabled]="
loading || uiDisabled || dtoContainer.loading || dtoContainer.uiDisabled
"
>
<!-- Start -->
<!-- Add Or Update -->
<x-slot
[name]="SlotNames.START"
[cssClass]="'ion-text-center'"
*ngIf="getValue(dtoContainer.showFormActionButton) | async"
>
<x-button
#btnAddOrUpdateDto
[type]="ButtonTypes.Raised"
(clicked)="handleAddOrUpdateClicked()"
[loading]="loading || dtoContainer.loading"
[uiDisabled]="
loading ||
uiDisabled ||
dtoContainer.loading ||
dtoContainer.uiDisabled ||
!(getValue(dtoContainer.canAddOrUpdate()) | async)
"
[color]="getValue(dtoContainer.formActionButtonColor) | async"
*ngIf="
(notNullOrUndefinedValue(dtoContainer.modelState) | async) &&
(dtoContainer.modelState.isValidForAdd ||
dtoContainer.modelState.isValidForUpdate)
"
[title]="
(getValue(dtoContainer.modelState) | async).isValidForAdd
? (getValue(dtoContainer.formActionButtonAddResource) | async)
: (getValue(dtoContainer.formActionButtonUpdateResource) | async)
"
>
</x-button>
</x-slot>
<!-- Center -->
<!-- Reset -->
<x-slot
[name]="SlotNames.CENTER"
[cssClass]="'ion-text-center'"
*ngIf="getValue(dtoContainer.showFormResetButton) | async"
>
<x-button
[type]="ButtonTypes.Raised"
[loading]="loading || dtoContainer.loading"
(clicked)="dtoContainer.handleResetModel()"
[uiDisabled]="
loading ||
uiDisabled ||
dtoContainer.loading ||
dtoContainer.uiDisabled ||
!(getValue(dtoContainer.canReset()) | async)
"
[color]="getValue(dtoContainer.formResetButtonColor) | async"
[title]="getValue(dtoContainer.formResetButtonResource) | async"
>
</x-button>
</x-slot>
<!-- End -->
<!-- Cancel -->
<x-slot
[name]="SlotNames.END"
[cssClass]="'ion-text-center'"
*ngIf="getValue(dtoContainer.showFormCancelButton) | async"
>
<x-button
#btnCancelModelEdit
#btnDtoCancelEditMode
[type]="ButtonTypes.Raised"
[loading]="loading || dtoContainer.loading"
[color]="getValue(dtoContainer.formCancelButtonColor) | async"
*ngIf="notNullOrUndefinedValue(dtoContainer.modelState) | async"
[title]="getValue(dtoContainer.formCancelButtonResource) | async"
(clicked)="dtoContainer.handleCancelEditModel(PresentTypes.AsForm)"
[uiDisabled]="
loading ||
uiDisabled ||
dtoContainer.loading ||
dtoContainer.uiDisabled ||
!(
(getValue(dtoContainer.modelState) | async).hasOpenFormField ||
(getValue(dtoContainer.presentType) | async) === PresentTypes.AsForm
)
"
></x-button>
</x-slot>
</x-slotter>
</ng-template>
<!-- Validation Errors Template -->
<ng-template #dtoValidationErrorsRef>
<ng-container
*ngIf="
getArrayValue(dtoContainer.modelState.errors) | async as validationErrors
"
>
<ng-container *ngFor="let err of validationErrors">
<section
[isForeground]="true"
class="v-dto-validation-error"
[xColor]="getValue(dtoContainer.formInvalidColor) | async"
>
{{ err }}
</section>
</ng-container>
</ng-container>
</ng-template>
<!-- Custom Template Handling for Keys -->
<ng-template
let-key="key"
let-index="index"
let-value="value"
#customTemplateRef
>
<!-- Also we have to pass all Presentation Types to Specified Template -->
<ng-container
*ngTemplateOutlet="
dtoContainer.getFieldTemplate(key);
context: {
key: key,
index: index,
value: value,
props: dtoContainer.getProperties()
}
"
></ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-dto\v-dto.component.scss
:host {
--applied-color: var(--x-color-primary-contrast);
}
.v-dto-field {
width: 100%;
max-width: 100%;
display: block;
overflow: hidden;
white-space: nowrap;
margin: {
top: 6px;
bottom: 6px;
}
}
.v-dto-field-label::after {
content: ":";
}
.v-dto-field-form,
.v-dto-field-label,
.v-dto-field-value,
.v-dto-field-pesent-as-form {
//
display: inline-block;
//
margin: {
left: 3px;
right: 3px;
}
}
.v-dto-field-value,
.v-dto-field-label {
height: 100%;
line-height: 100%;
font-size: small;
}
.v-dto-field-value {
width: calc(90%);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.v-dto-field-form-slot {
width: 100%;
}
.v-dto-field-form-slot:not(.rtl) {
padding-right: 60px;
}
.v-dto-field-form-slot.rtl {
padding-left: 6px;
}
.v-dto-actions,
.v-dto-validation-errors {
padding: 16px;
}
.v-dto-validation-error {
padding-bottom: 8px;
}
//
:host ::ng-deep .v-dto-field-present-toggelr {
*,
.x-button {
width: 16px;
height: 16px;
line-height: 16px;
}
x-icon {
--size: 14px;
}
}
//
::ng-deep.mat-datepicker-popup {
.x-form-control-date-picker {
//
&.mat-calendar {
//
color: var(--applied-color) !important;
//
.mat-calendar-body-cell-content {
color: var(--applied-color) !important;
}
}
}
}
//
::ng-deep.mat-select-panel-wrap {
.x-form-select-control {
//
.x-select-option {
//
color: var(--applied-color) !important;
background-color: var(--fill-color) !important;
//
.mat-option-pseudo-checkbox {
color: var(--applied-color) !important;
}
//
&.mat-active {
color: var(--active-color) !important;
}
//
&.mat-selected {
//
color: var(--active-color) !important;
//
.mat-pseudo-checkbox-checked {
background: var(--active-color) !important;
}
//
.mat-pseudo-checkbox::after {
color: var(--accent-color) !important;
}
}
//
&:hover {
$level: 0.5;
-webkit-filter: brightness($level);
filter: brightness($level);
color: var(--active-color) !important;
}
}
}
}
/**
* this Styling used for Ignore Mat Form Field Outline Gap Issues ...
**/
:host ::ng-deep .mat-form-field-appearance-outline.mat-form-field {
//
.mat-form-field-outline-start {
width: 0 !important;
}
//
.mat-form-field-outline-end {
width: 0 !important;
}
//
.mat-form-field-outline-gap {
width: 0 !important;
}
//
mat-label.x-field-label {
//
padding-left: 3px !important;
padding-right: 3px !important;
background-color: var(--outline-bg-color) !important;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-dto\v-dto.component.ts
import {
Input,
ViewChild,
Component,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
VModelFieldTypes,
isVBaseDtoAction,
VBaseDtoComponent,
VBaseDtoPresentType,
VBaseDtoModelAction,
VBaseDtoFieldViewAction,
VBaseDtoFieldFormPresentationIconPosition,
} from './v-dto.typings';
import {
nameof,
parseXColor,
isNullOrUndefined,
isNullOrEmptyString,
getXColorCssVariableName,
} from 'x-framework-core';
import {
XSlotName,
XButtonType,
XSlotLayout,
XButtonComponent,
} from 'x-framework-components';
import { VBaseComponent } from '../classes/v-base.component';
@Component({
selector: 'v-dto',
templateUrl: './v-dto.component.html',
styleUrls: ['./v-dto.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VDto extends VBaseComponent {
//
//#region Props ...
readonly SlotNames = Object.assign({}, XSlotName);
readonly FieldFormButtonPositions = Object.assign(
{},
VBaseDtoFieldFormPresentationIconPosition
);
readonly ButtonTypes = Object.assign({}, XButtonType);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly PresentTypes = Object.assign({}, VBaseDtoPresentType);
readonly ModelFieldTypes = Object.assign({}, VModelFieldTypes);
readonly FieldViewActions = Object.assign({}, VBaseDtoFieldViewAction);
@Input()
dtoContainer: VBaseDtoComponent<any>;
@ViewChild('btnAddOrUpdateDto')
btnAddOrUpdateDto: XButtonComponent;
@ViewChild('btnCancelModelEdit')
btnCancelModelEdit: XButtonComponent;
@ViewChild('nothingPresentTypeRef')
nothingPresentTypeRef: TemplateRef<any>;
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
//
isChanged = changeKeys.includes(nameof<VDto>('dtoContainer'));
if (isChanged) {
//
this.handleSetCSSVars();
this.handleSubscribeToDtoFormAction();
this.handleSubscribeToChangeNotifier();
this.registerDtoContainerActionProvider();
}
}
afterViewInit(): void {
super.afterViewInit();
//
this.handleSetCSSVars();
this.handleSubscribeToDtoFormAction();
this.handleSubscribeToChangeNotifier();
}
//#endregion
//
//#region UI Handlers ...
/**
* Handle Enter Pressed ...
*
* @param event
*/
handleEnterPressed(event: any) {
//
if (
isNullOrUndefined(this.dtoContainer) ||
isNullOrUndefined(this.btnAddOrUpdateDto) ||
this.btnAddOrUpdateDto.loading ||
this.btnAddOrUpdateDto.uiDisabled
) {
return;
}
//
this.dtoContainer.lock = false;
this.btnAddOrUpdateDto.clicked.emit();
}
/**
* Handle Escape Pressed ...
*
* @param event
*/
handleEscapePressed(event: any) {
//
if (
isNullOrUndefined(this.dtoContainer) ||
this.dtoContainer.loading ||
this.dtoContainer.uiDisabled
) {
return;
}
//
if (isNullOrUndefined(this.btnCancelModelEdit)) {
//
this.dtoContainer.handleCancelEditModel();
return;
}
//
if (this.btnCancelModelEdit.loading || this.btnCancelModelEdit.uiDisabled) {
return;
}
//
this.btnCancelModelEdit.clicked.emit();
}
/**
* Handle Add or Update ...
*
* @returns
*/
handleAddOrUpdateClicked() {
//
if (
isNullOrUndefined(this.dtoContainer) ||
isNullOrUndefined(this.btnAddOrUpdateDto) ||
this.btnAddOrUpdateDto.loading ||
this.btnAddOrUpdateDto.uiDisabled
) {
return;
}
//
this.dtoContainer.lock = false;
//
const action = VBaseDtoModelAction.AddOrUpdate;
this.dtoContainer.onDtoModelAction(VBaseDtoModelAction.AddOrUpdate);
this.dtoContainer.dtoModelActionFired.emit({
action,
payload: { ...this.dtoContainer.modelState },
});
}
/**
* Select Custom Present Type Templates ...
*/
handleSelectCustomPresentTypeTemplate(presentType: string) {
//
let result = this.nothingPresentTypeRef;
//
// Check Validate Present type and also DTO Container Exists ...
// then Select Present Type ...
if (
!isNullOrUndefined(this.dtoContainer) &&
!isNullOrEmptyString(presentType)
) {
//
result = this.dtoContainer.getCustomPresentTypeTemplate(presentType);
//
// Validate Template ...
if (isNullOrUndefined(result)) {
result = this.nothingPresentTypeRef;
}
}
//
return result;
}
//#endregion
//
//#region Private ...
private handleSetCSSVars() {
//
if (isNullOrUndefined(this.dtoContainer)) {
return;
}
//
const regularColorDescriptor = parseXColor(
this.dtoContainer.formRegularColor
);
//
const outlineBGVarName = 'outline-bg-color';
const outlineBGVarValue = getXColorCssVariableName(
regularColorDescriptor.name,
true,
regularColorDescriptor.brightnessLevel
);
//
this.setCssVar(outlineBGVarName, `var(${outlineBGVarValue})`);
this.setCssVar(
outlineBGVarName,
`var(${outlineBGVarValue})`,
document.documentElement
);
}
private handleSubscribeToChangeNotifier() {
//
if (isNullOrUndefined(this.dtoContainer)) {
return;
}
//
this.applyTakeUntilWrapper(
this.dtoContainer.changeNotifier.asObservable()
).subscribe((re) => {
this.detectChanges();
});
}
private handleSubscribeToDtoFormAction() {
//
if (isNullOrUndefined(this.dtoContainer)) {
return;
}
//
this.applyTakeUntilWrapper(
this.dtoContainer.dtoModelActionFired.asObservable()
).subscribe((res) => {});
}
private registerDtoContainerActionProvider() {
//
if (
isNullOrUndefined(this.dtoContainer) ||
isNullOrUndefined(this.dtoContainer.actionProvider)
) {
return;
}
//
this.applyTakeUntilWrapper(this.dtoContainer.actionProvider).subscribe(
(event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVBaseDtoAction(event.action)
) {
return;
}
//
this.detectChanges();
this.dtoContainer.detectChanges();
}
);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-dto\v-dto.typings.ts
import {
Inject,
Input,
NgZone,
Output,
Renderer2,
Component,
ElementRef,
Injectable,
TemplateRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XKey,
toMd5,
nameof,
toArray,
isNumber,
isBoolean,
toBoolean,
XKeyValue,
isFunction,
isSameObject,
XResourceIDs,
XExceptionIDs,
nameofInstance,
XColorIdentifier,
setPropertyValue,
isNullOrUndefined,
XResourceIdentifier,
isNullOrEmptyString,
XColorWithBrightness,
XPartialModelFieldNames,
} from 'x-framework-core';
import {
XSlotName,
XIconNames,
XButtonType,
XSlotLayout,
XCardAction,
XFormUpdateOn,
XFormBaseAction,
XPopoverService,
XIconIdentifier,
XCardActionModel,
XFormActionModel,
XBaseActionModel,
XFormControlType,
XFormControlAction,
XFormControlAppearance,
XFormValueChangeEventModel,
XFormControlAppearanceIdentifier,
XFormControlValueChangeEventModel,
} from 'x-framework-components';
import { map } from 'rxjs/operators';
import {
XSimpleFormConfig,
XSimpleFormControlConfig,
} from '../typings/simple.form.typings';
import { Subject, Subscription } from 'rxjs';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { isValueInEnum } from '../tools/v-base.tools';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XManagerService } from 'x-framework-services';
import { VBaseComponent } from '../classes/v-base.component';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { SharedService } from 'src/app/shared/services/shared.service';
//
//#region Model Field Type ...
/**
* All available filed Types of Models ...
*/
export enum VModelFieldTypes {
Date = 'date',
Array = 'array',
String = 'string',
Number = 'number',
Object = 'object',
Boolean = 'boolean',
Function = 'function',
}
/**
* retrieve specified Field Type's default value ...
*
* @param type VmodelFieldTypes member ...
* @returns
*/
export function getDefaultTypeValue(type: VModelFieldTypes) {
//
let result = undefined;
//
switch (type) {
//
case VModelFieldTypes.String:
result = '';
break;
//
case VModelFieldTypes.Number:
result = 0;
break;
//
case VModelFieldTypes.Boolean:
result = false;
break;
//
case VModelFieldTypes.Date:
case VModelFieldTypes.Function:
result = null;
break;
//
case VModelFieldTypes.Object:
result = undefined;
break;
//
case VModelFieldTypes.Array:
result = [];
break;
//
default:
result = undefined;
break;
}
//
return result;
}
//#endregion
//
//#region Definitions ...
/**
* each fields of a model described using this ...
*/
export interface VModelFieldDescriptor<T = any> {
/**
* Default Field Value ...
*/
value: T;
/**
* Field Name on Model ...
*/
key: string;
/**
* Hide Key/Value ...
*/
hide?: boolean;
/**
* Hide Label ...
*/
hideLabel?: boolean;
/**
* Specified Field Data Type ...
*/
type: VModelFieldTypes;
/**
* Field Label Resource Identifier ...
*/
label?: XResourceIdentifier;
/**
* Field Label Value Provider ...
* @returns
*/
valueProvider?: (value: T) => any;
/**
* allow this field to Edit as Field Form ...
*/
allowFieldForm?: boolean;
/**
* Field Form Control Config ...
*/
formControlConfig?: XSimpleFormControlConfig<any>;
/**
* this specified the content of
* field shown on below of label or not ...
*/
showContentBelowLabel?: boolean;
/**
* in some cases in custom template providing for Arrays ...
* maybe we have to use a List or etc component ...
* in this cases instead of looping arround array childs and passed
* each one separately to template, passed all childs to custom template ...
*/
pushArrayChildsOnceToTemplate?: boolean;
/**
* a View Template for a Key to
* provides Custom View for it ...
* this passed three variable:
*
* key: field name ...
* value: field value ...
* presentType: current context present type ...
*/
hasCustomTemplate?: boolean;
template?: TemplateRef<any>;
}
/**
* Holds Model state as this interface ...
*/
export interface VModelState<T> {
isBase: boolean;
isValid: boolean;
model: Partial<T>;
base?: Partial<T>;
isDefault: boolean;
isChanged: boolean;
default?: Partial<T>;
errors?: Array<string>;
isValidForAdd: boolean;
isValidForUpdate: boolean;
hasOpenFormField?: boolean;
}
/**
* Check a Model State Can Locked or not ...
*
* @param state
*/
export function isModelStateLocked<T = any>(state: VModelState<T>) {
//
let result = false;
if (isNullOrUndefined(state)) {
return result;
}
//
result =
!state ||
!state.isValid ||
((state.isValidForAdd || state.isValidForUpdate) && !state.isBase);
//
return result;
}
/**
* Check Spacified State is Valid for Add Action ...
*
* @param state
* @returns
*/
export function canAddModelState<T = any>(state: VModelState<T>) {
//
let result = false;
//
if (isNullOrUndefined(state)) {
return result;
}
//
result =
state.isValid &&
!state.isBase &&
state.isChanged &&
!state.isDefault &&
!isNullOrUndefined(state.base) &&
!isNullOrUndefined(state.default) &&
isSameObject(state.default, state.base);
//
return result;
}
/**
* Check Specified State is Valid for Update Action ...
*
* @param state
* @returns
*/
export function canUpdateModelState<T = any>(state: VModelState<T>) {
//
let result = false;
//
if (isNullOrUndefined(state)) {
return result;
}
//
result =
state.isValid &&
state.isChanged &&
!state.isDefault &&
!state.isBase &&
!isNullOrUndefined(state.base) &&
!isNullOrUndefined(state.default) &&
!isSameObject(state.default, state.base);
//
return result;
}
/**
* Modle must Validate using this interface ...
*/
export interface VModelValidationResult<T> {
isValid: boolean;
isValidForAdd: boolean;
normalized: Partial<T>;
isValidForUpdate: boolean;
validationErrors?: Array<string>;
}
//#endregion
//
//#region Present Type ...
/**
* available types of view ...
*/
export enum VBaseDtoPresentType {
//
AsNone = 'as_none',
AsForm = 'as_form',
AsView = 'as_view',
}
export type TVBaseDtoPresentTypeIdentifier = VBaseDtoPresentType | string;
export function isVBaseDtoPresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VBaseDtoPresentType));
}
/**
* Define Sections Type for Reloading Implementation ...
*/
export type TVDtoSections = XKey<typeof VBaseDtoPresentType> | 'All';
//#endregion
//
//#region Field View Actions ...
/**
* Form Filed Available Actions ...
*/
export enum VBaseDtoFieldViewAction {
Apply = 'apply',
Cancel = 'cancel',
ShowForm = 'showForm',
}
export type VBaseDtoFieldViewActionIdentifier =
| VBaseDtoFieldViewAction
| string;
export interface VModelFieldViewActionModel {
key: string;
action: VBaseDtoFieldViewActionIdentifier;
}
export function isVBaseDtoFieldViewAction(value: string) {
return isValueInEnum(value, Object.assign({}, VBaseDtoFieldViewAction));
}
//#endregion
//
//#region VBaseDtoFieldFormPresentationIconPosition ...
/**
* field form Presentation Side ...
*/
export enum VBaseDtoFieldFormPresentationIconPosition {
BeforeLabel = 'before_label',
BeforeValue = 'before_value',
AfterValue = 'after_value',
}
export type VBaseDtoFieldFormPresentationIconPositionIdentifier =
| VBaseDtoFieldFormPresentationIconPosition
| string;
export function isVBaseDtoFieldFormPresentationIconPosition(value: string) {
return isValueInEnum(
value,
Object.assign({}, VBaseDtoFieldFormPresentationIconPosition)
);
}
//#endregion
//
//#region VBaseDtoModelAction ...
/**
* Base Allowed Dto Actions ...
*/
export enum VBaseDtoModelAction {
Reset = 'reset',
Cancel = 'cancel',
AddOrUpdate = 'addOrUpdate',
}
export type VBaseDtoModelActionIdentifier = VBaseDtoModelAction | string;
export interface VBaseDtoModelFiredActionModel {
action: VBaseDtoModelActionIdentifier;
payload?: VModelState<any>;
}
export function isBaseDtoModelAction(value: string) {
return isValueInEnum(value, Object.assign({}, VBaseDtoModelAction));
}
//#endregion
//
//#region Container ...
/**
* Define all required properties here
* just as variables ...
*/
export interface IVBaseDtoContainer {
//
// Self Props ...
/**
* Specified Component present type ...
*/
presentType: TVBaseDtoPresentTypeIdentifier;
/**
* Specified Card Section css class names ...
*/
cssClass: Array<string>;
/**
* Labels Color ...
* usually used in View Presentation ...
*/
labelColor: XColorIdentifier;
/**
* Values Color ...
* usually used in View Presentation ...
*/
valueColor: XColorIdentifier;
/**
* hide optional values on view mode
* if it's not provide as FormField ...
*/
hideOptionalFieldsOnView: boolean;
/**
* show configured descriptor keys
* as inline form fields ...
*/
showFieldsForm: boolean;
/**
* Where to Show Form Field Prentation Button ...
*/
fieldFormButtonPosition: VBaseDtoFieldFormPresentationIconPositionIdentifier;
/**
* Specified Field Form View Toggeler ...
*/
fieldFormIcon: XIconIdentifier;
fieldFormIconColor: XColorIdentifier;
/**
* Specified Field Form Apply ...
*/
fieldFormApplyIcon: XIconIdentifier;
fieldFormApplyIconColor: XColorIdentifier;
/**
* Specified Field Form Cancel ...
*/
fieldFormCancelIcon: XIconIdentifier;
fieldFormCancelIconColor: XColorIdentifier;
/**
* Specify Force to Show Validation Errors or not ...
*/
showValidationErrors: boolean;
//
// Card Properties ...
/**
* Card Show as In Page ...
*/
isInPage: boolean;
/**
* Wrap Component with Card ...
*/
wrapWithCard: boolean;
/**
* Show Card Footrer Section ...
*/
cardShowFooter: boolean;
/**
* Show Card Actions Section ...
*/
cardShowActions: boolean;
/**
* Specify to Remove Card Marigin or not ...
*/
removeCardMargin: boolean;
/**
* Specified Card Color apply as Foreground ...
*/
cardForegroundColor: boolean;
/**
* Specified Card Title Resource Id ...
*/
cardTitle: XResourceIdentifier;
/**
* Specified Card Subtitle Resource Id ...
*/
cardSubTitle: XResourceIdentifier;
/**
* Specified Card Section css class names ...
*/
cardCssClass: Array<string>;
/**
* Specified Card Color ...
*/
cardColor: XColorIdentifier;
/**
* Card Action Provider Implementation ...
*/
cardActionProvider: Subject<XCardActionModel>;
//
// Form Props ...
/**
* Form Appearance ...
*/
formAppearance: XFormControlAppearanceIdentifier;
/**
* Forms Action Provider ...
*/
formActionProvider: Subject<XFormActionModel>;
/**
* Form Regular Color ...
*/
formRegularColor: XColorIdentifier;
/**
* Form Accent Color ...
*/
formAccentColor: XColorIdentifier;
/**
* Form Hint Color ...
*/
formHintColor: XColorIdentifier;
/**
* Form Active Color ...
*/
formActiveColor: XColorIdentifier;
/**
* Form Valid Color ...
*/
formValidColor: XColorIdentifier;
/**
* Form Invalid Color ...
*/
formInvalidColor: XColorIdentifier;
/**
* Form Pending Color ...
*/
formPendingColor: XColorIdentifier;
/**
* Form Fill Color ...
*/
formFillColor: XColorIdentifier;
/**
* Form Disabled Color ...
*/
formDisabledColor: XColorIdentifier;
/**
* Form Disabled Fill Color ...
*/
formDisabledFillColor: XColorIdentifier;
/**
* Specifie to Show Form Cancel Button or not ...
*/
showFormCancelButton: boolean;
/**
* Form Cancel Button Locale Resource ...
*/
formCancelButtonResource: XResourceIdentifier;
/**
* Form Cancel Button Color ...
*/
formCancelButtonColor: XColorIdentifier;
/**
* Show Form Reset Button or not ...
*/
showFormResetButton: boolean;
/**
* Form Reset Button Locale Resource ...
*/
formResetButtonResource: XResourceIdentifier;
/**
* Form Reset Button Color ...
*/
formResetButtonColor: XColorIdentifier;
/**
* Show Form Action Button ...
*/
showFormActionButton: boolean;
/**
* Form Add Action Button Locale Resource ...
*/
formActionButtonAddResource: XResourceIdentifier;
/**
* Form Update Action Button Locale Resource ...
*/
formActionButtonUpdateResource: XResourceIdentifier;
/**
* Form Action Button Color ...
*/
formActionButtonColor: XColorIdentifier;
}
/**
* this is a service which we called it Property Provider ...
* all variables defined here as readonly variable with
* default allowed values ...
*/
@Injectable({
providedIn: 'root',
})
export class VBaseDtoContainer implements Readonly<IVBaseDtoContainer> {
//
// Self Props ...
readonly presentType: TVBaseDtoPresentTypeIdentifier =
VBaseDtoPresentType.AsNone;
readonly cssClass: Array<string> = [];
readonly labelColor: XColorIdentifier = XColorWithBrightness.Tertiary;
readonly valueColor: XColorIdentifier = XColorWithBrightness.Dark;
readonly hideOptionalFieldsOnView: boolean = false;
readonly showFieldsForm: boolean = false;
readonly fieldFormButtonPosition: VBaseDtoFieldFormPresentationIconPositionIdentifier =
VBaseDtoFieldFormPresentationIconPosition.AfterValue;
readonly fieldFormIcon: XIconIdentifier = XIconNames.edit;
readonly fieldFormIconColor: XColorIdentifier = XColorWithBrightness.Tertiary;
readonly fieldFormApplyIcon: XIconIdentifier = XIconNames.checkmark;
readonly fieldFormApplyIconColor: XColorIdentifier =
XColorWithBrightness.Success;
readonly fieldFormCancelIcon: XIconIdentifier = XIconNames.cancel;
readonly fieldFormCancelIconColor: XColorIdentifier =
XColorWithBrightness.Danger;
readonly showValidationErrors: boolean = true;
//
// Card Props ...
readonly isInPage: boolean = false;
readonly wrapWithCard: boolean = false;
readonly cardShowFooter: boolean = false;
readonly cardShowActions: boolean = false;
readonly removeCardMargin: boolean = false;
readonly cardForegroundColor: boolean = false;
readonly cardTitle: XResourceIdentifier = '';
readonly cardSubTitle: XResourceIdentifier = '';
readonly cardCssClass: Array<string> = [];
readonly cardColor: string = XColorWithBrightness.Light;
readonly cardActionProvider: Subject<XCardActionModel> =
new Subject<XCardActionModel>();
//
readonly formAppearance: XFormControlAppearanceIdentifier =
XFormControlAppearance.Outline;
readonly formActionProvider: Subject<XFormActionModel> =
new Subject<XFormActionModel>();
//
readonly formRegularColor: XColorIdentifier = XColorWithBrightness.Dark;
readonly formAccentColor: XColorIdentifier = XColorWithBrightness.DangerShade;
readonly formHintColor: XColorIdentifier = XColorWithBrightness.DangerShade;
readonly formActiveColor: XColorIdentifier =
XColorWithBrightness.WarningShade;
readonly formValidColor: XColorIdentifier = XColorWithBrightness.Success;
readonly formInvalidColor: XColorIdentifier = XColorWithBrightness.Danger;
readonly formPendingColor: XColorIdentifier = XColorWithBrightness.Warning;
readonly formFillColor: XColorIdentifier = XColorWithBrightness.Primary;
readonly formDisabledColor: XColorIdentifier = XColorWithBrightness.LightTint;
readonly formDisabledFillColor: XColorIdentifier =
XColorWithBrightness.LightShade;
//
readonly showFormCancelButton: boolean = true;
readonly formCancelButtonResource: XResourceIdentifier = XResourceIDs.cancel;
readonly formCancelButtonColor: XColorIdentifier =
XColorWithBrightness.Danger;
readonly showFormResetButton: boolean = true;
readonly formResetButtonResource: XResourceIdentifier = XResourceIDs.reset;
readonly formResetButtonColor: XColorIdentifier =
XColorWithBrightness.Warning;
readonly showFormActionButton: boolean = true;
readonly formActionButtonAddResource: XResourceIdentifier = XResourceIDs.add;
readonly formActionButtonUpdateResource: XResourceIdentifier =
XResourceIDs.update;
readonly formActionButtonColor: XColorIdentifier =
XColorWithBrightness.Primary;
}
//#endregion
//
//#region Action Provider ...
export enum VBaseDtoAction {
/**
* Calling Change Detection ...
*/
Refresh = 'Refresh',
/**
* Reloading Component ...
*/
Reload = 'Reload',
/**
* ReState Model ...
*/
ReState = 'ReState',
/**
* Rene Model by Provided payload ...
*/
RenewModel = 'RenewModel',
/**
* Sep Properties ...
* payload.props: Partial<IVBaseDtoContainer> => Specified Properties Object to Set ...
* payload.section: VDtoSections => Specified Which Section to Reload (default: 'AsNone') ...
*/
SetProperties = 'SetProperties',
/**
* Focus on Specified Form Control ...
*/
FocusOnFormControl = 'FocusOnFormControl',
}
export type VBaseDtoActionIdentifier = VBaseDtoAction | string;
export interface VBaseDtoActionModel extends XBaseActionModel<VBaseDtoAction> {}
export function isVBaseDtoAction(value: string) {
return isValueInEnum(value, Object.assign({}, VBaseDtoAction));
}
//#endregion
//
//#region VBaseDtoComponent ...
/**
* a main component class wich provides all properties with their
* anotations and has a base constructor by inject Property Provider service ...
* Must Important things in this class is it provides an Actions called
* extractProps
*/
@Injectable()
@Component({
template: ``,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export abstract class VBaseDtoComponent<T>
extends VBaseComponent
implements IVBaseDtoContainer
{
//
//#region Props ...
//
//#region Readonly Fields ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly IconNames = Object.assign({}, XIconNames);
readonly FieldFormButtonPositions = Object.assign(
{},
VBaseDtoFieldFormPresentationIconPosition
);
readonly ButtonTypes = Object.assign({}, XButtonType);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly ResourceIDs = Object.assign({}, XResourceIDs);
readonly AppResourceIDs = Object.assign({}, AppResourceIDs);
readonly ColorNames = Object.assign({}, XColorWithBrightness);
readonly ModelFieldTypes = Object.assign({}, VModelFieldTypes);
readonly PresentTypes = Object.assign({}, VBaseDtoPresentType);
readonly FormControlTypes = Object.assign({}, XFormControlType);
readonly FieldViewActions = Object.assign({}, VBaseDtoFieldViewAction);
readonly FormControlAppearances = Object.assign({}, XFormControlAppearance);
//#endregion
//
//#region Inputs ...
//
//#region Custom ...
@Input()
providedFieldDescriptors: VModelFieldDescriptor<any>[];
//
private PRESENT_TYPE: string;
@Input()
presentType: string = this.propertyProvider.presentType;
//
@Input()
labelColor: XColorIdentifier = this.propertyProvider.labelColor;
//
@Input()
valueColor: XColorIdentifier = this.propertyProvider.valueColor;
//
@Input()
hideOptionalFieldsOnView: boolean =
this.propertyProvider.hideOptionalFieldsOnView;
//
@Input()
showFieldsForm: boolean = this.propertyProvider.showFieldsForm;
@Input()
fieldFormButtonPosition: VBaseDtoFieldFormPresentationIconPositionIdentifier =
this.propertyProvider.fieldFormButtonPosition;
//
@Input()
fieldFormIcon = this.propertyProvider.fieldFormIcon;
@Input()
fieldFormIconColor = this.propertyProvider.fieldFormIconColor;
//
@Input()
fieldFormApplyIcon = this.propertyProvider.fieldFormApplyIcon;
@Input()
fieldFormApplyIconColor = this.propertyProvider.fieldFormApplyIconColor;
//
@Input()
fieldFormCancelIcon = this.propertyProvider.fieldFormCancelIcon;
@Input()
fieldFormCancelIconColor = this.propertyProvider.fieldFormCancelIconColor;
//
@Input()
showValidationErrors = this.propertyProvider.showValidationErrors;
//
@Input()
cssClass: Array<string> = this.propertyProvider.cssClass;
//#endregion
//
//#region Card Props ...
@Input()
isInPage: boolean = this.propertyProvider.isInPage;
//
@Input()
wrapWithCard: boolean = this.propertyProvider.wrapWithCard;
//
@Input()
cardShowFooter: boolean = this.propertyProvider.cardShowFooter;
//
@Input()
cardShowActions: boolean = this.propertyProvider.cardShowActions;
//
@Input()
cardColor: string = this.propertyProvider.cardColor;
//
@Input()
cardForegroundColor: boolean = this.propertyProvider.cardForegroundColor;
//
@Input()
cardTitle: XResourceIdentifier = this.propertyProvider.cardTitle;
//
@Input()
cardSubTitle: XResourceIdentifier = this.propertyProvider.cardSubTitle;
//
@Input()
cardCssClass: Array<string> = this.propertyProvider.cardCssClass;
/**
* Remove Card Margin ...
*/
@Input()
removeCardMargin: boolean = this.propertyProvider.removeCardMargin;
//
@Input()
cardActionProvider: Subject<XCardActionModel> =
this.propertyProvider.cardActionProvider;
//#endregion
//
//#region Form Props ...
@Input()
formAppearance: XFormControlAppearanceIdentifier =
this.propertyProvider.formAppearance;
//
@Input()
formActionProvider: Subject<XFormActionModel> =
this.propertyProvider.formActionProvider;
/**
*
*/
@Input()
formRegularColor: XColorIdentifier = this.propertyProvider.formRegularColor;
@Input()
formAccentColor: XColorIdentifier = this.propertyProvider.formAccentColor;
@Input()
formHintColor: XColorIdentifier = this.propertyProvider.formHintColor;
@Input()
formActiveColor: XColorIdentifier = this.propertyProvider.formActiveColor;
@Input()
formValidColor: XColorIdentifier = this.propertyProvider.formValidColor;
@Input()
formInvalidColor: XColorIdentifier = this.propertyProvider.formInvalidColor;
@Input()
formPendingColor: XColorIdentifier = this.propertyProvider.formPendingColor;
@Input()
formFillColor: XColorIdentifier = this.propertyProvider.formFillColor;
@Input()
formDisabledColor: XColorIdentifier = this.propertyProvider.formDisabledColor;
@Input()
formDisabledFillColor: XColorIdentifier =
this.propertyProvider.formDisabledFillColor;
//#endregion
//
//#region Form Actions Buttons Config ...
//
// Cancel Button ...
@Input()
showFormCancelButton: boolean = this.propertyProvider.showFormCancelButton;
@Input()
formCancelButtonResource: XResourceIdentifier =
this.propertyProvider.formCancelButtonResource;
@Input()
formCancelButtonColor: XColorIdentifier =
this.propertyProvider.formCancelButtonColor;
//
// Reset Button ...
@Input()
showFormResetButton: boolean = this.propertyProvider.showFormResetButton;
@Input()
formResetButtonResource: XResourceIdentifier =
this.propertyProvider.formResetButtonResource;
@Input()
formResetButtonColor: XColorIdentifier =
this.propertyProvider.formResetButtonColor;
//
// Action Button Button ...
@Input()
showFormActionButton: boolean = this.propertyProvider.showFormActionButton;
@Input()
formActionButtonAddResource: XResourceIdentifier =
this.propertyProvider.formActionButtonAddResource;
@Input()
formActionButtonUpdateResource: XResourceIdentifier =
this.propertyProvider.formActionButtonUpdateResource;
@Input()
formActionButtonColor: XColorIdentifier =
this.propertyProvider.formActionButtonColor;
//#endregion
//#endregion
//
//#region Events Emitters ...
changeSubject = new Subject<T>();
/**
* Model Changed Event Emitter ...
*/
@Output()
changeNotifier: EventEmitter<VModelState<T>> = new EventEmitter<
VModelState<T>
>();
/**
* Notify Base Dto Actions (Form Action Buttons ...) Fired ...
*/
@Output()
dtoModelActionFired = new EventEmitter<VBaseDtoModelFiredActionModel>();
/**
* Field Presenter Type Change ...
*/
@Output()
fieldPresentTypeChanged = new EventEmitter<VModelFieldViewActionModel>();
//#endregion
//
@Input()
actionProvider = new Subject<VBaseDtoActionModel>();
//
public inFormFields: Array<string> = [];
public inFormFieldsFormConfigs: Array<XSimpleFormConfig<any>> = [];
public inFormFieldValues: Array<XKeyValue<string, any>> = [];
//
// Provide Filed Descriptors ...
private FIELD_DESCRIPTORS: Array<VModelFieldDescriptor<any>> = [];
get fieldDescriptors() {
//
let result: Array<VModelFieldDescriptor<any>> = [];
//
if (this.hasChild(this.FIELD_DESCRIPTORS)) {
result = [...this.FIELD_DESCRIPTORS];
} else {
//
if (
!isNullOrUndefined(this.providedFieldDescriptors) &&
this.hasChild(this.providedFieldDescriptors)
) {
//
result = [...this.providedFieldDescriptors];
} else {
result = [...this.defaultFieldDescriptors];
}
//
this.onFieldDescriptorsReady();
}
//
return result;
}
//
//#region Model ...
//
// Model State Used to Provided Changes Happens ...
private MODEL_STATE: VModelState<T>;
get modelState() {
return this.MODEL_STATE;
}
//
// Base Model used to Check Changes ...
private BASE_MODEL: T = undefined;
get baseModel() {
return this.BASE_MODEL;
}
//
private MODEL: T = undefined;
//
// Specified Model Unique Identifier ...
modelUniqueIdentifier: string = 'id';
getModelUniqueIdentifierValue() {
//
let result: string = undefined;
//
if (
!isNullOrUndefined(this.model) &&
!isNullOrEmptyString(this.modelUniqueIdentifier)
) {
result = (this.model as any)[this.modelUniqueIdentifier];
}
//
return result;
}
//
@Input()
set model(v: T) {
//
const preparedModel = this.prepare(v);
this.MODEL = {
...preparedModel,
};
this.BASE_MODEL = {
...preparedModel,
};
//
this.onModelAssigned();
this.setAttribute(
this.modelUniqueIdentifier,
this.getModelUniqueIdentifierValue()
);
}
get model() {
return this.MODEL;
}
//#endregion
//#endregion
//
//#region Reloading Mechanism ...
showComponentCard: boolean = true;
showComponentPresentType: boolean = true;
showComponentViewPresentType: boolean = true;
showComponentFormPresentType: boolean = true;
//
// Since We Have Implement new Reloading mechanism ...
// here we create a new Reload for VDto ...
async reloadDtoComponent(section: TVDtoSections): Promise<void> {
//
let task: Promise<void> = undefined;
switch (section) {
//
case 'All':
//
this.showComponent = false;
this.showComponentCard = false;
this.showComponentPresentType = false;
this.showComponentViewPresentType = false;
this.showComponentFormPresentType = false;
//
task = this.waitForElement(this.element.nativeElement.tagName).then(
() => {
//
this.showComponent = true;
this.showComponentCard = true;
this.showComponentPresentType = true;
this.showComponentViewPresentType = true;
this.showComponentFormPresentType = true;
}
);
break;
//
case 'AsForm':
//
this.showComponentPresentType = false;
this.showComponentFormPresentType = false;
//
task = this.waitForElement(this.element.nativeElement.tagName).then(
() => {
//
this.showComponent = true;
this.showComponentPresentType = true;
this.showComponentFormPresentType = true;
}
);
break;
//
case 'AsView':
//
this.showComponentPresentType = false;
this.showComponentViewPresentType = false;
//
task = this.waitForElement(this.element.nativeElement.tagName).then(
() => {
//
this.showComponent = true;
this.showComponentPresentType = true;
this.showComponentViewPresentType = true;
}
);
break;
//
case 'AsNone':
//
// Do Nothisn ...
task = undefined;
break;
}
//
if (section !== 'AsNone') {
//
if (!isNullOrUndefined(task)) {
await task;
}
//
this.handleChangeDetection();
}
}
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: VBaseDtoContainer
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService
);
}
//#endregion
//
//#region LifeCycles ...
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChange = false;
//
// Check Action Provider Changes ...
isChanged = changeKeys.includes(
nameof<VBaseDtoComponent<T>>('actionProvider')
);
if (isChanged) {
//
// Check Validation ...
if (isNullOrUndefined(this.actionProvider)) {
//
applyChange = true;
this.actionProvider = new Subject<VBaseDtoActionModel>();
this.registerActionProvider();
}
}
//
// Check Form Action Provider Changes ...
isChanged = changeKeys.includes(
nameof<VBaseDtoComponent<T>>('formActionProvider')
);
if (isChanged) {
//
if (isNullOrUndefined(this.formActionProvider)) {
//
applyChange = true;
this.formActionProvider = this.propertyProvider.formActionProvider;
this.registerActionProvider();
}
}
//
// Check Card Action Provider Changes ...
isChanged = changeKeys.includes(
nameof<VBaseDtoComponent<T>>('cardActionProvider')
);
if (isChanged) {
//
if (isNullOrUndefined(this.cardActionProvider)) {
//
applyChange = true;
this.cardActionProvider = this.propertyProvider.cardActionProvider;
this.registerActionProvider();
}
}
//
// Check Present Type Changed ...
isChanged = changeKeys.includes(
nameof<VBaseDtoComponent<T>>('presentType')
);
if (isChanged) {
//
if (isNullOrEmptyString(this.presentType)) {
//
applyChange = true;
this.presentType = this.propertyProvider.presentType;
}
//
if (this.PRESENT_TYPE !== this.presentType) {
//
this.onPresentTypeChanged(this.presentType, this.PRESENT_TYPE);
this.PRESENT_TYPE = this.presentType;
this.setAttribute(
nameofInstance(this, 'presentType'),
this.presentType
);
}
}
//
if (applyChange) {
this.handleChangeDetection();
}
}
afterViewInit(): void {
super.afterViewInit();
//
this.registerChangeSubject();
this.registerActionProvider();
//
this.onPresentTypeChanged(this.presentType, this.PRESENT_TYPE);
}
//#endregion
//
//#region Abstractions ...
/**
* Used to Handle Model Assigned ...
*/
onModelAssigned() {}
/**
* When a Field Present As Form ...
*
* @param key
*/
onFieldFormPresent(key: string) {}
/**
* Calls When Filed Form Action Calling ...
*
* @param model
*/
onFieldPresentTypeChanged(model: VModelFieldViewActionModel) {}
/**
* When Present Type Changed ...
*
* @param to new Present Type ...
* @param from old Present Type ...
*/
onPresentTypeChanged(to: string, from: string) {}
/**
* Calls When FormModel Config Prepared ...
*/
onFormModelConfigPrepared() {}
/**
* Notify Prepare Field Descriptors Ready ...
*/
onFieldDescriptorsReady() {}
/**
* Calls when a DTO Action done ...
* fired Event ...
*
* @param action
*/
onDtoModelAction(action: VBaseDtoModelActionIdentifier) {}
/**
* Handle DTO State Management ...
*/
handleDtoStateManagement() {
//
const state = this.getState();
this.MODEL_STATE = {
...state,
};
//
this.lock = isModelStateLocked({ ...this.modelState });
this.detectChanges();
//
this.changeNotifier.emit(this.MODEL_STATE);
}
/**
* a function to prepare Model ...
*
* @param model
*/
abstract prepare(model?: T): T;
/**
* Provide Default Model ...
*/
abstract defaultModel: T;
/**
* Check Model Validation and Normalized it ...
*/
abstract validate(model?: Partial<T>): VModelValidationResult<T>;
/**
* Default Field Descriptors ...
*/
abstract defaultFieldDescriptors: Array<VModelFieldDescriptor<any>>;
//#endregion
//
//#region Properties ...
/**
* Collect Properties ...
*
* @returns
*/
getProperties() {
//
let result: Partial<IVBaseDtoContainer> = {};
//
const keys = Object.keys(this.propertyProvider);
keys.forEach((k) => {
//
const value = (this as any)[k];
if (!isNullOrUndefined(value)) {
(result as any)[k] = value;
}
});
//
return result;
}
/**
* Set Properties ...
*
* @param props
* @returns
*/
setProperties(
props: Partial<IVBaseDtoContainer>,
section: TVDtoSections = 'AsNone'
) {
//
if (isNullOrUndefined(props)) {
return;
}
//
let isChanged = false;
const keys = Object.keys(props);
keys.forEach((key) => {
//
let propertyValue = (props as any)[key];
let currentValue = (this as any)[key];
let propertyDefaultValue = (this.propertyProvider as any)[key];
let selectedValue =
!isNullOrUndefined(propertyValue) && propertyValue !== currentValue
? propertyValue
: isNullOrUndefined(propertyValue) && isNullOrUndefined(currentValue)
? propertyDefaultValue
: currentValue;
if (!isChanged) {
isChanged =
!isNullOrUndefined(selectedValue) && selectedValue !== currentValue;
}
setPropertyValue(this, key, selectedValue);
});
//
if (isChanged) {
//
if (section === 'AsNone') {
this.handleChangeDetection();
} else {
this.reloadDtoComponent(section).then();
}
}
}
/**
* Validate Changed Properties ...
*/
handleValidateChangedProperties() {
//
let applyChanges = false;
const keys = Object.keys(this.propertyProvider);
keys.forEach((k) => {
//
const value = (this as any)[k];
const defValue = (this.propertyProvider as any)[k];
if (isNullOrUndefined(value)) {
//
if (!applyChanges) {
applyChanges = true;
}
//
(this as any)[k] = defValue;
}
});
//
if (applyChanges) {
this.handleChangeDetection();
}
}
//#endregion
//
//#region Providers ...
/**
* Generate Unique Identifier MD5 string based on keys of Filed ...
*
* @returns
*/
generateFormName() {
//
const source = this.extractDescribedKeys()
.map((k) => `${k}`)
.join('|');
const result = toMd5(source);
//
return result;
}
/**
* Extract Avavilable Described Keys ...
*/
extractDescribedKeys() {
return this.fieldDescriptors.map((i) => i.key);
}
/**
* extract a Partial<T> which filled by provided object ...
*
* @returns
*/
extractDecribedModel() {
//
let result: Partial<T> = {};
//
// Validate Field Descriptors Exists or not ...
if (!this.hasChild(this.fieldDescriptors)) {
return result;
}
//
for (var fd of this.fieldDescriptors) {
//
if (isNullOrUndefined(fd)) {
continue;
}
//
let fdValue = getDefaultTypeValue(fd.type);
let mdValue = (this.MODEL as any)[fd.key];
(result as any)[fd.key] = !isNullOrUndefined(mdValue) ? mdValue : fdValue;
}
//
return result;
}
/**
* extract a Partial<T> which filled by provided base object ...
*
* @returns
*/
extractDecribedBaseModel() {
//
let result: Partial<T> = {};
//
// Validate Field Descriptors Exists or not ...
if (!this.hasChild(this.fieldDescriptors)) {
return result;
}
//
for (var fd of this.fieldDescriptors) {
//
if (isNullOrUndefined(fd)) {
continue;
}
//
let fdValue = getDefaultTypeValue(fd.type);
let mdValue = (this.BASE_MODEL as any)[fd.key];
(result as any)[fd.key] = !isNullOrUndefined(mdValue) ? mdValue : fdValue;
}
//
return result;
}
/**
* extract a Partial<T> which filled by provided default object ...
*
* @returns
*/
extractDecribedDefaultModel() {
//
let result: Partial<T> = {};
//
// Validate Field Descriptors Exists or not ...
if (!this.hasChild(this.fieldDescriptors)) {
return result;
}
//
for (var fd of this.fieldDescriptors) {
//
if (isNullOrUndefined(fd)) {
continue;
}
//
let fdValue = getDefaultTypeValue(fd.type);
let mdValue = (this.defaultModel as any)[fd.key];
(result as any)[fd.key] = !isNullOrUndefined(mdValue) ? mdValue : fdValue;
}
//
return result;
}
/**
* get current Model State ...
*
* @returns
*/
getState() {
//
// Extract required Models ...
const model = {
...this.MODEL,
...this.extractDecribedModel(),
};
const baseModel = {
...this.BASE_MODEL,
...this.extractDecribedBaseModel(),
};
const defaultModel = {
...this.defaultModel,
...this.extractDecribedDefaultModel(),
};
//
// Check Compared States ...
const isBase = this.isSame(model, baseModel);
const isDefault = this.isSame(model, defaultModel);
const isChanged = !isBase && !isDefault;
//
const validatedResult = this.validate();
//
const isValid = !isChanged ? true : validatedResult.isValid;
const payload = {
...(isValid ? model : validatedResult.normalized),
};
//
const hasOpenFormField = this.handleFormFieldStates();
//
let result: VModelState<T> = {
isBase,
isValid,
isDefault,
isChanged,
hasOpenFormField,
model: { ...payload },
base: { ...this.BASE_MODEL },
default: { ...this.defaultModel },
isValidForAdd: validatedResult.isValidForAdd,
errors: [...validatedResult.validationErrors],
isValidForUpdate: validatedResult.isValidForUpdate,
};
//
this.MODEL_STATE = {
...result,
};
//
return result;
}
/**
* get FieldType of Specified Key ...
*
* @param key
* @returns
*/
getFieldType(key: string) {
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
return undefined;
}
//
return fd.type;
}
/**
* get Field Value based on Specified Key ...
*
* @param key
* @returns
*/
getFieldValue(key: string) {
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
let result = undefined;
if (isNullOrUndefined(fd)) {
return result;
}
//
const descModel: any = {
...this.extractDecribedModel(),
};
//
if (
fd.type !== VModelFieldTypes.Array &&
fd.type !== VModelFieldTypes.Object
) {
result = descModel[key];
} else if (fd.type === VModelFieldTypes.Object) {
result = {
...descModel[key],
};
} else if (fd.type === VModelFieldTypes.Array) {
result = [...descModel[key]];
}
//
return result;
}
/**
* get fields provided value ...
*
* @param key
* @param managerService
* @returns
*/
getFieldProvidedValue(key: string) {
//
let result: any;
//
let value = this.getFieldValue(key);
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) &&
!isNullOrUndefined(fd.valueProvider) &&
isFunction(fd.valueProvider)
? fd.valueProvider(value)
: this.getFieldValue(key);
//
return result;
}
/**
* get field label translation ...
*
* @param key
* @param managerService
* @returns
*/
getFieldProvidedLabel(key: string) {
//
let result: any;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) && !isNullOrUndefined(fd.label)
? this.resourceProvider(fd.label)
: key;
//
return result;
}
/**
* Check a Key has Custom Template or not ...
*
* @param key
* @returns
*/
hasCustomTemplate(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
return result;
}
//
result = !isNullOrUndefined(fd.hasCustomTemplate) && !!fd.hasCustomTemplate;
return result;
}
/**
* Extract Only Fields Key which Has Custom Templates ...
* @returns
*/
getCustomTemplateKeys() {
return this.fieldDescriptors
.filter(
(fd) =>
fd.hasCustomTemplate &&
!isNullOrUndefined(fd.template) &&
!isNullOrUndefined(fd.formControlConfig)
)
.map((fd) => fd.key);
}
/**
* Retrieve Specified Field's Template ...
*
* @param key
* @returns
*/
getFieldTemplate(key: string) {
//
const fd = this.fieldDescriptors.find((f) => f.key == key);
if (isNullOrUndefined(fd)) {
return undefined;
}
//
return fd.template;
}
/**
* retrieve Specified Field's Form Control Config ...
*
* @param key
*/
getFieldFormControlConfig(key: string) {
//
if (!this.isFormView(key)) {
return undefined;
}
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
if (isNullOrUndefined(fd)) {
return undefined;
}
//
let result = this.inFormFieldsFormConfigs.find((f) => f.name === key);
//
return result;
}
/**
* Get Specified Exception Message ...
*
* @param ex
* @returns
*/
getExceptionMessage(ex: XExceptionIDs) {
return this.managerService.localizationService.getErrorMessage(ex);
}
//
// Form Model ...
/**
* retrieve Model Form Config ...
*/
modelFormConfig: XSimpleFormConfig<T>;
/**
* get Current Model form Config ...
*
* @returns
*/
getDtoFormModelConfig(ignoreCustomTemplates: boolean = true) {
//
const model = {
...this.extractDecribedModel(),
};
const name = this.generateFormName();
const controls = this.fieldDescriptors
.filter(
(i) =>
!isNullOrUndefined(i.formControlConfig) &&
(ignoreCustomTemplates ? true : !i.hasCustomTemplate)
)
.map((i) => i.formControlConfig)
.sort((i) => i.index);
//
// Generate Form Config ...
const result: XSimpleFormConfig<Partial<T>> = {
name,
model,
controls,
};
//
return result;
}
/**
* Check Form Model Config ...
*
* @returns
*/
getFormModelConfig() {
//
if (isNullOrUndefined(this.modelFormConfig)) {
//
const m = this.getDtoFormModelConfig();
if (!isNullOrUndefined(m)) {
//
this.modelFormConfig = {
...m,
};
//
this.prepareFormModelConfig();
}
}
//
return this.modelFormConfig;
}
/**
* if there is any Custom Present Types ...
* we have to override this for Implementation ...
*
* @param presentType
* @returns
*/
getCustomPresentTypeTemplate(presentType: string): TemplateRef<any> {
return null;
}
//
//#region Can ...
/**
* Check a Filed can Shown or not ...
*
* @param key
* @returns
*/
canShowField(key: string) {
//
let result = false;
//
const value = this.getFieldValue(key);
const fd = this.fieldDescriptors.find((i) => i.key === key);
result = !isNullOrUndefined(fd)
? isNullOrUndefined(fd.hide) || !fd.hide
: false;
result =
result &&
(this.hideOptionalFieldsOnView
? this.showFieldsForm && !!fd.allowFieldForm
? true
: !isNullOrEmptyString(value) && !isNullOrUndefined(value)
: true);
//
return result;
}
/**
* Check a Field Label can Show or not ...
*
* @param key
* @returns
*/
canShowFieldLabel(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result = !isNullOrUndefined(fd)
? isNullOrUndefined(fd.hideLabel) || !fd.hideLabel
: false;
//
return result;
}
/**
* Check can Present Field as Form or not ...
* @param key
*/
canPresentFieldForm(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) &&
!isNullOrUndefined(fd.allowFieldForm) &&
fd.allowFieldForm &&
![
//
VModelFieldTypes.Object,
VModelFieldTypes.Function,
].includes(fd.type);
//
const isInFormView =
this.hasChild(this.inFormFields) && this.inFormFields.includes(key);
//
result = result && !isInFormView;
//
return result;
}
/**
* Check can Present Field as View or not ...
* @param key
*/
canPresentFieldView(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) &&
!isNullOrUndefined(fd.allowFieldForm) &&
!!fd.allowFieldForm;
//
const isInFormView =
this.hasChild(this.inFormFields) && this.inFormFields.includes(key);
//
result = result && isInFormView;
//
return result;
}
/**
* Check Can Show Specified Key's Content below
* Label ...
*
* @param key
* @returns
*/
canBreakContent(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
return result;
}
//
result =
!isNullOrUndefined(fd.showContentBelowLabel) &&
!!fd.showContentBelowLabel;
return result;
}
/**
* Push All Childs once ...
*
* @param key
* @returns
*/
canPushChilds(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
return result;
}
//
result =
fd.type === VModelFieldTypes.Array &&
!isNullOrUndefined(fd.pushArrayChildsOnceToTemplate) &&
!!fd.pushArrayChildsOnceToTemplate;
return result;
}
/**
* Check Specified Described Key has Custom Template Provided ...
*
* @param key
* @returns
*/
canShowCustomTemplate(key: string) {
//
return this.getValue(this.fieldDescriptors).pipe(
map((fds) => fds.find((fd) => fd.key === key)),
map((fd) => fd.hasCustomTemplate && !isNullOrUndefined(fd.template))
);
}
/**
* Check Can Reset Model or not ...
*
* @returns
*/
canReset() {
return this.isModelStateChanged();
}
/**
* Check Can Add or Update Model or not ...
*
* @returns
*/
canAddOrUpdate() {
return this.isModelStateHasValidChanges();
}
/**
* Check can Show Dto CRUD actions or not ...
*
* @returns
*/
canShowDtoActions() {
return this.getValue(this.lock);
}
/**
* Can Show Validation Errors ...
*
* @returns
*/
canShowValidationErros() {
return this.showValidationErrors && !isNullOrUndefined(this.model);
}
//#endregion
//
//#region is ...
/**
* Check a Field is in Field View ...
*
* @param key
* @returns
*/
isFieldView(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) &&
(!this.hasChild(this.inFormFields) || !this.inFormFields.includes(key));
//
return result;
}
/**
* Check a Filed is in Form View or not ...
*
* @param key
* @returns
*/
isFormView(key: string) {
//
let result = false;
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
result =
!isNullOrUndefined(fd) &&
this.hasChild(this.inFormFields) &&
this.inFormFields.includes(key);
//
return result;
}
/**
* compare to object is same or not ...
*
* @param model
* @param compareWith
* @returns
*/
isSame(model?: Partial<T>, compareWith?: Partial<T>) {
return isSameObject(this.normalize(model), this.normalize(compareWith));
}
/**
* Check current Described Fields are same as base ...
*
* @param model
* @returns
*/
isSameAsBase(model?: Partial<T>) {
return this.isSame(this.normalize(model), this.extractDecribedBaseModel());
}
/**
* Check current Described Fields are same as default ...
*
* @param model
* @returns
*/
isSameAsDefault(model?: Partial<T>) {
return this.isSame(
this.normalize(model),
this.extractDecribedDefaultModel()
);
}
/**
* Check Model is Changed or not ...
*
* @returns
*/
isModelStateChanged() {
//
const result =
!isNullOrUndefined(this.modelState) && this.modelState.isChanged;
return result;
}
/**
* Check Model is Changed and the Changes are Valid or not ...
*
* @returns
*/
isModelStateHasValidChanges() {
//
const result = this.isModelStateChanged() && this.modelState.isValid;
return result;
}
//#endregion
//#endregion
//
//#region Handlers ...
/**
* Handle XChanges on Form Field ...
*
* @param model
* @returns
*/
handleInFormFieldValueChanged(model: XFormControlValueChangeEventModel) {
//
// Validate Model ...
if (isNullOrUndefined(model)) {
return;
}
//
// Check Descriptor Field Exists ...
const fd = this.fieldDescriptors.find((f) => f.key === model.name);
if (isNullOrUndefined(fd)) {
return;
}
//
// Check Field is in Form View ...
if (!this.isFormView(fd.key)) {
return;
}
//
// Check Model Exists or not ...
const index = this.inFormFieldValues.findIndex((f) => f.key === fd.key);
if (index >= 0) {
//
// Update Exists Model ...
this.inFormFieldValues[index].value = model.value;
} else {
//
// Add New Changed Model ...
this.inFormFieldValues.push({
key: fd.key,
value: model.value,
});
}
//
this.changeSubject.next({ ...this.MODEL });
}
/**
* Apply Specified Value Form Changes to Model ...
*/
handleApplyFormFieldChanges(key: string) {
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
return;
}
//
if (!this.isFormView(key)) {
return;
}
//
const formValueControl = this.inFormFieldValues.find((f) => f.key === key);
if (!isNullOrUndefined(formValueControl)) {
//
setPropertyValue(this.MODEL, key, formValueControl.value);
//
this.changeSubject.next({ ...this.MODEL });
}
}
/**
* Handle Change Detection ...
*
* @param force
* @param delay
*/
handleChangeDetection(force: boolean = false) {
//
this.sendCardAction(XCardAction.Refresh);
this.sendFormAction(XFormBaseAction.Refresh);
//
this.detectChanges();
//
if (force) {
this.reloadComponent().then();
}
}
/**
* Check Current Model State based on informView Fields and
* notify What to Do on Each State ...
*/
handleFormFieldStates() {
//
const result = this.hasChild(this.inFormFields);
return result;
}
/**
* Apply Form Modle Changes ...
* whole described form controls ...
*
* @param model
* @returns
*/
handleDtoFormValueChanged(model: Partial<T>) {
//
if (isNullOrUndefined(model)) {
return;
}
//
const keys = Object.keys(model);
if (!this.hasChild(keys)) {
return;
}
//
for (const key of keys) {
//
const fd = this.fieldDescriptors.find((f) => f.key === key);
if (isNullOrUndefined(fd)) {
continue;
}
//
setPropertyValue(this.MODEL, key, (model as any)[key]);
}
//
this.changeSubject.next({ ...this.MODEL });
}
/**
* Handle Disbaled Requirements ...
*/
handleEnabled() {
this.handleStateOfViews(false);
}
/**
* Handle Enabled Requirements ...
*/
handleDisabled() {
this.handleStateOfViews(true);
}
/**
* Form Field Action Handlers ...
*
* @param key
* @param action
*/
handleFormFieldAction(
key: string,
action: VBaseDtoFieldViewActionIdentifier
) {
//
switch (action) {
//
case this.FieldViewActions.ShowForm:
this.toggleFieldView(key);
this.onFieldFormPresent(key);
break;
//
case this.FieldViewActions.Apply:
case this.FieldViewActions.Cancel:
if (action === this.FieldViewActions.Apply) {
this.handleApplyFormFieldChanges(key);
} else {
}
this.toggleFieldView(key);
break;
}
//
const actionModel: VModelFieldViewActionModel = {
key: key,
action: action,
};
this.onFieldPresentTypeChanged(actionModel);
this.fieldPresentTypeChanged.emit(actionModel);
}
/**
* reset all changes in model
* close all open FormFields and etc ...
*/
async handleResetModel() {
//
this.doLockBreakAction(() => {
//
this.formActionProvider.next({
action: XFormBaseAction.ClearData,
});
//
this.resetFieldDescriptors();
this.modelFormConfig = undefined;
//
this.reset();
this.detectChanges();
//
const action = VBaseDtoModelAction.Reset;
//
this.onDtoModelAction(action);
//
// Emit Event Handler ...
this.dtoModelActionFired.emit({
action,
payload: { ...this.modelState },
});
});
}
/**
* Cancel Form/Field Form(s) View ...
*
* @param toPresentType a View to change Present Type ...
*/
async handleCancelEditModel(
toPresentType: string = VBaseDtoPresentType.AsView
) {
//
await this.doLockBreakAction((toPresentType) => {
//
if (this.presentType === this.PresentTypes.AsForm) {
this.handleResetFormView(this.presentType);
} else {
this.handleResetFormView(toPresentType);
}
//
const action = VBaseDtoModelAction.Cancel;
//
this.onDtoModelAction(action);
//
// Emit Event Handler ...
this.dtoModelActionFired.emit({
action,
payload: { ...this.modelState },
});
});
}
/**
* handle form changes on FormView present type ...
*
* @param event
* @returns
*/
handleFormValueChanged(event: XFormValueChangeEventModel) {
//
if (this.presentType !== VBaseDtoPresentType.AsForm) {
return;
}
//
this.handleDtoFormValueChanged(event.value);
}
//
//#region Do ...
/**
* Hide and Show Fileds ...
*
* @param keys
* @param hide
* @returns
*/
hideFields(keys: XPartialModelFieldNames<T>, hide: boolean) {
//
// Validate Args ...
if (
isNullOrUndefined(keys) ||
!this.hasChild(keys) ||
!this.hasChild(this.fieldDescriptors)
) {
return;
}
//
const hideValue = !!hide;
const keysArr = toArray(keys).map((k) => k.toString());
//
this.handleResetFieldStates();
//
// Update Field Descriptor(s) ...
this.FIELD_DESCRIPTORS = [
...this.FIELD_DESCRIPTORS.map((fd) => {
//
const model: VModelFieldDescriptor<any> = {
...fd,
hide: keysArr.includes(fd.key) ? hideValue : fd.hide,
};
//
return model;
}),
];
//
this.detectChanges();
}
/**
* Hide and Show Filed Labels ...
*
* @param keys
* @param hide
* @returns
*/
hideFieldLabels(keys: XPartialModelFieldNames<T>, hide: boolean) {
//
// Validate Args ...
if (
isNullOrUndefined(keys) ||
!this.hasChild(keys) ||
!this.hasChild(this.fieldDescriptors)
) {
return;
}
//
const hideValue = !!hide;
const keysArr = toArray(keys).map((k) => k.toString());
//
this.handleResetFieldStates();
//
// Update Field Descriptor(s) ...
this.FIELD_DESCRIPTORS = [
...this.FIELD_DESCRIPTORS.map((fd) => {
//
const model: VModelFieldDescriptor<any> = {
...fd,
hideLabel: keysArr.includes(fd.key) ? hideValue : fd.hideLabel,
};
//
return model;
}),
];
//
this.detectChanges();
}
/**
* Allow / Disallow Field Form Edit ...
*
* @param keys
* @param disable
* @returns
*/
disableFieldEdits(keys: XPartialModelFieldNames<T>, disable: boolean) {
//
// Validate Args ...
if (
isNullOrUndefined(keys) ||
!this.hasChild(keys) ||
!this.hasChild(this.fieldDescriptors)
) {
return;
}
//
const disableValue = !!!disable;
const keysArr = toArray(keys).map((k) => k.toString());
//
this.handleResetFieldStates();
//
this.FIELD_DESCRIPTORS = [
...this.FIELD_DESCRIPTORS.map((fd) => {
//
// Renew Field Descriptor ...
const model: VModelFieldDescriptor<any> = {
...fd,
allowFieldForm: keysArr.includes(fd.key)
? disableValue
: fd.allowFieldForm,
};
//
return model;
}),
];
//
this.detectChanges();
}
/**
* Reproduce Field Descriptors ...
*/
resetFieldDescriptors() {
//
if (!this.hasChild(this.FIELD_DESCRIPTORS)) {
return;
}
//
this.FIELD_DESCRIPTORS = [];
this.detectChanges();
if (
!isNullOrUndefined(this.providedFieldDescriptors) &&
this.hasChild(this.providedFieldDescriptors)
) {
//
this.FIELD_DESCRIPTORS = [...this.providedFieldDescriptors];
} else {
//
this.FIELD_DESCRIPTORS = [...this.defaultFieldDescriptors];
}
//
this.onFieldDescriptorsReady();
this.detectChanges();
}
/**
* Toggle Field View ...
*
* @param key
* @returns
*/
toggleFieldView(key: string) {
//
const fd = this.fieldDescriptors.find((i) => i.key === key);
if (isNullOrUndefined(fd)) {
return;
}
//
// Check Can Toggle ...
if (isNullOrUndefined(fd.allowFieldForm) || !fd.allowFieldForm) {
return;
}
//
const isFieldView = this.isFieldView(key);
//
// Show Field Form ...
if (isFieldView) {
//
this.inFormFields.push(key);
let value = this.getFieldValue(key);
let fieldFormModel = {} as any;
fieldFormModel[key] = value;
//
// Here we Apply Some Changes for Handling Form Field ...
const fieldFormControlConfig = fd.formControlConfig;
if (!isNullOrUndefined(fieldFormControlConfig)) {
//
fieldFormControlConfig.updateOn = XFormUpdateOn.CHANGE;
fieldFormControlConfig.eventHandlers = {
valueChanged: (model) => {
this.handleInFormFieldValueChanged(model);
},
};
//
let fieldFormConfig: XSimpleFormConfig<any> = {
name: key,
model: fieldFormModel,
controls: [{ ...fieldFormControlConfig }],
};
this.inFormFieldsFormConfigs.push(fieldFormConfig);
}
}
//
// Show View Filed ...
else {
//
this.inFormFieldValues = this.inFormFieldValues.filter(
(k) => k.key !== key
);
this.inFormFields = this.inFormFields.filter((k) => k !== key);
this.inFormFieldsFormConfigs = this.inFormFieldsFormConfigs.filter(
(k) => k.name !== key
);
}
//
this.changeSubject.next({ ...this.MODEL });
}
/**
* prepare a provided object using Described fields ...
*
* @param model
*/
normalize(model?: Partial<T>) {
//
const defaultModel = {
...this.defaultModel,
...this.extractDecribedDefaultModel(),
};
//
if (isNullOrUndefined(model)) {
//
model = {
...defaultModel,
};
}
//
for (const fd of this.fieldDescriptors) {
//
const mdValue = (model as any)[fd.key];
(model as any)[fd.key] = !isNullOrUndefined(mdValue) ? mdValue : fd.value;
}
//
return model;
}
/**
* force Cancel Edit Mode ...
*/
cancelEdit() {
//
if (this.presentType === this.PresentTypes.AsForm) {
this.reset();
} else {
//
for (let key of this.inFormFields) {
this.toggleFieldView(key);
}
}
//
this.handleChangeDetection();
}
/**
* Do Focus On Specified Key Control ...
* only on Form Presenting ...
*
* @param key
*/
focusOnFormControl(key: string) {
//
if (isNullOrEmptyString(key)) {
return;
} else if (
this.presentType === this.PresentTypes.AsView &&
!this.isFormView(key)
) {
return;
} else if (this.presentType !== this.PresentTypes.AsForm) {
return;
}
//
let cIdx = this.fieldDescriptors.findIndex((c) => c.key === key);
if (cIdx < 0) {
return;
}
//
if (
isNullOrUndefined(
this.fieldDescriptors[cIdx].formControlConfig.focusHandler
)
) {
return;
}
//
this.fieldDescriptors[cIdx].formControlConfig.focusHandler.next();
}
/**
* Re Produce Component model ...
*
* @param force
* @param model
*/
reNewModel(force = false, model?: T) {
//
if (force) {
//
if (isNullOrUndefined(model)) {
this.model = model;
} else {
//
this.model = {
...model,
};
}
}
//
this.resetFieldDescriptors();
this.handleResetFieldStates();
this.reloadComponent().then(() => (this.showComponent = true));
}
/**
* reset all Changes ...
*/
reset() {
//
this.handleResetFieldStates();
//
this.MODEL = {
...this.BASE_MODEL,
};
//
this.changeSubject.next({ ...this.MODEL });
}
//#endregion
//#endregion
//
//#region Focus Handler ...
private focusHandlerIntervalId: NodeJS.Timeout = undefined;
public registerFocusHandler(formName: string, key: string) {
//
this.unregisterFocusHandler();
if (isNullOrEmptyString(formName) || isNullOrEmptyString(key)) {
return;
}
//
let className = `x-form-${formName}`;
this.focusHandlerIntervalId = setInterval(() => {
//
const target = document.querySelector(`.${className}`) as HTMLElement;
if (!isNullOrUndefined(target)) {
//
this.focusOnFormControl(key);
this.unregisterFocusHandler();
}
}, 100);
}
private unregisterFocusHandler() {
//
if (isNullOrUndefined(this.focusHandlerIntervalId)) {
return;
}
//
clearInterval(this.focusHandlerIntervalId);
this.focusHandlerIntervalId = undefined;
}
//#endregion
//
//#region Action Provider ...
//
private ACTION_PROVIDER_SUBSCRIPTION: Subscription = undefined;
private FORM_ACTION_PROVIDER_SUBSCRIPTION: Subscription = undefined;
private CARD_ACTION_PROVIDER_SUBSCRIPTION: Subscription = undefined;
//
private registerActionProvider() {
//
this.unregisterActionProviders();
//
if (!isNullOrUndefined(this.actionProvider)) {
//
this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider
.asObservable()
.subscribe(async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVBaseDtoAction(event.action)
) {
return;
}
//
await this.handleAction(event.action, event.payload);
});
}
//
if (!isNullOrUndefined(this.formActionProvider)) {
//
this.FORM_ACTION_PROVIDER_SUBSCRIPTION =
this.formActionProvider.subscribe(async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVBaseDtoAction(event.action)
) {
return;
}
//
await this.handleFormAction(event.action, event.payload);
});
}
//
if (!isNullOrUndefined(this.cardActionProvider)) {
//
this.CARD_ACTION_PROVIDER_SUBSCRIPTION =
this.cardActionProvider.subscribe(async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVBaseDtoAction(event.action)
) {
return;
}
//
await this.handleCardAction(event.action, event.payload);
});
}
}
//
private unregisterActionProviders() {
//
if (!isNullOrUndefined(this.ACTION_PROVIDER_SUBSCRIPTION)) {
//
this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.ACTION_PROVIDER_SUBSCRIPTION = undefined;
}
//
if (!isNullOrUndefined(this.FORM_ACTION_PROVIDER_SUBSCRIPTION)) {
//
this.FORM_ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.FORM_ACTION_PROVIDER_SUBSCRIPTION = undefined;
}
//
if (!isNullOrUndefined(this.CARD_ACTION_PROVIDER_SUBSCRIPTION)) {
//
this.CARD_ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.CARD_ACTION_PROVIDER_SUBSCRIPTION = undefined;
}
}
//
sendAction(action: VBaseDtoAction, payload?: any) {
//
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.actionProvider.next({ action, payload });
}
//
async handleAction(action: VBaseDtoAction, payload?: any) {
//
// Refresh ...
if (action === VBaseDtoAction.Refresh) {
//
if (isNullOrUndefined(payload)) {
this.handleChangeDetection();
} else {
//
if (isNumber(payload)) {
this.runTimedOut(() => this.handleChangeDetection(), payload);
} else if (
isBoolean(payload.force) &&
toBoolean(payload.force) === true
) {
this.handleChangeDetection(true);
}
}
}
//
// Reload ...
if (action === VBaseDtoAction.Reload) {
await this.reloadComponent();
}
//
// Renew Model ...
if (action === VBaseDtoAction.RenewModel && !isNullOrUndefined(payload)) {
this.reNewModel(true, { ...payload });
}
//
// Set Properties ...
if (
action === VBaseDtoAction.SetProperties &&
!isNullOrUndefined(payload)
) {
this.setProperties(payload.props, payload.section);
}
//
// Focus On Form Control ...
if (
action === VBaseDtoAction.FocusOnFormControl &&
!isNullOrUndefined(payload)
) {
this.focusOnFormControl(payload);
}
//
// ReState ...
if (action === VBaseDtoAction.ReState) {
this.handleDtoStateManagement();
}
}
//
sendFormAction(action: XFormBaseAction | XFormControlAction, payload?: any) {
//
if (isNullOrUndefined(this.formActionProvider)) {
return;
}
//
this.formActionProvider.next({ action, payload });
}
//
async handleFormAction(
action: XFormBaseAction | XFormControlAction | string,
payload?: any
) {}
//
sendCardAction(action: XCardAction, payload?: any) {
//
if (isNullOrUndefined(this.cardActionProvider)) {
return;
}
//
this.cardActionProvider.next({ action, payload });
}
//
async handleCardAction(action: XCardAction | string, payload?: any) {}
//#endregion
//
//#region Private ...
private registerChangeSubject() {
//
this.applyTakeUntilWrapper(this.changeSubject).subscribe((event) => {
this.handleDtoStateManagement();
});
}
//
private handleResetFormView(toPresentType: string) {
//
this.cancelEdit();
const isFormView = this.presentType === this.PresentTypes.AsForm;
if (isFormView && this.presentType !== toPresentType) {
this.presentType = toPresentType;
}
//
this.detectChanges();
}
/**
* Clean All Open Field Edits and Form Configs ...
*/
private handleResetFieldStates() {
//
this.inFormFields = [];
this.inFormFieldValues = [];
this.modelFormConfig = undefined;
this.inFormFieldsFormConfigs = [];
}
/**
* Handle State of Views ...
*
* @param disabled
*/
private async handleStateOfViews(disabled: boolean) {
//
// Check State of Field Descriptors ...
if (this.hasChild(this.fieldDescriptors)) {
//
// Loop through Descriptors ...
const fds = await Promise.all(
this.fieldDescriptors.map(async (fd) => {
//
// Check has Form Control Config ...
if (!isNullOrUndefined(fd.formControlConfig)) {
//
const appearance = await this.getValueAsync(
fd.formControlConfig.appearance
);
appearance.disabled = disabled;
//
fd.formControlConfig.appearance = {
...appearance,
};
}
//
return fd;
})
);
//
// Renew Field Descriptors ...
this.FIELD_DESCRIPTORS = [...fds];
}
//
// Check All Open Filed Form Configs ...
if (this.hasChild(this.inFormFieldsFormConfigs)) {
//
const fcs = await Promise.all(
this.inFormFieldsFormConfigs.map(async (fc) => {
//
if (!isNullOrUndefined(fc.controls)) {
//
let controls = await this.getArrayValueAsync(fc.controls);
if (this.hasChild(controls)) {
//
controls = await Promise.all([
...controls.map(async (c) => {
//
if (!isNullOrUndefined(c.appearance)) {
//
const appearance = await this.getValueAsync(c.appearance);
appearance.disabled = disabled;
c.appearance = {
...appearance,
};
}
//
return c;
}),
]);
//
fc.controls = [...controls];
}
}
//
return fc;
})
);
//
this.inFormFieldsFormConfigs = [...fcs];
}
}
/**
* Handle Prepare Form Model Config ...
*
* @returns
*/
private async prepareFormModelConfig() {
//
// Valide Model Form Config ...
if (
isNullOrUndefined(this.modelFormConfig) ||
isNullOrUndefined(this.modelFormConfig.controls)
) {
return;
}
//
let updated = false;
const controls = await this.getArrayValueAsync(
this.modelFormConfig.controls
);
for (let control of controls) {
//
const appearance = await this.getValueAsync(control.appearance);
if (!isNullOrUndefined(appearance)) {
//
if (
isNullOrUndefined(appearance.label) &&
isNullOrUndefined(appearance.placeholder)
) {
//
// Detect Field Descriptor ...
const fd = this.fieldDescriptors.find(
(f) => f.key === control.propName
);
if (!isNullOrUndefined(fd)) {
//
// Set Label ...
appearance.label = fd.label;
// appearance.appearance = XFormControlAppearance.Outline;
//
// Update Controle appearance ...
control.appearance = {
...appearance,
};
//
if (!updated) {
updated = true;
}
}
}
}
}
//
if (updated) {
//
this.modelFormConfig = {
...this.modelFormConfig,
controls: [...controls],
};
//
this.detectChanges();
}
//
this.onFormModelConfigPrepared();
}
//#endregion
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-error\v-error.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
class="v-card"
[color]="getValue(cardColor) | async"
[ngClass]="getValue(cardCssClass) | async"
[isInPage]="getValue(isInPage) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<div
class="v-component-wrapper"
*ngIf="hasChildValue(errors) | async"
[ngClass]="getValue(cssClass) | async"
[xColor]="getBackgroundColorValue() | async"
>
<!-- Title -->
<ng-container *ngTemplateOutlet="errorTitleRef"></ng-container>
<!-- Errors -->
<ng-container
*ngTemplateOutlet="
(countChildsValue(errors) | async) > 1
? multipleErrorRef
: singleErrorRef
"
>
</ng-container>
<!-- Actions -->
<ng-container *ngIf="!(getValue(wrapWithCard) | async)">
<div class="x-actions">
<ng-content select="[actions]"></ng-content>
</div>
</ng-container>
</div>
</ng-template>
<!-- Error Title Template -->
<ng-template #errorTitleRef>
<div class="v-error-title" *ngIf="notEmptyValue(this.errorTitle) | async">
<span
[isForeground]="true"
class="v-error-title-text"
[xColor]="getErrorTitleColor() | async"
>{{ getErrorTitle() | async }}</span
>
</div>
</ng-template>
<!-- Single Error Template -->
<ng-template #singleErrorRef>
<div class="v-error-message v-single-error">
<span [isForeground]="true" [xColor]="getColor() | async">
{{ getSingleErrorMessage() | async }}
</span>
</div>
</ng-template>
<!-- Multiple Error Template -->
<ng-template #multipleErrorRef>
<div class="v-error-message v-multiple-error">
<ul
class="v-error-list"
[isForeground]="true"
[xColor]="getColor() | async"
>
<li
class="v-error-list-item"
*ngFor="let error of getArrayValue(errors) | async"
>
{{ error }}
</li>
</ul>
</div>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-error\v-error.component.scss
.v-error-title-text::after {
content: ":";
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-error\v-error.component.ts
import {
isNullOrEmptyString,
XColorIdentifier,
XOneOrManyType,
XResourceIdentifier,
XStandardType,
} from 'x-framework-core';
import { VBaseInCardComponent } from '../classes/v-base.component';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { map } from 'rxjs/operators';
@Component({
selector: 'v-error',
templateUrl: './v-error.component.html',
styleUrls: ['./v-error.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VErrorComponent extends VBaseInCardComponent {
//
//#region Props ...
//
cardColor: XStandardType<string> = this.ColorNames.DangerShade;
//
@Input()
cssClass: XOneOrManyType<string>;
@Input()
errorTitle: XResourceIdentifier = this.ResourceIDs.error;
@Input()
errorTitleColor: XColorIdentifier = this.ColorNames.Light;
@Input()
errors: XOneOrManyType<string>;
@Input()
color: XColorIdentifier = this.ColorNames.Warning;
@Input()
backgroundColor: XColorIdentifier = this.ColorNames.DangerShade;
//#endregion
//
//#region Constructor ...
//#endregion
//
//#region LifeCycles ...
//#endregion
//
//#region Register Handlers ...
//#endregion
//
//#region Task Handlers ...
//#endregion
//
//#region UI Providers ...
getColor() {
return this.getValue(this.color).pipe(
map((color) =>
!isNullOrEmptyString(color) ? color : this.ColorNames.Warning
)
);
}
getErrorTitle() {
return this.getResourceValue(
isNullOrEmptyString(this.errorTitle.toString())
? this.errorTitle
: this.ResourceIDs.error
);
}
getErrorTitleColor() {
return this.getValue(this.errorTitleColor).pipe(
map((color) =>
!isNullOrEmptyString(color) ? color : this.ColorNames.Light
)
);
}
getBackgroundColorValue() {
return this.getValue(this.backgroundColor).pipe(
map((color) =>
!isNullOrEmptyString(color) ? color : this.ColorNames.DangerShade
)
);
}
getSingleErrorMessage() {
return this.getArrayValue(this.errors).pipe(
map((errors) => errors[0] || '')
);
}
//#endregion
//
//#region UI Handlers ...
//#endregion
//
//#region Actions ...
//#endregion
//
//#region Abstracts ...
//#endregion
//
//#region Private ...
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-lazy-view\v-lazy-view.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
class="v-card v-no-margin-card v-lazy-view-card"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
[ngClass]="getArrayValue(cssClass) | async"
class="v-lazy-view-container v-component-wrapper"
>
<!-- Component Data -->
<ng-container
*ngTemplateOutlet="
(getValue(loading) | async)
? loadingRef
: (notNullOrUndefinedValue(model) | async) &&
(notNullOrUndefinedValue(componentType) | async)
? presenterRef
: emptyRef
"
></ng-container>
<!-- Lazy Actions -->
<section class="v-lazy-view-actions"></section>
</section>
</ng-template>
<!-- -->
<!-- Templates -->
<!-- -->
<!-- Nothing Template -->
<ng-template #nothingRef></ng-template>
<!-- Empty Template -->
<ng-template #emptyRef>
<x-empty
(refresh)="handleRefreshTask()"
[actionProvider]="emptyActionProvider"
[color]="getValue(emptyColor) | async"
[message]="getValue(emptyMessage) | async"
[cssClass]="getValue(emptyCssClass) | async"
[refreshIcon]="getValue(emptyRefreshIcon) | async"
[messageColor]="getValue(emptyMessageColor) | async"
[foregroundColor]="getValue(emptyForegroundColor) | async"
[refreshButtonColor]="getValue(emptyRefreshButtonColor) | async"
[hasRefresh]="
modelLoaderTask &&
!((getValue(loading) | async) || (getValue(uiDisabled) | async))
"
*ngIf="(getValue(showEmpty) | async) && !(getValue(loading) | async)"
[foregroundMessageColor]="getValue(emptyForegroundMessageColor) | async"
[foregroundRefreshButtonColor]="getValue(emptyForegroundRefreshButtonColor)"
>
</x-empty>
</ng-template>
<!-- Loading Template -->
<ng-template #loadingRef>
<ng-container *ngIf="getValue(loading) | async">
<!-- Loader Container -->
<section class="v-lazy-view-loader">
<!-- Spinner Loading -->
<x-spinner
[color]="getValue(loaderColor) | async"
[name]="getValue(loaderSpinner) | async"
*ngIf="(getValue(loaderType) | async) === LoaderTypes.Spinner"
></x-spinner>
<!-- ProgressBar Loading -->
<x-progress-bar
[type]="ProgressBarTypes.Indeterminate"
[color]="getValue(loaderColor) | async"
*ngIf="(getValue(loaderType) | async) === LoaderTypes.Progress"
></x-progress-bar>
</section>
</ng-container>
</ng-template>
<!-- Presenter Template -->
<ng-template #presenterRef>
<!-- Render Component -->
<section
[xRenderInputs]="componentInputs"
[xRenderComponent]="componentType"
(xOnRendered)="handleComponentRendered($event)"
class="v-lazy-view-rendered-component-container">
</section>
</ng-template>
<!-- Error Template -->
<ng-template #errorRef> Nothing ...</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-lazy-view\v-lazy-view.component.scss
.v-lazy-view-loader {
text-align: center;
min-height: 50px;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-lazy-view\v-lazy-view.component.ts
import {
Type,
Input,
NgZone,
Inject,
Output,
Component,
Renderer2,
ElementRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XTask,
nameof,
XSpinner,
setTaskId,
generateUuid,
getFieldNames,
XStandardType,
XTaskPriority,
XOneOrManyType,
XColorIdentifier,
isNullOrUndefined,
XSpinnerIdentifier,
isNullOrEmptyString,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
XEmptyAction,
XPopoverService,
XIconIdentifier,
XProgressBarType,
XEmptyActionModel,
XComponentCardContainer,
XComponentEmptyContainer,
IXComponentEmptyContainer,
} from 'x-framework-components';
import {
VLazyViewLoadingType,
isLazyViewLoadingType,
VLazyViewLoadingTypeIdentifier,
} from './v-lazy-view.typings';
import { Observable, Subject } from 'rxjs';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/overlay';
import { XManagerService } from 'x-framework-services';
import { VBaseInCardComponent } from '../classes/v-base.component';
import { SharedService } from 'src/app/shared/services/shared.service';
@Component({
selector: 'v-lazy-view',
templateUrl: './v-lazy-view.component.html',
styleUrls: ['./v-lazy-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VLazyView<TModel = any, TView = any>
extends VBaseInCardComponent
implements IXComponentEmptyContainer
{
//
//#region Props ...
//
readonly LoaderSpinners = Object.assign({}, XSpinner);
readonly LoaderTypes = Object.assign({}, VLazyViewLoadingType);
readonly ProgressBarTypes = Object.assign({}, XProgressBarType);
//
// Set a UUID for Task Handling ...
private UNIQUE_ID: string = generateUuid('VLazyView');
get UUID() {
return this.UNIQUE_ID;
}
/**
* a Data Model which required to Load ...
*/
model: TModel = undefined;
/**
* Component Css Classes ...
*/
@Input()
cssClass: XOneOrManyType<string>;
/**
* Specified Lazy Loaded Component Type ...
*/
@Input()
componentType: Type<TView>;
/**
* a Task Which Load the Data Model ...
*/
@Input()
modelLoaderTask: Observable<TModel>;
/**
* a Property to Set Data Model to Component ...
*/
@Input()
modelAssignOnComponentPropKey: keyof TView;
/**
* Lazy Loaded Component Inputs ...
*/
@Input()
componentInputs: {
[key in keyof TView]?: any;
};
//
//#region Empty Props ...
//
showEmpty: XStandardType<boolean> = true;
//
emptyHasRefresh: XStandardType<boolean> = true;
//
@Output()
emptyRefresh: EventEmitter<void> = this.emptyPropertyProvider.emptyRefresh;
@Input()
emptyColor: XStandardType<XColorIdentifier> =
this.emptyPropertyProvider.emptyColor;
@Input()
emptyMessage: XStandardType<XResourceIdentifier> =
this.emptyPropertyProvider.emptyMessage;
@Input()
emptyCssClass: XStandardType<XOneOrManyType<string>> =
this.emptyPropertyProvider.emptyCssClass;
@Input()
emptyRefreshIcon: XStandardType<XIconIdentifier> =
this.emptyPropertyProvider.emptyRefreshIcon;
@Input()
emptyMessageColor: XStandardType<XColorIdentifier> =
this.emptyPropertyProvider.emptyMessageColor;
@Input()
emptyActionProvider: Subject<XEmptyActionModel> =
this.emptyPropertyProvider.emptyActionProvider;
@Input()
emptyForegroundColor: XStandardType<boolean> =
this.emptyPropertyProvider.emptyForegroundColor;
@Input()
emptyRefreshButtonColor: XStandardType<XColorIdentifier> =
this.emptyPropertyProvider.emptyRefreshButtonColor;
@Input()
emptyForegroundMessageColor: XStandardType<boolean> =
this.emptyPropertyProvider.emptyForegroundMessageColor;
@Input()
emptyForegroundRefreshButtonColor: XStandardType<boolean> =
this.emptyPropertyProvider.emptyForegroundRefreshButtonColor;
//#endregion
//
// TODO: Add Props for Loading Support ...
@Input()
loaderColor: XColorIdentifier = XColorWithBrightness.Warning;
@Input()
loaderType: VLazyViewLoadingTypeIdentifier = this.LoaderTypes.Spinner;
@Input()
loaderSpinner: XSpinnerIdentifier = this.LoaderSpinners.Lines;
//
@Output()
onComponentLoadingFinished = new EventEmitter<TView>();
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: XComponentCardContainer,
protected emptyPropertyProvider: XComponentEmptyContainer
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService,
propertyProvider
);
}
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleProcessModel();
}
/**
* Process Binding Changes ...
*
* @param changeKeys
*/
onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Check Empty Props Changes ...
const emptyProps = [
nameof<VLazyView>('emptyColor'),
nameof<VLazyView>('emptyMessage'),
nameof<VLazyView>('emptyRefresh'),
nameof<VLazyView>('emptyCssClass'),
nameof<VLazyView>('emptyRefreshIcon'),
nameof<VLazyView>('emptyMessageColor'),
nameof<VLazyView>('emptyActionProvider'),
nameof<VLazyView>('emptyForegroundColor'),
nameof<VLazyView>('emptyRefreshButtonColor'),
nameof<VLazyView>('emptyForegroundMessageColor'),
nameof<VLazyView>('emptyForegroundRefreshButtonColor'),
];
isChanged = emptyProps.findIndex((ep) => changeKeys.includes(ep)) >= 0;
if (isChanged) {
//
if (this.handleEmptyPropertiesChanged()) {
if (!applyChanges) {
applyChanges = true;
}
}
}
//
// Loader Color ...
isChanged = changeKeys.includes(nameof<VLazyView>('loaderColor'));
if (isChanged) {
//
if (
isNullOrUndefined(this.loaderColor) ||
isNullOrEmptyString(this.loaderColor)
) {
//
this.loaderColor = XColorWithBrightness.Warning;
if (!applyChanges) {
applyChanges = true;
}
}
}
//
// Loader Type ...
isChanged = changeKeys.includes(nameof<VLazyView>('loaderType'));
if (isChanged) {
//
if (!isLazyViewLoadingType(this.loaderType)) {
//
this.loaderType = this.LoaderTypes.Spinner;
if (!applyChanges) {
applyChanges = true;
}
}
}
//
// Loader Spinner ...
isChanged = changeKeys.includes(nameof<VLazyView>('loaderSpinner'));
if (isChanged) {
//
if (
isNullOrUndefined(this.loaderSpinner) ||
!isNullOrEmptyString(this.loaderSpinner)
) {
//
this.loaderSpinner = this.LoaderSpinners.Lines;
if (!applyChanges) {
applyChanges = true;
}
}
}
//
// Check Requirements for Handling Lazy Load ...
const modelProps = [
nameof<VLazyView>('componentType'),
nameof<VLazyView>('modelLoaderTask'),
nameof<VLazyView>('componentInputs'),
nameof<VLazyView>('modelAssignOnComponentPropKey'),
];
isChanged = modelProps.findIndex((mp) => changeKeys.includes(mp)) >= 0;
if (isChanged) {
this.handleProcessModel();
}
//
if (applyChanges) {
this.handleDetectChanges();
}
}
/**
* Register Requirements ...
*/
registerViewHandlers(): void {
super.registerViewHandlers();
}
//#endregion
//
//#region Handlers ...
/**
* Handle Detect Changes ...
*/
handleDetectChanges() {
//
this.detectChanges();
this.emptyActionProvider.next({ action: XEmptyAction.Refresh });
//
this.runTimedOut(() => this.handleDetectChanges(), 1000);
}
/**
* Handle Process Model and Lazy Loading ...
*/
handleProcessModel() {
//
// Check Requirements ...
const isValid =
!this.loading &&
!this.uiDisabled &&
isNullOrUndefined(this.model) &&
!isNullOrEmptyString(this.UUID) &&
!isNullOrUndefined(this.componentType) &&
!isNullOrUndefined(this.modelLoaderTask) &&
!isNullOrUndefined(this.modelAssignOnComponentPropKey);
if (!isValid) {
return;
}
//
this.applyLoadingWrapper(this.modelLoaderTask).subscribe((model) => {
//
if (!isNullOrUndefined(model)) {
//
this.model = {
...model,
};
} else {
this.model = undefined;
}
//
// Prepare Inputs ...
if (isNullOrUndefined(this.componentInputs)) {
this.componentInputs = {};
}
//
// Set Model to Inputs ...
(this.componentInputs as any)[this.modelAssignOnComponentPropKey] = {
...model,
};
//
this.handleDetectChanges();
});
}
/**
* When Refresh Button Clicked ...
*/
handleRefreshTask() {
//
this.handleProcessModel();
this.emptyRefresh.emit();
}
/**
* Provides Component instance for attaching
* Event Listeners or do anything else ...
*
* @param event
*/
handleComponentRendered(event: TView) {
this.onComponentLoadingFinished.emit(event);
}
//#endregion
//
//#region Private ...
private handleEmptyPropertiesChanged() {
//
let result = false;
//
if (isNullOrUndefined(this.emptyColor)) {
//
this.emptyColor = this.emptyPropertyProvider.emptyColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyMessage)) {
//
this.emptyMessage = this.emptyPropertyProvider.emptyMessage;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyCssClass)) {
//
this.emptyCssClass = this.emptyPropertyProvider.emptyCssClass;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyRefreshIcon)) {
//
this.emptyRefreshIcon = this.emptyPropertyProvider.emptyRefreshIcon;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyMessageColor)) {
//
this.emptyMessageColor = this.emptyPropertyProvider.emptyMessageColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyActionProvider)) {
//
this.emptyActionProvider = this.emptyPropertyProvider.emptyActionProvider;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyForegroundColor)) {
//
this.emptyForegroundColor =
this.emptyPropertyProvider.emptyForegroundColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyRefreshButtonColor)) {
//
this.emptyRefreshButtonColor =
this.emptyPropertyProvider.emptyRefreshButtonColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyForegroundMessageColor)) {
//
this.emptyForegroundMessageColor =
this.emptyPropertyProvider.emptyForegroundMessageColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyForegroundRefreshButtonColor)) {
//
this.emptyForegroundRefreshButtonColor =
this.emptyPropertyProvider.emptyForegroundRefreshButtonColor;
if (!result) {
result = true;
}
}
//
if (isNullOrUndefined(this.emptyRefresh)) {
//
this.emptyRefresh = this.emptyPropertyProvider.emptyRefresh;
if (!result) {
result = true;
}
}
//
return result;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-lazy-view\v-lazy-view.typings.ts
import { isValueInEnum } from "../tools/v-base.tools";
export enum VLazyViewLoadingType {
Spinner = 'spinner',
Progress = 'progress',
}
export type VLazyViewLoadingTypeIdentifier = VLazyViewLoadingType | string;
export function isLazyViewLoadingType(value: VLazyViewLoadingTypeIdentifier) {
return isValueInEnum<VLazyViewLoadingTypeIdentifier>(
value,
Object.assign({}, VLazyViewLoadingType)
);
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page\v-page.component.html
<x-page
#xPage
[loading]="loading"
[uiDisabled]="uiDisabled"
[color]="getValue(color) | async"
(loadMore)="loadMore.emit($event)"
[sideColor]="sideColor || 'primary'"
[hasSide]="getValue(hasSide) | async"
[actions]="getValue(actions) | async"
[sideSlot]="getValue(sideSlot) | async"
[sideType]="getValue(sideType) | async"
(refreshPage)="refreshPage.emit($event)"
[menuState]="getValue(menuState) | async"
[menuActionProvider]="menuActionProvider"
[toolbarBackHandler]="toolbarBackHandler"
[showFooter]="getValue(showFooter) | async"
[cssClass]="getArrayValue(cssClass) | async"
(scrolledEvent)="scrolledEvent.emit($event)"
(imageCaptured)="handleImageCaptured($event)"
[footerColor]="getValue(footerColor) | async"
[toolbarTitle]="getValue(toolbarTitle) | async"
[toolbarColor]="getValue(toolbarColor) | async"
[actionsActionProvider]="actionsActionProvider"
(menuStateChange)="menuStateChange.emit($event)"
[sideMenuWidth]="getValue(sideMenuWidth) | async"
[toolbarEndSlotButtonsTemplate]="
getValue(toolbarEndSlotButtonsTemplate) | async
"
[toolbarHasBack]="getValue(toolbarHasBack) | async"
[toolbarHasMenu]="getValue(toolbarHasMenu) | async"
[toolbarLogoUrl]="getValue(toolbarLogoUrl) | async"
[toolbarStartSlotButtonsTemplate]="
getValue(toolbarStartSlotButtonsTemplate) | async
"
[toggleMenuWhen]="getValue(toggleMenuWhen) | async"
[refreshingText]="getValue(refreshingText) | async"
[toolbarShowBack]="getValue(toolbarShowBack) | async"
[toolbarSubTitle]="getValue(toolbarSubTitle) | async"
[toolbarShowMenu]="getValue(toolbarShowMenu) | async"
[toolbarBackSlot]="getValue(toolbarBackSlot) | async"
[toolbarMenuSlot]="getValue(toolbarMenuSlot) | async"
[toolbarShowLogo]="getValue(toolbarShowLogo) | async"
[toolbarLogoSlot]="getValue(toolbarLogoSlot) | async"
[sideMenuMaxWidth]="getValue(sideMenuMaxWidth) | async"
[sideMenuMinWidth]="getValue(sideMenuMinWidth) | async"
[toolbarTitleSlot]="getValue(toolbarTitleSlot) | async"
[scrollEndClasses]="getValue(scrollEndClasses) | async"
[scrollingClasses]="getValue(scrollingClasses) | async"
[toolbarShowTitle]="getValue(toolbarShowTitle) | async"
[captureTopOffset]="getValue(captureTopOffset) | async"
[scrollEventActionProvider]="scrollEventActionProvider"
[toolbarContentTemplate]="
(getValue(showToolbarContent) | async)
? toolbarContentTemplate || toolbarContentTemplateRef
: undefined
"
[refreshingSpinner]="getValue(refreshingSpinner) | async"
[showPageRefresher]="getValue(showPageRefresher) | async"
[enableCaptureMode]="getValue(enableCaptureMode) | async"
[scrollBehaviour]="getArrayValue(scrollBehaviour) | async"
(splitterStateChanged)="splitterStateChanged.emit($event)"
[toolbarCssClass]="getArrayValue(toolbarCssClass) | async"
[refreshPullingText]="getValue(refreshPullingText) | async"
[refreshPullingIcon]="getValue(refreshPullingIcon) | async"
[toolbarContentSlot]="getValue(toolbarContentSlot) | async"
[toolbarDefaultHref]="getValue(toolbarDefaultHref) | async"
[scrollStartClasses]="getValue(scrollStartClasses) | async"
[footerContentTemplate]="footerContentTemplate || footerRef"
[toolbarAutoHideMenu]="getValue(toolbarAutoHideMenu) | async"
[captureBottomOffset]="getValue(captureBottomOffset) | async"
[provideScrollEvents]="getValue(provideScrollEvents) | async"
[toolbarShowSubTitle]="getValue(toolbarShowSubTitle) | async"
[downloadCapturedImage]="getValue(downloadCapturedImage) | async"
[toolbarEndSlotButtonsTemplate]="toolbarEndSlotButtonsTemplateRef"
[infinityScrollDisabled]="getValue(infinityScrollDisabled) | async"
[toolbarProgressBarColor]="getValue(toolbarProgressBarColor) | async"
[infinityScrollThreshold]="getValue(infinityScrollThreshold) | async"
[showPageInfinityScroller]="getValue(showPageInfinityScroller) | async"
[toolbarShowProgressBar]="
(getValue(loading) | async) || (getValue(toolbarShowProgressBar) | async)
"
[infinityScrollerLoadingText]="getValue(infinityScrollerLoadingText) | async"
[infinityScrollerLoadingSpinner]="getValue(infinityScrollerLoadingSpinner) | async"
>
<!-- Side Content -->
<div class="app-side-content" side>
<ng-container *ngTemplateOutlet="defaultSideMenuRef"></ng-container>
</div>
<!-- Page Content -->
<div class="app-page-content">
<ng-content></ng-content>
</div>
</x-page>
<!-- Page Footer -->
<ng-template #footerRef>
<!-- Content -->
<h4 class="x-footer-content">{{ resourceProvider(ResourceIDs.company) }}</h4>
</ng-template>
<!-- Page Side Menu -->
<ng-template #defaultSideMenuRef>
<x-navigator
[items]="navPages"
[loading]="loading"
[closeSiblings]="false"
[showChildIcon]="true"
[disableParentRouting]="true"
*ngIf="getValue(hasSide) | async"
[dividerColor]="ColorNames.Primary"
[highlightColor]="ColorNames.Warning"
[uiDisabled]="uiDisabled || loading"
[childColor]="ColorNames.PrimaryShade"
[parentColor]="ColorNames.TertiaryShade"
[parentActiveColor]="ColorNames.WarningShade"
[startupState]="NavigatorStartupStates.CollapseAll"
(itemSelected)="handleNavigatorItemSelected($event)"
></x-navigator>
</ng-template>
<!-- Toolbar Menu -->
<ng-template #toolbarEndSlotButtonsTemplateRef>
<x-button
[type]="'icon'"
[color]="'cb-inherit'"
(click)="handleMoreButtonClicked($event)"
>
<x-icon [name]="'menu_vertical'" [color]="'light'"></x-icon>
</x-button>
</ng-template>
<!-- Toolbar Content -->
<ng-template #toolbarContentTemplateRef>
<!-- Loading User Model -->
<ng-container *ngIf="sharedService.user$ | async as user">
<v-user
[model]="user"
[canSelectUser]="true"
*ngIf="canShowUser(user)"
[loading]="getValue(loading) | async"
[presentType]="UserPresentTypes.AsInfo"
[userInfoColor]="ColorNames.Transparent"
[uiDisabled]="getValue(uiDisabled) | async"
(userSelected)="handleUserSelected($event)"
></v-user>
</ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page\v-page.component.scss
// :host ::ng-deep .app-page-content {
// overflow: auto;
// height: calc(100% + (var(--content-offset-top) + var(--content-offset-bottom)));
// }
// :host ::ng-deep x-fab {
// .x-fab {
// margin-bottom: calc(var(--content-offset-bottom));
// }
// }
.x-footer-content {
margin: 0;
padding: 0;
}
:host ::ng-deep {
.x-toolbar-end-slot {
flex-shrink: 0 !important;
justify-content: flex-end;
}
.x-toolbar-start-slot {
flex-shrink: 1 !important;
flex-grow: 7 !important;
justify-content: flex-start;
overflow: hidden;
text-overflow: ellipsis;
}
x-expandable-list {
&.x-disabled {
//
// .item-parent,
// .item-child,
.expandable-item-parent {
background-color: var(--x-color-medium-shade) !important;
}
}
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page\v-page.component.ts
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
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page-presenter\v-page-presenter.component.html
<v-page
[color]="containerBase.color"
[loading]="containerBase.loading"
[hasSide]="containerBase.hasSide"
[actions]="containerBase.actions"
[sideSlot]="containerBase.sideSlot"
[sideType]="containerBase.sideType"
[cssClass]="containerBase.cssClass"
[sideColor]="containerBase.sideColor"
[menuState]="containerBase.menuState"
[uiDisabled]="containerBase.uiDisabled"
[footerColor]="containerBase.footerColor"
[showToolbar]="containerBase.showToolbar"
[toolbarColor]="containerBase.toolbarColor"
[toolbarTitle]="containerBase.toolbarTitle"
[sideMenuWidth]="containerBase.sideMenuWidth"
[refreshingText]="containerBase.refreshingText"
[toolbarHasBack]="containerBase.toolbarHasBack"
[toggleMenuWhen]="containerBase.toggleMenuWhen"
[toolbarHasMenu]="containerBase.toolbarHasMenu"
[toolbarLogoUrl]="containerBase.toolbarLogoUrl"
[toolbarMenuSlot]="containerBase.toolbarMenuSlot"
[toolbarShowMenu]="containerBase.toolbarShowMenu"
[scrollBehaviour]="containerBase.scrollBehaviour"
[scrollBehaviour]="containerBase.scrollBehaviour"
[toolbarLogoSlot]="containerBase.toolbarLogoSlot"
[toolbarShowLogo]="containerBase.toolbarShowLogo"
[toolbarBackSlot]="containerBase.toolbarBackSlot"
[toolbarShowBack]="containerBase.toolbarShowBack"
[toolbarSubTitle]="containerBase.toolbarSubTitle"
[sideMenuMinWidth]="containerBase.sideMenuMinWidth"
[sideMenuMaxWidth]="containerBase.sideMenuMaxWidth"
[toolbarTitleSlot]="containerBase.toolbarTitleSlot"
[toolbarShowTitle]="containerBase.toolbarShowTitle"
[scrollEndClasses]="containerBase.scrollEndClasses"
[captureTopOffset]="containerBase.captureTopOffset"
[refreshingSpinner]="containerBase.refreshingSpinner"
[showPageRefresher]="containerBase.showPageRefresher"
[enableCaptureMode]="containerBase.enableCaptureMode"
[refreshPullingText]="containerBase.refreshPullingText"
[refreshPullingIcon]="containerBase.refreshPullingIcon"
[showToolbarContent]="containerBase.showToolbarContent"
[showToolbarEndSlot]="containerBase.showToolbarEndSlot"
[toolbarDefaultHref]="containerBase.toolbarDefaultHref"
[toolbarContentSlot]="containerBase.toolbarContentSlot"
[scrollStartClasses]="containerBase.scrollStartClasses"
[toolbarBackHandler]="containerBase.toolbarBackHandler"
[toolbarAutoHideMenu]="containerBase.toolbarAutoHideMenu"
[toolbarShowSubTitle]="containerBase.toolbarShowSubTitle"
[provideScrollEvents]="containerBase.provideScrollEvents"
[captureBottomOffset]="containerBase.captureBottomOffset"
[downloadCapturedImage]="containerBase.downloadCapturedImage"
[toolbarShowProgressBar]="containerBase.toolbarShowProgressBar"
[infinityScrollDisabled]="containerBase.infinityScrollDisabled"
[infinityScrollThreshold]="containerBase.infinityScrollThreshold"
[toolbarProgressBarColor]="containerBase.toolbarProgressBarColor"
[showPageInfinityScroller]="containerBase.showPageInfinityScroller"
[infinityScrollerLoadingText]="containerBase.infinityScrollerLoadingText"
[infinityScrollerLoadingSpinner]="
containerBase.infinityScrollerLoadingSpinner
"
[menuActionProvider]="containerBase.menuActionProvider"
[toolbarActionProvider]="containerBase.toolbarActionProvider"
[actionsActionProvider]="containerBase.actionsActionProvider"
[scrollEventActionProvider]="containerBase.scrollEventActionProvider"
(loadMore)="containerBase.loadMore.emit($event)"
(refreshPage)="containerBase.refreshPage.emit($event)"
(imageCaptured)="containerBase.imageCaptured.emit($event)"
(scrolledEvent)="containerBase.scrolledEvent.emit($event)"
(menuStateChange)="containerBase.menuStateChange.emit($event)"
(splitterStateChanged)="containerBase.splitterStateChanged.emit($event)"
>
<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
</v-page>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page-presenter\v-page-presenter.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page-presenter\v-page-presenter.component.ts
import {
Input,
Component,
TemplateRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
nameof,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
VBasePageAction,
VBasePageActionModel,
} from './v-page-presenter.typings';
import { Subject, Subscription } from 'rxjs';
import { VPageComponent } from '../v-page/v-page.component';
import { VBaseComponent } from '../classes/v-base.component';
@Component({
selector: 'v-page-presenter',
templateUrl: './v-page-presenter.component.html',
styleUrls: ['./v-page-presenter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VPagePresenter extends VBaseComponent {
//
//#region Props ...
@Input()
containerBase: VPageComponent;
@Input()
contentTemplate: TemplateRef<any>;
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
}
async onChange(changeKeys: string[]) {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
isChanged = changeKeys.includes(nameof<VPagePresenter>('actionProvider'));
if (isChanged) {
this.subscribeActionProvider();
}
//
if (applyChanges) {
//
if (!isNullOrUndefined(this.containerBase)) {
this.containerBase.detectChanges();
}
//
this.detectChanges();
}
}
//#endregion
//
//#region Hnadlers ...
//#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.RefreshHost
) {
this.detectChanges();
}
});
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-page-presenter\v-page-presenter.typings.ts
import { isValueInEnum } from '../tools/v-base.tools';
import { XBaseActionModel } from 'x-framework-components';
//
//#region Action Provider ...
export enum VBasePageAction {
Refresh = 'Refresh',
RefreshHost = 'RefreshHost',
RefreshContainer = 'RefreshContainer',
}
export type VBasePageActionIdentifier = VBasePageAction | string;
export function isVBasePageAction(value: string) {
return isValueInEnum(value, Object.assign(VBasePageAction, {}));
}
export interface VBasePageActionModel
extends XBaseActionModel<VBasePageAction> {}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-paginator\v-paginator.component.html
<!-- -->
<!-- Main Content -->
<!-- -->
<ion-toolbar
class="x-paginator-bar"
*ngIf="notNullOrUndefinedValue(options) | async"
[xColor]="getValue(color) | async"
>
<!-- Mat Paginator Component -->
<mat-paginator
#matPaginator
[hidden]="true"
[disabled]="loading || uiDisabled"
[pageSize]="getValue(options.pageSize) | async"
></mat-paginator>
<!-- Start Slot Contents -->
<ion-buttons slot="start" *ngIf="options">
<!-- First -->
<ion-button
#first
(click)="firstPage()"
*ngIf="options.first"
[hidden]="getValue(options.first.hidden) | async"
[disabled]="
loading || uiDisabled || (getValue(options.first.disabled) | async)
"
>
<ng-container
*ngTemplateOutlet="
iconRef;
context: {
name: getValue(options.first.icon) | async,
tooltip: getResourceValue(options.first.title) | async,
color: getValue(options.first.color) | async,
applyDirection: true
}
"
></ng-container>
</ion-button>
<!-- Previous -->
<ion-button
#previous
(click)="previousPage()"
*ngIf="options.prev"
[hidden]="getValue(options.prev.hidden) | async"
[disabled]="
loading || uiDisabled || (getValue(options.prev.disabled) | async)
"
>
<ng-container
*ngTemplateOutlet="
iconRef;
context: {
name: getValue(options.prev.icon) | async,
tooltip: getResourceValue(options.prev.title) | async,
color: getValue(options.prev.color) | async,
applyDirection: true
}
"
></ng-container>
</ion-button>
</ion-buttons>
<!-- Pagination Info -->
<div class="x-paginator-wrapper">
<div fxLayout="row" fxLayoutAlign="center stretch">
<!-- Current Page -->
<div fxFlex="auto">
<ion-button
fill="clear"
expand="full"
class="cb-inherit"
(click)="showPageNumberInput = true"
[disabled]="loading || uiDisabled"
*ngIf="!showPageNumberInput && options && options.pageInfoProvider"
>
{{
options.pageInfoProvider(
current,
options.pageSize,
totalPages,
totalItems
)
}}
</ion-button>
<!-- Change Current Page -->
<ion-input
step="1"
[min]="1"
type="number"
debounce="500"
required="true"
autofocus="true"
[value]="current"
[max]="totalPages"
class="cb-inherit"
inputmode="numeric"
*ngIf="showPageNumberInput"
[disabled]="loading || uiDisabled"
(ionBlur)="showPageNumberInput = false"
(ionChange)="handlePageChange($event)"
></ion-input>
</div>
<!-- Page Size -->
<div fxFlex="auto">
<ion-button
fill="clear"
expand="full"
class="cb-inherit"
*ngIf="options.pageSize"
[disabled]="loading || uiDisabled"
(click)="handlePageSizeChange($event)"
>
{{ applyLocale(options.pageSize) }}
</ion-button>
</div>
</div>
</div>
<!-- End Slot Content -->
<ion-buttons slot="end">
<!-- Noxt -->
<ion-button
#next
(click)="nextPage()"
*ngIf="options.next"
[hidden]="getValue(options.next.hidden) | async"
[disabled]="
loading || uiDisabled || (getValue(options.next.disabled) | async)
"
>
<ng-container
*ngTemplateOutlet="
iconRef;
context: {
name: getValue(options.next.icon) | async,
tooltip: getResourceValue(options.next.title) | async,
color: getValue(options.next.color) | async,
applyDirection: true
}
"
></ng-container>
</ion-button>
<!-- Last -->
<ion-button
#last
(click)="lastPage()"
*ngIf="options.last"
[hidden]="getValue(options.last.hidden) | async"
[disabled]="
loading || uiDisabled || (getValue(options.last.disabled) | async)
"
>
<ng-container
*ngTemplateOutlet="
iconRef;
context: {
name: getValue(options.last.icon) | async,
tooltip: getResourceValue(options.last.title) | async,
color: getValue(options.prev.color) | async,
applyDirection: true
}
"
></ng-container>
</ion-button>
</ion-buttons>
</ion-toolbar>
<!-- -->
<!-- Templates -->
<!-- -->
<!-- Icon Template Ref -->
<ng-template
#iconRef
let-name="name"
let-tooltip="tooltip"
let-color="color"
let-applyDirection="applyDirection"
>
<x-icon
[name]="name"
[color]="color"
[loading]="loading"
[matTooltip]="tooltip"
[foregroundColor]="true"
[uiDisabled]="uiDisabled"
[applyDirection]="applyDirection"
[cssClass]="!color ? 'cb-inherit' : null"
></x-icon>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-paginator\v-paginator.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-paginator\v-paginator.component.ts
import {
isNumber,
isFunction,
getFieldNames,
isNullOrUndefined,
XColorIdentifier,
} from 'x-framework-core';
import {
VPaginatorAction,
VPaginatorOptions,
isVPaginatorAction,
VPaginatorActionModel,
VPaginatorActionButton,
} from './v-paginator.typings';
import { Subject, Subscription } from 'rxjs';
import { VBaseComponent } from '../classes/v-base.component';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'v-paginator',
templateUrl: './v-paginator.component.html',
styleUrls: ['./v-paginator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VPaginatorComponent extends VBaseComponent {
//
//#region Props ...
//
readonly ComponentFiledNames = getFieldNames(this);
@Input()
actionProvider: Subject<VPaginatorActionModel> = undefined;
@Input()
showPageNumberInput = false;
@Input()
current: number = undefined;
@Input()
totalPages: number = undefined;
@Input()
totalItems: number = undefined;
@Input()
color: XColorIdentifier = undefined;
@Input()
firstButtonDescriptor: VPaginatorActionButton = undefined;
@Input()
nextButtonDescriptor: VPaginatorActionButton = undefined;
@Input()
prevButtonDescriptor: VPaginatorActionButton = undefined;
@Input()
lastButtonDescriptor: VPaginatorActionButton = undefined;
//
@Input()
pageSizeProvider: () => number = undefined;
@Input()
pageSizesProvider: () => Array<number> = undefined;
@Input()
pageInfoProvider: (
page: number,
pageSize: number,
totalPages: number,
totalItems: number
) => string = undefined;
//
private OPTIONS: VPaginatorOptions = undefined;
get options() {
//
// Do Options Preparation ...
this.OPTIONS = {
//
first: {
...this.firstButtonDescriptor,
},
//
last: {
...this.lastButtonDescriptor,
},
//
next: {
...this.nextButtonDescriptor,
},
//
prev: {
...this.prevButtonDescriptor,
},
//
pageSize: this.pageSizeProvider(),
pageInfoProvider: this.pageInfoProvider,
pageSizes: [...this.pageSizesProvider()],
};
//
return this.OPTIONS;
}
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
}
afterViewInit(): void {
super.afterViewInit();
this.applyButtonDescriptors();
}
registerViewHandlers(): void {
super.registerViewHandlers();
//
this.subscribeActionProvider();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Action Provider ...
isChanged = changeKeys.includes(this.ComponentFiledNames.actionProvider);
if (isChanged) {
//
if (isNullOrUndefined(this.actionProvider)) {
//
applyChanges = true;
this.actionProvider = new Subject<VPaginatorActionModel>();
}
//
this.subscribeActionProvider();
}
//
// Color ...
isChanged = changeKeys.includes(this.ComponentFiledNames.color);
if (isChanged) {
//
if (isNullOrUndefined(this.color)) {
//
applyChanges = true;
this.color = this.ColorNames.Primary;
}
}
//
// Page ...
isChanged = changeKeys.includes(this.ComponentFiledNames.pageSizeProvider);
if (isChanged) {
//
if (
isNullOrUndefined(this.pageSizeProvider) ||
!isFunction(this.pageSizeProvider)
) {
//
applyChanges = true;
this.pageSizeProvider = () => {
return 0;
};
}
}
//
// Page Sizes ...
isChanged = changeKeys.includes(this.ComponentFiledNames.pageSizesProvider);
if (isChanged) {
//
if (
isNullOrUndefined(this.pageSizesProvider) ||
!isFunction(this.pageSizesProvider)
) {
//
applyChanges = true;
this.pageSizesProvider = () => {
return [0];
};
}
}
//
// Page Info Provider ...
isChanged = changeKeys.includes(this.ComponentFiledNames.pageInfoProvider);
if (isChanged) {
//
if (
isNullOrUndefined(this.pageInfoProvider) ||
!isFunction(this.pageInfoProvider)
) {
//
applyChanges = true;
this.pageInfoProvider = (
page: number,
pageSize: number,
totalPages: number,
totalItems: number
) => {
return this.applyLocale(`${page}/${totalPages}`);
};
}
}
//
// Button Descriptors Change ...
isChanged =
changeKeys.includes(this.ComponentFiledNames.firstButtonDescriptor) ||
changeKeys.includes(this.ComponentFiledNames.lastButtonDescriptor) ||
changeKeys.includes(this.ComponentFiledNames.nextButtonDescriptor) ||
changeKeys.includes(this.ComponentFiledNames.prevButtonDescriptor);
if (isChanged) {
applyChanges = this.applyButtonDescriptors();
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region Actions ...
firstPage() {
this.sendActionProviderAction(VPaginatorAction.First);
}
lastPage() {
this.sendActionProviderAction(VPaginatorAction.Last);
}
nextPage() {
this.sendActionProviderAction(VPaginatorAction.Next, this.current);
}
previousPage() {
this.sendActionProviderAction(VPaginatorAction.Prev, this.current);
}
//#endregion
//
//#region Handlers ...
async handlePageChange(event: CustomEvent | any) {
//
const selectedPage = (event.detail.value as number) || undefined;
if (!selectedPage) {
return;
}
//
this.sendActionProviderAction(VPaginatorAction.To, selectedPage);
}
async handlePageSizeChange(event?: any) {
this.sendActionProviderAction(VPaginatorAction.SelectPageSizeRequest, {
current: this.pageSizeProvider(),
allowed: this.pageSizesProvider(),
});
}
//#endregion
//
//#region Action Provider ...
private ACTION_PROVIDER_SUBSCRIPTION: Subscription = undefined;
private unsubscribeActionProvider() {
//
if (isNullOrUndefined(this.ACTION_PROVIDER_SUBSCRIPTION)) {
return;
}
//
this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.ACTION_PROVIDER_SUBSCRIPTION = undefined;
}
private subscribeActionProvider() {
//
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.unsubscribeActionProvider();
//
this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider.subscribe(
(action) => {
//
if (isNullOrUndefined(action)) {
return;
}
//
// Handle Different Actions ...
if (action.action === VPaginatorAction.Refresh) {
//
if (!isNullOrUndefined(action.payload) && isNumber(action.payload)) {
this.runTimedOut(() => this.detectChanges(), action.payload);
} else {
this.detectChanges();
}
}
}
);
}
private sendActionProviderAction(action: VPaginatorAction, payload?: any) {
//
// Validate ...
if (!isVPaginatorAction(action) || isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.actionProvider.next({ action, payload });
}
//#endregion
//
//#region Private ...
private applyButtonDescriptors() {
//
let result = true;
//
//#region First ...
//
// Object ...
if (isNullOrUndefined(this.firstButtonDescriptor)) {
this.firstButtonDescriptor = {};
}
//
// Color ...
if (isNullOrUndefined(this.firstButtonDescriptor.color)) {
this.firstButtonDescriptor.color = this.ColorNames.Light;
}
//
// Icon ..
if (isNullOrUndefined(this.firstButtonDescriptor.icon)) {
this.firstButtonDescriptor.icon = this.IconNames.first;
}
//
// Title ..
if (isNullOrUndefined(this.firstButtonDescriptor.title)) {
this.firstButtonDescriptor.title = this.ResourceIDs.first;
}
//#endregion
//
//#region Last ...
//
// Object ...
if (isNullOrUndefined(this.lastButtonDescriptor)) {
this.lastButtonDescriptor = {};
}
//
// Color ...
if (isNullOrUndefined(this.lastButtonDescriptor.color)) {
this.lastButtonDescriptor.color = this.ColorNames.Light;
}
//
// Icon ..
if (isNullOrUndefined(this.lastButtonDescriptor.icon)) {
this.lastButtonDescriptor.icon = this.IconNames.last;
}
//
// Title ..
if (isNullOrUndefined(this.lastButtonDescriptor.title)) {
this.lastButtonDescriptor.title = this.ResourceIDs.last;
}
//#endregion
//
//#region Next ...
//
// Object ...
if (isNullOrUndefined(this.nextButtonDescriptor)) {
this.nextButtonDescriptor = {};
}
//
// Color ...
if (isNullOrUndefined(this.nextButtonDescriptor.color)) {
this.nextButtonDescriptor.color = this.ColorNames.Light;
}
//
// Icon ..
if (isNullOrUndefined(this.nextButtonDescriptor.icon)) {
this.nextButtonDescriptor.icon = this.IconNames.next;
}
//
// Title ..
if (isNullOrUndefined(this.nextButtonDescriptor.title)) {
this.nextButtonDescriptor.title = this.ResourceIDs.next;
}
//#endregion
//
//#region Prev ...
//
// Object ...
if (isNullOrUndefined(this.prevButtonDescriptor)) {
this.prevButtonDescriptor = {};
}
//
// Color ...
if (isNullOrUndefined(this.prevButtonDescriptor.color)) {
this.prevButtonDescriptor.color = this.ColorNames.Light;
}
//
// Icon ..
if (isNullOrUndefined(this.prevButtonDescriptor.icon)) {
this.prevButtonDescriptor.icon = this.IconNames.previous;
}
//
// Title ..
if (isNullOrUndefined(this.prevButtonDescriptor.title)) {
this.prevButtonDescriptor.title = this.ResourceIDs.previous;
}
//#endregion
//
this.sendActionProviderAction(VPaginatorAction.UpdateButtonDescriptors, {
first: this.firstButtonDescriptor,
last: this.lastButtonDescriptor,
next: this.nextButtonDescriptor,
prev: this.prevButtonDescriptor,
});
//
return result;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-paginator\v-paginator.typings.ts
import {
XLocale,
XStandardType,
XColorIdentifier,
XResourceIdentifier,
} from 'x-framework-core';
import { isValueInEnum } from '../tools/v-base.tools';
import { XBaseActionModel, XIconIdentifier } from 'x-framework-components';
export interface VPaginatorActionButton {
hidden?: boolean;
disabled?: boolean;
icon?: XIconIdentifier;
color?: XColorIdentifier;
title?: XResourceIdentifier;
handler?: (item?: any) => XStandardType<void>;
}
//
//#region Action Provider ...
export enum VPaginatorAction {
Refresh = 'refresh',
To = 'To',
Last = 'Last',
Next = 'Next',
Prev = 'Prev',
First = 'First',
SelectPageSizeRequest = 'SelectPageSizeRequest',
UpdateButtonDescriptors = 'UpdateButtonDescriptors',
}
export type VPaginatorActionIdentifier = VPaginatorAction | string;
export function isVPaginatorAction(value: string) {
return isValueInEnum(value, Object.assign(VPaginatorAction, {}));
}
export interface VPaginatorActionModel
extends XBaseActionModel<VPaginatorAction> {}
//#endregion
export interface VPaginatorOptions {
pageSizes: number[];
pageSize: number;
first?: VPaginatorActionButton;
last?: VPaginatorActionButton;
next?: VPaginatorActionButton;
prev?: VPaginatorActionButton;
pageInfoProvider: (
currentPage: number,
pageSize: number,
totalPages: number,
totalItems: number
) => string;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-person\v-person.component.html
<!-- Non View Presenting -->
<ng-container *ngIf="presentType !== PersonPresentTypes.AsView">
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
class="v-card v-dto-card"
[avatarUrl]="getCardAvatar()"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
[class.v-no-margin-card]="getValue(removeCardMargin) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
class="v-dto v-component-wrapper"
[ngClass]="getArrayValue(cssClass) | async"
>
<!-- Component Data -->
<ng-container *ngIf="getValue(presentType) | async as prType">
<ng-container
*ngTemplateOutlet="
prType === PersonPresentTypes.AsName
? namePresenterRef
: prType === PersonPresentTypes.AsInfo
? infoPresenterRef
: prType === PersonPresentTypes.AsAvatar
? avatarPresenterRef
: prType === PersonPresentTypes.AsCard
? cardPresenterRef
: nothingPresentTypeRef
"
></ng-container>
</ng-container>
</section>
</ng-template>
</ng-container>
<!-- View Presenting -->
<ng-container *ngIf="presentType === PersonPresentTypes.AsView">
<ng-container *ngTemplateOutlet="viewPresenterRef"></ng-container>
</ng-container>
<!-- -->
<!-- Present Types Templates -->
<!-- -->
<!-- View Template -->
<ng-template #viewPresenterRef>
<v-dto #dtoPresenter [dtoContainer]="this"></v-dto>
</ng-template>
<!-- Name Template -->
<ng-template #namePresenterRef>
<section class="v-person-full-name">
{{ getFullName() }}
</section>
</ng-template>
<!-- Avatar Template -->
<ng-template #avatarPresenterRef>
<div class="v-avatar-box">
<img
[src]="getAvatar()"
class="v-avatar-image animated zoomIn center-vertical"
/>
</div>
</ng-template>
<!-- Card Info Template -->
<ng-template #infoPresenterRef>
<div class="v-person-card-info">
<!-- Full Name -->
<div class="v-person-card-info-top">
<ng-container *ngTemplateOutlet="namePresenterRef"></ng-container>
</div>
<!-- User Name -->
<div class="v-person-card-info-bottom">
<section class="v-person-user-name">
{{ getFieldValue(FieldNames.username) }}
</section>
</div>
</div>
</ng-template>
<!-- Card Template -->
<ng-template #cardPresenterRef>
<div class="v-person-card">
<x-slotter
[hasCenterSlot]="false"
[layout]="SlotLayouts.HORIZONTAL"
[startTopSlotCssClass]="
avatarSlotInCardView === SlotNames.START ? 'x-dynamic-slot' : ''
"
[endBottomSlotCssClass]="
avatarSlotInCardView === SlotNames.END ? 'x-dynamic-slot' : ''
"
>
<!-- Start -->
<x-slot [name]="SlotNames.START" [cssClass]="'ion-text-start'">
<!-- Fill Template -->
<ng-container
*ngTemplateOutlet="
avatarSlotInCardView === SlotNames.START
? avatarPresenterRef
: infoPresenterRef
"
>
</ng-container>
</x-slot>
<!-- End -->
<x-slot [name]="SlotNames.END" [cssClass]="'ion-text-end'">
<!-- Fill Template -->
<ng-container
*ngTemplateOutlet="
avatarSlotInCardView === SlotNames.END
? avatarPresenterRef
: infoPresenterRef
"
>
</ng-container>
</x-slot>
</x-slotter>
</div>
</ng-template>
<!-- Nothing Template -->
<ng-template #nothingPresentTypeRef> </ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-person\v-person.component.scss
.v-avatar-box,
.v-avatar-image {
width: 32px;
height: 32px;
}
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
.v-person-full-name {
font-size: small;
}
.v-person-user-name {
font-size: x-small;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-person\v-person.component.ts
import {
XPersonDto,
defaultPersonDto,
preparePersonDtoFields,
} from 'x-saherelm-api-sdk';
import {
PersonPresentType,
isPersonPresentType,
PersonPresentTypeIdentifier,
} from './v-person.typings';
import {
XSlotName,
XSlotLayout,
XSlotNameIdentifier,
} from 'x-framework-components';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../v-dto/v-dto.typings';
import { getPersonAvatar, getPersonFullName } from './v-person.tools';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { nameof, getFieldNames, isNullOrEmptyString } from 'x-framework-core';
@Component({
selector: 'v-person',
templateUrl: './v-person.component.html',
styleUrls: ['./v-person.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VPersonComponent extends VBaseDtoComponent<XPersonDto> {
//
//#region Props ...
addDtoTaskName: string;
updateDtoTaskName: string;
//
readonly SlotNames = Object.assign(XSlotName, {});
readonly SlotLayouts = Object.assign(XSlotLayout, {});
readonly PersonPresentTypes = Object.assign(PersonPresentType, {});
//
readonly defaultModel: XPersonDto = { ...defaultPersonDto };
readonly FieldNames = getFieldNames(this.defaultModel);
//
hideOptionalFieldsOnView: boolean = false;
//
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// User Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.username,
value: this.defaultModel.username,
label: this.ResourceIDs.username,
type: this.ModelFieldTypes.String,
},
//
// First Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.firstname,
value: this.defaultModel.firstname,
label: this.ResourceIDs.first_name,
type: this.ModelFieldTypes.String,
},
//
// Last Name ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.lastname,
value: this.defaultModel.lastname,
label: this.ResourceIDs.last_name,
type: this.ModelFieldTypes.String,
},
//
// Avatar ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.avatar,
value: this.defaultModel.avatar,
label: this.ResourceIDs.avatar,
type: this.ModelFieldTypes.String,
},
];
//
@Input()
presentType: PersonPresentTypeIdentifier = this.PersonPresentTypes.AsView;
@Input()
avatarSlotInCardView: XSlotNameIdentifier = this.SlotNames.START;
@Input()
showAvatarInCard: boolean = false;
//#endregion
//
//#region LifeCycles ...
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Handle Preset Type Changes ...
isChanged = changeKeys.includes(nameof<VPersonComponent>('presentType'));
if (isChanged) {
//
if (!isPersonPresentType(this.presentType)) {
//
applyChanges = true;
this.presentType = this.PersonPresentTypes.AsView;
}
}
//
// Handle Avatar Slot Changed ...
isChanged = changeKeys.includes(
nameof<VPersonComponent>('avatarSlotInCardView'),
);
if (isChanged) {
//
if (isNullOrEmptyString(this.avatarSlotInCardView)) {
applyChanges = true;
this.avatarSlotInCardView = this.SlotNames.START;
}
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region UI Providers ...
getFullName() {
return getPersonFullName(this.model);
}
getAvatar() {
return getPersonAvatar(this.model);
}
getCardAvatar() {
//
if (!this.showAvatarInCard) {
return undefined;
}
//
return this.getAvatar();
}
//#endregion
//
//#region Abstract ...
validate(model?: Partial<XPersonDto>): VModelValidationResult<XPersonDto> {
throw new Error('Method not implemented.');
}
prepare(model?: XPersonDto): XPersonDto {
return preparePersonDtoFields(model);
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-person\v-person.tools.ts
import { XPersonDto } from 'x-saherelm-api-sdk';
import { defaultPersonAvatar, defaultPersonFullName } from './v-person.typings';
import { isNullOrEmptyString, isNullOrUndefined } from 'x-framework-core';
export function getPersonFullName(model: XPersonDto) {
//
let result = defaultPersonFullName;
//
if (
!isNullOrUndefined(model) &&
(!isNullOrEmptyString(model.firstname) ||
!isNullOrEmptyString(model.lastname))
) {
result = `${model.firstname} ${model.lastname}`;
}
//
return result;
}
export function getPersonAvatar(model: XPersonDto) {
//
let result = defaultPersonAvatar;
//
if (!isNullOrUndefined(model) && !isNullOrEmptyString(model.avatar)) {
result = model.avatar;
}
//
return result;
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-person\v-person.typings.ts
import { isValueInEnum } from "../public-api";
export enum PersonPresentType {
AsView = 'as_view',
AsName = 'as_name',
AsInfo = 'as_info',
AsCard = 'as_card',
AsAvatar = 'as_avatar',
}
export type PersonPresentTypeIdentifier = PersonPresentType | string;
export function isPersonPresentType(value: string) {
return isValueInEnum(value, Object.assign(PersonPresentType, {}));
}
//
export const defaultPersonFullName = '...';
export const defaultPersonAvatar = '/assets/image/default-user-image.png';
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-query-presenter\v-query-presenter.base.page.ts
import {
Inject,
NgZone,
Component,
Renderer2,
ElementRef,
TemplateRef,
ChangeDetectorRef,
} from '@angular/core';
import {
XStandardType,
XBehaviourEvent,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
} from 'x-framework-core';
import { Subject } from 'rxjs';
import {
XSlotName,
XListItem,
XSlotLayout,
XFabButtonSide,
XPageContainer,
XIconIdentifier,
XPopoverService,
XFormControlType,
XFormControlAppearance,
XSearchBarTypeIdentifier,
XSearchBarShowButtonIdentifier,
XSearchBarEnterKeyHintIdentifier,
} from 'x-framework-components';
import {
defaultAddFabButton,
defaultEditFabButton,
defaultOptionsFabItem,
defaultRemoveFabButton,
defaultRefreshFabButton,
} from '../constants/fab.defaults';
import {
VQueryPresenterTabs,
VQueryPresenterAction,
isVQueryPresenterAction,
VQueryPresenterContainer,
IVQueryPresenterContainer,
VQueryPresenterActionModel,
VQueryPresenterActionIdentifier,
VQueryPresenterPaginatorPosition,
VQueryFilledItemsProcessingResult,
} from './v-query-presenter.typings';
import {
XSimpleFabItem,
toSimpleFabItems,
} from '../typings/simple.fab.typings';
import {
defaultConnectSlideOption,
defaultDetailsSlideOption,
} from './../constants/slide.options.defaults';
import { MenuController } from '@ionic/angular';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XManagerService } from 'x-framework-services';
import { VPageComponent } from '../v-page/v-page.component';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { SharedService } from 'src/app/shared/services/shared.service';
import { XScrollingHoldInfo } from 'src/app/shared/services/x-scrolling-provider.service';
/**
* a Base Implementation of VQueryPresenter Page ...
*/
@Component({
template: '',
})
export abstract class VQueryPresenterBasePage
extends VPageComponent
implements IVQueryPresenterContainer
{
//
//#region Readonly Fields ...
//
readonly PaginatorPositions = Object.assign(
VQueryPresenterPaginatorPosition,
{}
);
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly FormControlTypes = Object.assign(XFormControlType, {});
readonly VQueryPresenterActions = Object.assign(VQueryPresenterAction, {});
readonly FormControlAppearances = Object.assign(XFormControlAppearance, {});
//#endregion
//
//#region Page Props ...
infinityScrollThreshold: XStandardType<string> = '10px';
//#endregion
//
//#region Query Presenter Props ...
tabsCssClass: string[] = this.queryPresenterPropertyProvider.tabsCssClass;
tabsSelectedIndex: number =
this.queryPresenterPropertyProvider.tabsSelectedIndex;
tabsShowHeader: boolean = this.queryPresenterPropertyProvider.tabsShowHeader;
tabsDisablePagination: boolean =
this.queryPresenterPropertyProvider.tabsDisablePagination;
tabsAlignTabs: string = this.queryPresenterPropertyProvider.tabsAlignTabs;
tabsDynamicHeight: boolean =
this.queryPresenterPropertyProvider.tabsDynamicHeight;
tabsAnimationDuration: string =
this.queryPresenterPropertyProvider.tabsAnimationDuration;
tabsDisableRipple: boolean =
this.queryPresenterPropertyProvider.tabsDisableRipple;
tabsHeaderPosition: string =
this.queryPresenterPropertyProvider.tabsHeaderPosition;
tabsLoopTabs: boolean = this.queryPresenterPropertyProvider.tabsLoopTabs;
tabsSwipable: boolean = this.queryPresenterPropertyProvider.tabsSwipable;
tabsLock: boolean = this.queryPresenterPropertyProvider.tabsLock;
tabsColor: string = this.queryPresenterPropertyProvider.tabsColor;
tabsHeaderColor: string = this.queryPresenterPropertyProvider.tabsHeaderColor;
tabsActiveTabColor: string =
this.queryPresenterPropertyProvider.tabsActiveTabColor;
tabsTabIndicatorColor: string =
this.queryPresenterPropertyProvider.tabsTabIndicatorColor;
tabsTabPaginatorColor: string =
this.queryPresenterPropertyProvider.tabsTabPaginatorColor;
showPaginator: boolean = this.queryPresenterPropertyProvider.showPaginator;
paginatorPosition: string =
this.queryPresenterPropertyProvider.paginatorPosition;
paginatorColor: string = this.queryPresenterPropertyProvider.paginatorColor;
listTabTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.listTabTitle;
showItemsAsList: boolean =
this.queryPresenterPropertyProvider.showItemsAsList;
modelIdentifier: string = this.queryPresenterPropertyProvider.modelIdentifier;
wrapListTabWithCard: boolean =
this.queryPresenterPropertyProvider.wrapListTabWithCard;
listTabCardIsInPage: boolean =
this.queryPresenterPropertyProvider.listTabCardIsInPage;
listTabCardTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.listTabCardTitle;
listTabCardSubTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.listTabCardSubTitle;
listTabCardColor: string =
this.queryPresenterPropertyProvider.listTabCardColor;
showEmpty: boolean = this.queryPresenterPropertyProvider.showEmpty;
emptyCssClass: string[] = this.queryPresenterPropertyProvider.emptyCssClass;
emptyColor: string = this.queryPresenterPropertyProvider.emptyColor;
emptyForegroundColor: boolean =
this.queryPresenterPropertyProvider.emptyForegroundColor;
emptyMessage: XResourceIdentifier =
this.queryPresenterPropertyProvider.emptyMessage;
emptyMessageColor: string =
this.queryPresenterPropertyProvider.emptyMessageColor;
emptyForegroundMessageColor: boolean =
this.queryPresenterPropertyProvider.emptyForegroundMessageColor;
emptyHasRefresh: boolean =
this.queryPresenterPropertyProvider.emptyHasRefresh;
emptyRefreshButtonColor: string =
this.queryPresenterPropertyProvider.emptyRefreshButtonColor;
emptyForegroundRefreshButtonColor: boolean =
this.queryPresenterPropertyProvider.emptyForegroundRefreshButtonColor;
emptyRefreshIcon: XIconIdentifier =
this.queryPresenterPropertyProvider.emptyRefreshIcon;
detailsTabTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.detailsTabTitle;
addTabTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.addTabTitle;
updateTabTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.updateTabTitle;
showSearchBar: boolean = this.queryPresenterPropertyProvider.showSearchBar;
searchBarValue: string = this.queryPresenterPropertyProvider.searchBarValue;
searchBarDebounce: number =
this.queryPresenterPropertyProvider.searchBarDebounce;
searchBarSpellcheck: boolean =
this.queryPresenterPropertyProvider.searchBarSpellcheck;
searchBarCssClass: Array<string> =
this.queryPresenterPropertyProvider.searchBarCssClass;
searchBarColor: XColorIdentifier =
this.queryPresenterPropertyProvider.searchBarColor;
searchBarForegroundColor: boolean =
this.queryPresenterPropertyProvider.searchBarForegroundColor;
searchBarTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.searchBarTitle;
searchBarType: XSearchBarTypeIdentifier =
this.queryPresenterPropertyProvider.searchBarType;
searchBarClearButtonIcon: XIconIdentifier =
this.queryPresenterPropertyProvider.searchBarClearButtonIcon;
searchBarCancelButtonIcon: XIconIdentifier =
this.queryPresenterPropertyProvider.searchBarCancelButtonIcon;
searchBarSearchButtonIcon: XIconIdentifier =
this.queryPresenterPropertyProvider.searchBarSearchButtonIcon;
searchBarSearchInputColor: XColorIdentifier =
this.queryPresenterPropertyProvider.searchBarSearchInputColor;
searchBarForegroundSearchInputColor: boolean =
this.queryPresenterPropertyProvider.searchBarForegroundSearchInputColor;
searchBarKeyboardType: XSearchBarTypeIdentifier =
this.queryPresenterPropertyProvider.searchBarKeyboardType;
searchBarCancelButtonTitle: XResourceIdentifier =
this.queryPresenterPropertyProvider.searchBarCancelButtonTitle;
searchBarClearButtonIconColor: XColorIdentifier =
this.queryPresenterPropertyProvider.searchBarClearButtonIconColor;
searchBarCancelButtonIconColor: XColorIdentifier =
this.queryPresenterPropertyProvider.searchBarCancelButtonIconColor;
searchBarSearchButtonIconColor: XColorIdentifier =
this.queryPresenterPropertyProvider.searchBarSearchButtonIconColor;
searchBarEnterKeyHint: XSearchBarEnterKeyHintIdentifier =
this.queryPresenterPropertyProvider.searchBarEnterKeyHint;
searchBarShowClearButton: XSearchBarShowButtonIdentifier =
this.queryPresenterPropertyProvider.searchBarShowClearButton;
searchBarShowCancelButton: XSearchBarShowButtonIdentifier =
this.queryPresenterPropertyProvider.searchBarShowCancelButton;
//
//#region Abstracts ...
/**
* An Action Provider for Handle Connection Between Page and Component ...
*/
abstract queryPresenterActionProvider: Subject<VQueryPresenterActionModel>;
/**
* if showItemsAsList false, use this template for Present Query items ...
*/
abstract itemTemplate: TemplateRef<any>;
/**
* if showItemsAsList true, use this template for Present Query items in List ...
*/
abstract listItemTemplate: TemplateRef<any>;
/**
* Specified Template for Adding Item ...
*/
abstract addItemTemplate: TemplateRef<any>;
/**
* Specified Template for Update Item ...
*/
abstract updateItemTemplate: TemplateRef<any>;
/**
* Specified Template for Details Item ...
*/
abstract detailsItemTemplate: TemplateRef<any>;
/**
* Provide Query ...
*/
abstract query: XQueryDto;
/**
* holding dto's based query result ...
*/
abstract dtoQueryResult: XQueryResultDto<any>;
//#endregion
//#endregion
//
activeTab: number;
prevTab: number;
/**
* Override Back Handling Mechanism for Toolbar ...
*/
toolbarBackHandler?: () => XStandardType<void> = async () => {
//
// Check Params ...
const hasIdentifierParam = this.managerService.isContainQueryParam(
this.modelIdentifier
);
if (!this.lock && hasIdentifierParam) {
//
if (this.activeTab !== VQueryPresenterTabs.List) {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.List
);
await this.managerService.navigate([], {
queryParams: {
id: null,
a: null,
},
queryParamsHandling: 'merge',
});
return;
}
}
//
switch (this.activeTab) {
//
// Add ...
case VQueryPresenterTabs.Add:
//
// Go Back to List ...
await this.doLockBreakAction(() => {
//
// Send Change Tab ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List
);
});
break;
//
// Update ...
case VQueryPresenterTabs.Update:
//
// Go Back to List ...
await this.doLockBreakAction(() => {
//
// Send Change Tab ...
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
this.prevTab || VQueryPresenterTabs.List
);
});
break;
//
// Details ...
case VQueryPresenterTabs.Details:
//
// Send Change Tab ...
let tab = this.prevTab;
if (this.prevTab === VQueryPresenterTabs.Update) {
tab = VQueryPresenterTabs.List;
}
//
this.sendQueryPresenterAction(VQueryPresenterAction.ChangeTab, tab);
break;
}
};
//
//#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,
protected queryPresenterPropertyProvider: VQueryPresenterContainer
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
menuController,
managerService,
changeDetector,
popoverService,
propertyProvider
);
}
//#endregion
//
//#region LifeCycles ...
onInit(): void {
super.onInit();
}
async onChange(changeKeys: string[]): Promise<void> {
await super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
if (applyChanges) {
this.detectChanges();
}
}
async afterViewInit(): Promise<void> {
await super.afterViewInit();
//
this.handleRegisterListeners();
}
onDestroy(): void {
super.onDestroy();
//
this.removeScrollingInfo();
}
async onLoading(state: boolean): Promise<void> {
super.onLoading(state);
//
await this.handleStateChanged(state);
}
async onUiDisabled(state: boolean): Promise<void> {
super.onUiDisabled(state);
//
await this.handleStateChanged(state);
}
//#endregion
//
//#region Predefine Configurations ...
configureAsExplorerPage() {
//
this.handleParams = true;
this.showSearchBar = false;
this.showPaginator = false;
this.tabsShowHeader = false;
this.showItemsAsList = false;
this.showPageRefresher = true;
this.listTabCardIsInPage = false;
this.wrapListTabWithCard = false;
this.tabsDisablePagination = true;
this.showPageInfinityScroller = true;
}
configureAsManagementPage() {
//
this.handleParams = false;
this.showSearchBar = true;
this.showPaginator = true;
this.tabsShowHeader = false;
this.showItemsAsList = true;
this.showPageRefresher = false;
this.listTabCardIsInPage = true;
this.wrapListTabWithCard = true;
this.tabsDisablePagination = true;
this.showPageInfinityScroller = false;
}
//#endregion
//
//#region Params Handling ...
/**
* a Flag for Enable/Disable Params Processing ...
*/
private handleParams = false;
protected hasParams = false;
/**
* Parse Params and Request to Do it, if detected ...
*
* @returns
*/
async parseParams(): Promise<void> {
super.parseParams();
//
if (!this.handleParams) {
//
await this.managerService.navigate([], {
queryParams: {
id: null,
a: null,
},
queryParamsHandling: 'merge',
});
return;
}
//
// Handle Parse Param ...
const hasIdentifierParam = this.managerService.isContainQueryParam(
this.modelIdentifier
);
//
if (hasIdentifierParam) {
//
const identifier = this.managerService.getQueryParamValOfType<string>(
this.modelIdentifier
);
//
let action = 'd';
if (this.managerService.isContainQueryParam('a')) {
action = this.managerService.getQueryParamValOfType<string>('a');
}
//
if (!isNullOrEmptyString(action)) {
//
let actionId =
action === 'd'
? defaultDetailsSlideOption.id
: action === 'c'
? defaultConnectSlideOption.id
: undefined;
//
if (
!isNullOrEmptyString(actionId) &&
!isNullOrEmptyString(identifier)
) {
//
this.hasParams = true;
await this.handleParamAction(identifier, actionId);
}
}
}
}
//#endregion
//
//#region Abstraction ...
/**
* Run Provided Query and Retrieve Query Result ...
*
* @param query
*/
abstract handleGetQueryResult(
query: XQueryDto
): Promise<XQueryResultDto<any>>;
/**
* Retrieve Provided List Items and Validate them and also add Required Options
* then return validated list ...
*
* @param items
*/
abstract validateAndPrepareListItems(
items: Array<XListItem<any>>
): Promise<Array<XListItem<any>>>;
/**
* Handle Request for Non as List Items Presenting ...
*
* @param current
* @param recieved
*/
abstract requestFillingItems(
exists: Array<any>,
recieved: Array<any>
): Promise<VQueryFilledItemsProcessingResult<any>>;
/**
* When Non as List Items Filled, this method call for Change Detection ...
*
* @param ids
*/
async handleRefreshViews(ids: Array<string>) {}
/**
* Handle all Provided actions for Specified Model ...
*
* @param info
*/
abstract doItemActions(info: { id: string; data: any }): Promise<void>;
/**
* a Flag which Specified has Query Result Error ...
*/
abstract hasQueryResultError: boolean;
/**
* Handle Custo Error States ...
*
* @param error
*/
abstract handleCustomError(
error?: VQueryPresenterActionIdentifier
): Promise<void>;
/**
* if a Params Recieved, Handle it ...
*
* @param identifier
* @param acttion
*/
async handleParamAction(identifier: string, action: string) {}
/**
* State Management after Tab Open ...
*
* @param tab
*/
async handleStateAfterTabOpen(tab: number) {
//
this.prevTab = this.activeTab;
this.activeTab = tab;
this.closeActions();
//
this.toolbarHasBack = false;
this.toolbarShowBack = false;
let actions: XSimpleFabItem = undefined;
//
const isAdmin = await this.getValueAsync(this.sharedService.isAdmin$);
const isAgent = await this.getValueAsync(this.sharedService.isAgent$);
const isAdminAgent = isAdmin || isAgent;
//
switch (tab) {
//
// List ...
case VQueryPresenterTabs.List:
//
actions = {
...defaultOptionsFabItem,
childs: [
//
// Add ...
{
...defaultAddFabButton,
handler: async () => {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ChangeTab,
VQueryPresenterTabs.Add
);
//
this.closeActions();
},
disabled: this.loading || this.uiDisabled || !isAdminAgent,
},
//
// Refresh ...
{
...defaultRefreshFabButton,
handler: async () => {
//
this.sendQueryPresenterAction(VQueryPresenterAction.ResetQuery);
//
this.closeActions();
},
disabled: this.loading || this.uiDisabled,
},
],
};
//
setTimeout(() => {
this.infinityScrollDisabled = false;
}, 1000);
await this.handleScrollingState(500);
break;
//
// Add ...
case VQueryPresenterTabs.Add:
//
this.toolbarHasBack = true;
this.toolbarShowBack = true;
break;
//
// Details ...
case VQueryPresenterTabs.Details:
//
this.toolbarHasBack = true;
this.toolbarShowBack = true;
//
actions = {
...defaultOptionsFabItem,
childs: [
//
// Remove ...
{
...defaultRemoveFabButton,
index: 0,
side: XFabButtonSide.START,
handler: async () => {
//
await this.doItemActions({
id: defaultRemoveFabButton.id,
data: undefined,
});
//
this.closeActions();
},
disabled: this.loading || this.uiDisabled,
},
//
// Edit ...
{
...defaultEditFabButton,
index: 1,
handler: async () => {
//
await this.doItemActions({
id: defaultEditFabButton.id,
data: undefined,
});
//
this.closeActions();
},
disabled: this.loading || this.uiDisabled,
},
],
};
break;
//
// Update ...
case VQueryPresenterTabs.Update:
//
this.toolbarHasBack = true;
this.toolbarShowBack = true;
break;
}
//
if (!isNullOrUndefined(actions)) {
//
this.actions = {
...actions,
};
} else {
this.actions = undefined;
}
//
this.detectChanges();
}
/**
* State Management before Tab Changed
*
* @param from
* @param to
*/
async handleStateBeforeTabOpen(from: number, to: number) {
//
if (from === VQueryPresenterTabs.List) {
this.infinityScrollDisabled = true;
}
}
//#endregion
//
//#region Private ...
private async handleStateChanged(state: boolean) {
//
// Handle Fab Actions State Management ...
if (!isNullOrUndefined(this.actions)) {
//
let actions: XSimpleFabItem = await toSimpleFabItems(this.actions);
//
this.actions = {
...actions,
disabled: actions.disabled || this.loading || this.uiDisabled,
childs: [
...actions.childs.map((i) => {
return {
...i,
disabled: i.disabled || this.loading || this.uiDisabled,
};
}),
],
};
}
//
const prevTab = this.prevTab;
await this.handleStateAfterTabOpen(this.activeTab);
this.prevTab = prevTab;
}
//#endregion
//
//#region Scroller Handler ...
/**
* Handling Scroll Event ...
*
* @param event
*/
async onScrolledEvent(event: XBehaviourEvent): Promise<void> {
await super.onScrolledEvent(event);
//
if (
event.type === 'scrolling' &&
this.activeTab === VQueryPresenterTabs.List
) {
//
const identifier = this.constructor.name;
const yOffset = event.event.detail.scrollTop as number;
const xOffset = event.event.detail.scrollLeft as number;
//
if (yOffset > 0) {
//
const infoModel: XScrollingHoldInfo = {
identifier,
yOffset,
xOffset,
};
//
this.sharedService.scrollingInfoProvider.set(infoModel);
}
}
}
/**
* Handle Scrolling to last Saved State ...
*/
async handleScrollingState(delay?: number) {
//
const identifier = this.constructor.name;
const infoModel = this.sharedService.scrollingInfoProvider.get(identifier);
//
// Validate ...
if (
!isNullOrUndefined(infoModel) &&
infoModel.yOffset > 0 &&
this.activeTab === VQueryPresenterTabs.List
) {
//
let identifier: string = undefined;
if (
!isNullOrUndefined(this.dtoQueryResult) &&
this.hasChild(this.dtoQueryResult.items)
) {
identifier = (
this.dtoQueryResult.items[this.dtoQueryResult.items.length - 1] as any
)[this.modelIdentifier];
}
//
if (isNullOrEmptyString(identifier)) {
return;
}
//
await this.waitForElement(
`[${this.modelIdentifier}="${identifier}"]`
).then(async (el) => {
//
this.infinityScrollDisabled = true;
//
setTimeout(async () => {
//
await this.scrollByPoint(infoModel.xOffset, infoModel.yOffset, delay);
setTimeout(() => {
this.infinityScrollDisabled = false;
}, 1000);
}, delay);
});
}
}
/**
* Remove Scrolling Info ...
*/
removeScrollingInfo() {
//
const identifier = this.constructor.name;
if (this.sharedService.scrollingInfoProvider.has(identifier)) {
this.sharedService.scrollingInfoProvider.remove(identifier);
}
}
//#endregion
//
//#region Action Provider ...
private handleRegisterListeners() {
//
// Register Query Presenter Action Provider ...
if (!isNullOrUndefined(this.queryPresenterActionProvider)) {
//
this.applyTakeUntilWrapper(this.queryPresenterActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVQueryPresenterAction(event.action)
) {
return;
}
//
await this.handleQueryPresenterAction(event.action, event.payload);
}
);
}
//
// Register Load More Event Listener ...
this.applyTakeUntilWrapper(this.loadMore.asObservable()).subscribe(
(res) => {
//
// Setting Timout for Cancel Loading ...
setTimeout(async () => {
await this.cancelLoading(res.event);
}, 500);
//
this.sendQueryPresenterAction(
VQueryPresenterAction.LoadMore,
this.hasQueryResultError
);
}
);
//
// Register Page Refresh Event Listener ...
this.applyTakeUntilWrapper(this.refreshPage.asObservable()).subscribe(
(res) => {
//
// Setting Timout for Cancel Loading ...
setTimeout(async () => {
await this.cancelLoading(res.event);
}, 500);
//
this.sendQueryPresenterAction(VQueryPresenterAction.ResetQuery);
}
);
}
protected sendQueryPresenterAction(
action: VQueryPresenterAction,
payload?: any
) {
//
if (isNullOrUndefined(this.queryPresenterActionProvider)) {
return;
}
//
this.queryPresenterActionProvider.next({ action, payload });
}
protected async handleQueryPresenterAction(
action: VQueryPresenterActionIdentifier,
payload?: any
) {
//
// TabOpenedState ...
if (
action === VQueryPresenterAction.TabOpenedState &&
!isNullOrUndefined(payload)
) {
await this.handleStateAfterTabOpen(payload);
}
//
// TabRequestState ...
if (
action === VQueryPresenterAction.TabRequestState &&
!isNullOrUndefined(payload)
) {
await this.handleStateBeforeTabOpen(payload.from, payload.to);
}
//
// RequestQueryResult ...
if (action === VQueryPresenterAction.RequestQueryResult) {
//
try {
//
// Normalize Payload ...
if (isNullOrUndefined(payload)) {
//
payload = {
...this.query,
};
}
//
this.activeTab = VQueryPresenterTabs.List;
const result = await this.handleGetQueryResult(payload);
//
// Validate Result ...
if (!isNullOrUndefined(result)) {
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ResponseQueryResult,
{
query: payload,
result,
}
);
}
} catch {
await this.handleCustomError(action);
}
}
//
// ValidateListItems ...
if (action === VQueryPresenterAction.ValidateListItems) {
//
const items = await this.validateAndPrepareListItems(payload);
this.sendQueryPresenterAction(
VQueryPresenterAction.ResponseListItems,
items
);
}
//
// Request Item Action ...
if (
action === VQueryPresenterAction.RequestItemAction &&
!isNullOrUndefined(payload)
) {
await this.doItemActions(payload);
}
//
// Request Filling Items ...
if (
action === VQueryPresenterAction.RequestFillingItems &&
!isNullOrUndefined(payload)
) {
//
const model = await this.requestFillingItems(
[...payload.exists],
[...payload.recieved]
);
//
this.sendQueryPresenterAction(
VQueryPresenterAction.ResponseFillingItems,
model
);
this.infinityScrollDisabled = true;
}
//
// Refresh Views ...
if (
action === VQueryPresenterAction.RefreshViews &&
!isNullOrUndefined(payload)
) {
await this.handleRefreshViews(payload);
}
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-query-presenter\v-query-presenter.component.html
<!-- -->
<!-- Main Content -->
<!-- -->
<x-tabs
[lock]="tabsLock"
[color]="tabsColor"
[loading]="loading"
[cssClass]="tabsCssClass"
[swipable]="tabsSwipable"
[loopTabs]="tabsLoopTabs"
[alignTabs]="tabsAlignTabs"
[showHeader]="tabsShowHeader"
[headerColor]="tabsHeaderColor"
[dynamicHeight]="tabsDynamicHeight"
[disableRipple]="tabsDisableRipple"
[selectedIndex]="tabsSelectedIndex"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="tabsActionProvider"
[headerPosition]="tabsHeaderPosition"
[activeTabColor]="tabsActiveTabColor"
[disablePagination]="tabsDisablePagination"
[animationDuration]="tabsAnimationDuration"
[tabIndicatorColor]="tabsTabIndicatorColor"
[tabPaginatorColor]="tabsTabPaginatorColor"
(animationDone)="tabsAnimationDone.emit($event)"
(selectedTabChange)="tabsSelectedTabChange.emit($event)"
(selectedIndexChange)="tabsSelectedIndexChange.emit($event)"
>
<!-- List -->
<x-tab>
<!-- Tab Title -->
<div x-tab-label>{{ resourceProvider(listTabTitle) }}</div>
<!-- Tab Content -->
<div
class="x-tab-content-wrapper"
*ngIf="getValue(showListTabContent) | async"
>
<ng-container *ngTemplateOutlet="listTabRef"></ng-container>
</div>
</x-tab>
<!-- Details -->
<x-tab>
<!-- Tab Title -->
<div x-tab-label>{{ resourceProvider(detailsTabTitle) }}</div>
<!-- Tab Content -->
<div
class="x-tab-content-wrapper"
*ngIf="getValue(showDetailsTabContent) | async"
>
<ng-container *ngTemplateOutlet="detailsTabRef"></ng-container>
</div>
</x-tab>
<!-- Add -->
<x-tab>
<!-- Tab Title -->
<div x-tab-label>{{ resourceProvider(addTabTitle) }}</div>
<!-- Tab Content -->
<div
class="x-tab-content-wrapper"
*ngIf="getValue(showAddTabContent) | async"
>
<ng-container *ngTemplateOutlet="addTabRef"></ng-container>
</div>
</x-tab>
<!-- Update -->
<x-tab>
<!-- Tab Title -->
<div x-tab-label>{{ resourceProvider(updateTabTitle) }}</div>
<!-- Tab Content -->
<div
class="x-tab-content-wrapper"
*ngIf="getValue(showUpdateTabContent) | async"
>
<ng-container *ngTemplateOutlet="updateTabRef"></ng-container>
</div>
</x-tab>
</x-tabs>
<!-- -->
<!-- Templates -->
<!-- -->
<!-- Nothing Template -->
<ng-template #nothingRef></ng-template>
<!-- -->
<!-- List Tab Template -->
<!-- -->
<ng-template #listTabRef>
<!-- Tab Content -->
<div
class="x-tab-content x-list-tab-content"
*ngIf="getValue(showListTabContent) | async"
>
<!-- If List Tab Wrapped With Card -->
<ng-container *ngIf="wrapListTabWithCard">
<x-card
class="v-card"
[loading]="loading"
[title]="listTabCardTitle"
[color]="listTabCardColor"
[class.v-no-margin-card]="true"
[isInPage]="listTabCardIsInPage"
[subTitle]="listTabCardSubTitle"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="listTabCardActionProvider"
[showHeader]="
(notNullOrUndefinedValue(listTabCardTitle) | async) ||
(notNullOrUndefinedValue(listTabCardSubTitle) | async)
"
>
<ng-container *ngTemplateOutlet="listTabContentRef"></ng-container>
</x-card>
</ng-container>
<!-- If List Tab Doesn'tWrapped With Card -->
<ng-container *ngIf="!wrapListTabWithCard">
<ng-container *ngTemplateOutlet="listTabContentRef"></ng-container>
</ng-container>
</div>
</ng-template>
<!-- -->
<!-- List Tab Content Template -->
<!-- -->
<ng-template #listTabContentRef>
<!-- Container -->
<div class="x-list-tab-container">
<!-- Search -->
<x-search-bar
class="v-search"
[loading]="loading"
[type]="searchBarType"
[value]="searchBarValue"
[color]="searchBarColor"
[title]="searchBarTitle"
*ngIf="canShowSearchBar()"
[debounce]="searchBarDebounce"
[cssClass]="searchBarCssClass"
[spellcheck]="searchBarSpellcheck"
[uiDisabled]="uiDisabled || loading"
[keyboardType]="searchBarKeyboardType"
[enterKeyHint]="searchBarEnterKeyHint"
[actionProvider]="searchBarActionProvider"
[foregroundColor]="searchBarForegroundColor"
[clearButtonIcon]="searchBarClearButtonIcon"
[showClearButton]="searchBarShowClearButton"
[showCancelButton]="searchBarShowCancelButton"
[cancelButtonIcon]="searchBarCancelButtonIcon"
[searchButtonIcon]="searchBarSearchButtonIcon"
[searchInputColor]="searchBarSearchInputColor"
(queryChange)="handleSearchQuryChanged($event)"
[cancelButtonTitle]="searchBarCancelButtonTitle"
[clearButtonIconColor]="searchBarClearButtonIconColor"
[cancelButtonIconColor]="searchBarCancelButtonIconColor"
[searchButtonIconColor]="searchBarSearchButtonIconColor"
[foregroundSearchInputColor]="searchBarForegroundSearchInputColor"
></x-search-bar>
<!-- Paginator -->
<ng-container
*ngIf="
showPaginator &&
(paginatorPosition === PaginatorPositions.Above ||
paginatorPosition === PaginatorPositions.Both)
"
>
<ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
</ng-container>
<!-- Present Items -->
<ng-container *ngTemplateOutlet="itemsPresenterRef"></ng-container>
<!-- Paginator -->
<ng-container
*ngIf="
showPaginator &&
(paginatorPosition === PaginatorPositions.Below ||
paginatorPosition === PaginatorPositions.Both)
"
>
<ng-container *ngTemplateOutlet="paginatorRef"></ng-container>
</ng-container>
</div>
</ng-template>
<!-- Paginator Template -->
<ng-template #paginatorRef>
<ng-container
*ngIf="
showPaginator &&
(notNullOrUndefinedValue(queryResult) | async) &&
(notNullOrUndefinedValue(queryResult.items) | async) &&
(hasChildValue(queryResult.items) | async)
"
>
<v-paginator
[loading]="loading"
[color]="paginatorColor"
[current]="paginatorCurrent"
[totalPages]="paginatorTotalPages"
[totalItems]="paginatorTotalItems"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="paginatorActionProvider"
[pageInfoProvider]="paginatorPInfoProvider"
[nextButtonDescriptor]="nextButtonDescriptor"
[prevButtonDescriptor]="prevButtonDescriptor"
[lastButtonDescriptor]="lastButtonDescriptor"
[pageSizeProvider]="paginatorPageSizeProvider"
[firstButtonDescriptor]="firstButtonDescriptor"
[pageSizesProvider]="paginatorPageSizesProvider"
></v-paginator>
</ng-container>
</ng-template>
<!-- Items Presenter Template -->
<ng-template #itemsPresenterRef>
<!-- Show Items As List -->
<ng-container
*ngIf="
showItemsAsList && (notNullOrUndefinedValue(listItemTemplate) | async)
"
>
<x-list
class="v-card"
[loading]="loading"
[emptyColor]="emptyColor"
[emptyMessage]="emptyMessage"
(emptyRefresh)="resetQuery()"
[emptyCssClass]="emptyCssClass"
[itemTemplate]="listItemTemplate"
[emptyHasRefresh]="emptyHasRefresh"
[uiDisabled]="uiDisabled || loading"
[items]="getValue(listItems) | async"
[emptyRefreshIcon]="emptyRefreshIcon"
[actionProvider]="listActionProvider"
[emptyMessageColor]="emptyMessageColor"
[showOptionsAsMenu]="isMobileUi$ | async"
[showOptionsAsSlide]="isMobileUi$ | async"
[emptyForegroundColor]="emptyForegroundColor"
[showEmpty]="
showEmpty &&
!loading &&
(notNullOrUndefinedValue(query) | async) &&
(notNullOrUndefinedValue(queryResult) | async) &&
!(hasChildValue(listItems) | async)
"
[emptyRefreshButtonColor]="emptyRefreshButtonColor"
(optionSelected)="handleListItemOptionsSelected($event)"
[emptyForegroundMessageColor]="emptyForegroundMessageColor"
[emptyForegroundRefreshButtonColor]="emptyForegroundRefreshButtonColor"
>
</x-list>
</ng-container>
<!-- Show Items As View -->
<ng-container
*ngIf="!showItemsAsList && (notNullOrUndefinedValue(itemTemplate) | async)"
>
<!-- Present Items if Exists -->
<ng-container
*ngIf="
(notNullOrUndefinedValue(query) | async) &&
(notNullOrUndefinedValue(items) | async) &&
(hasChildValue(items) | async)
"
>
<ng-container *ngFor="let item of items; trackBy: itemsTrackBy">
<!-- Presenting Item -->
<ng-container *ngTemplateOutlet="itemTemplate; context: { item: item }">
</ng-container>
</ng-container>
</ng-container>
<!-- Present Empty if nothing Found -->
<ng-container
*ngIf="
!loading &&
(notNullOrUndefinedValue(query) | async) &&
(notNullOrUndefinedValue(queryResult) | async) &&
!(hasChildValue(queryResult.items) | async)
"
>
<x-empty
*ngIf="showEmpty"
[loading]="loading"
[color]="emptyColor"
(refresh)="resetQuery()"
[message]="emptyMessage"
[cssClass]="emptyCssClass"
[hasRefresh]="emptyHasRefresh"
[refreshIcon]="emptyRefreshIcon"
[messageColor]="emptyMessageColor"
[uiDisabled]="uiDisabled || loading"
[foregroundColor]="emptyForegroundColor"
[refreshButtonColor]="emptyRefreshButtonColor"
[foregroundMessageColor]="emptyForegroundMessageColor"
[foregroundRefreshButtonColor]="emptyForegroundRefreshButtonColor"
></x-empty>
</ng-container>
</ng-container>
</ng-template>
<!-- -->
<!-- Details Tab Template -->
<!-- -->
<ng-template #detailsTabRef>
<!-- Tab Content -->
<div
class="x-tab-content"
*ngIf="
(getValue(showDetailsTabContent) | async) &&
(notNullOrUndefinedValue(detailsItemTemplate) | async)
"
>
<ng-container *ngTemplateOutlet="detailsItemTemplate"></ng-container>
</div>
</ng-template>
<!-- -->
<!-- Add Tab Template -->
<!-- -->
<ng-template #addTabRef>
<!-- Tab Content -->
<div
class="x-tab-content"
*ngIf="
(getValue(showAddTabContent) | async) &&
(notNullOrUndefinedValue(addItemTemplate) | async)
"
>
<ng-container *ngTemplateOutlet="addItemTemplate"></ng-container>
</div>
</ng-template>
<!-- -->
<!-- Update Tab Template -->
<!-- -->
<ng-template #updateTabRef>
<!-- Tab Content -->
<div class="x-tab-content" *ngIf="getValue(showUpdateTabContent) | async">
<ng-container *ngTemplateOutlet="updateItemTemplate"></ng-container>
</div>
</ng-template>
<!-- -->
<!-- Items List Template -->
<!-- -->
<ng-template #itemsListRef>
<!-- Contentainer -->
<!-- *ngIf="getValue(showUpdateTabContent) | async" -->
<div class="x-items-list-wrapper">List ...</div>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-query-presenter\v-query-presenter.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-query-presenter\v-query-presenter.component.ts
import {
Input,
NgZone,
Inject,
Output,
Renderer2,
Component,
ElementRef,
TemplateRef,
EventEmitter,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
nameof,
getKeys,
isNumber,
toNumber,
isBoolean,
toBoolean,
XColorIdentifier,
XModalButtonRole,
isNullOrUndefined,
isNullOrEmptyString,
XPickerColumnOption,
XResourceIdentifier,
} from 'x-framework-core';
import { Subject } from 'rxjs';
import {
XCardAction,
XListAction,
XTabsAction,
XIconIdentifier,
XPopoverService,
XTabChangeEvent,
XCardActionModel,
XListActionModel,
XSearchBarAction,
XTabsActionModel,
XTabAlignIdentifier,
XListActionIdentifier,
XSearchBarActionModel,
XTabsActionIdentifier,
XSearchBarTypeIdentifier,
XSearchBarActionIdentifier,
XListItemOptionSelectedEvent,
XTabsHeaderPositionIdentifier,
XSearchBarShowButtonIdentifier,
XSearchBarEnterKeyHintIdentifier,
} from 'x-framework-components';
import {
VQueryPresenterTabs,
VQueryPresenterAction,
isVQueryPresenterAction,
VQueryPresenterContainer,
IVQueryPresenterContainer,
VQueryPresenterActionModel,
VQueryPresenterActionIdentifier,
VQueryPresenterPaginatorPosition,
VQueryFilledItemsProcessingResult,
VQueryPresenterPaginatorPositionIdentifier,
} from './v-query-presenter.typings';
import {
VPaginatorAction,
VPaginatorActionModel,
VPaginatorActionButton,
VPaginatorActionIdentifier,
} from '../v-paginator/v-paginator.typings';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { isValueInEnum } from '../tools/v-base.tools';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XManagerService } from 'x-framework-services';
import { VBaseComponent } from '../classes/v-base.component';
import { XQueryDto, XQueryResultDto } from 'x-framework-identity-sdk';
import { XSimpleListItem } from '../typings/simple.list-item.typings';
import { SharedService } from 'src/app/shared/services/shared.service';
/**
* a Component for Presenting items based on Query Model ...
*/
@Component({
selector: 'v-query-presenter',
templateUrl: 'v-query-presenter.component.html',
styleUrls: ['v-query-presenter.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VQueryPresenterComponent
extends VBaseComponent
implements IVQueryPresenterContainer
{
//
//#region Props ...
readonly PaginatorPositions = Object.assign(
VQueryPresenterPaginatorPosition,
{}
);
//
//#region Tabs Presenter Props ...
@Input()
tabsCssClass: Array<string> = this.propertyProvider.tabsCssClass;
@Input()
tabsSelectedIndex: number = this.propertyProvider.tabsSelectedIndex;
@Input()
tabsShowHeader: boolean = this.propertyProvider.tabsShowHeader;
@Input()
tabsDisablePagination: boolean = this.propertyProvider.tabsDisablePagination;
@Input()
tabsAlignTabs: XTabAlignIdentifier = this.propertyProvider.tabsAlignTabs;
@Input()
tabsDynamicHeight: boolean = this.propertyProvider.tabsDynamicHeight;
@Input()
tabsAnimationDuration: string = this.propertyProvider.tabsAnimationDuration;
@Input()
tabsDisableRipple: boolean = this.propertyProvider.tabsDisableRipple;
@Input()
tabsHeaderPosition: XTabsHeaderPositionIdentifier =
this.propertyProvider.tabsHeaderPosition;
@Input()
tabsLoopTabs: boolean = this.propertyProvider.tabsLoopTabs;
@Input()
tabsSwipable: boolean = this.propertyProvider.tabsSwipable;
@Input()
tabsLock: boolean = this.propertyProvider.tabsLock;
@Input()
tabsColor: XColorIdentifier = this.propertyProvider.tabsColor;
@Input()
tabsHeaderColor: XColorIdentifier = this.propertyProvider.tabsHeaderColor;
@Input()
tabsActiveTabColor: XColorIdentifier =
this.propertyProvider.tabsActiveTabColor;
@Input()
tabsTabIndicatorColor: XColorIdentifier =
this.propertyProvider.tabsTabIndicatorColor;
@Input()
tabsTabPaginatorColor: XColorIdentifier =
this.propertyProvider.tabsTabPaginatorColor;
//#endregion
//
//#region Non Inputs ...
//
// Action Providers ...
listActionProvider = new Subject<XListActionModel>();
tabsActionProvider = new Subject<XTabsActionModel>();
listTabCardActionProvider = new Subject<XCardActionModel>();
searchBarActionProvider = new Subject<XSearchBarActionModel>();
paginatorActionProvider = new Subject<VPaginatorActionModel>();
@Output()
tabsAnimationDone = new EventEmitter<void>();
@Output()
tabsSelectedIndexChange = new EventEmitter<number>();
@Output()
tabsSelectedTabChange = new EventEmitter<XTabChangeEvent>();
//#endregion
@Input()
actionProvider = new Subject<VQueryPresenterActionModel>();
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
public propertyProvider: VQueryPresenterContainer
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService
);
}
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleRegisterListeners();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = changeKeys.includes(
nameof<VQueryPresenterComponent>('query')
);
if (isChanged) {
this.resetQuery();
}
//
this.validateProperties();
}
async onLoading(state: boolean): Promise<void> {
super.onLoading(state);
//
await this.handleStateChanges(state);
}
async onUiDisabled(state: boolean): Promise<void> {
super.onUiDisabled(state);
//
await this.handleStateChanges(state);
}
//#endregion
//
//#region Properties ...
/**
* Extract Current Properties ...
*
* @returns
*/
getProperties() {
//
let keys = getKeys(this.propertyProvider);
let result: Partial<IVQueryPresenterContainer> = {};
//
keys.forEach((k) => {
(result as any)[k] = (this as any)[k];
});
//
return result;
}
/**
* Set Properties ...
*
* @param props
*/
setProperties(props: Partial<IVQueryPresenterContainer>) {
//
let applyChanges = false;
let keys = Object.keys(props);
//
keys.forEach((k) => {
//
const value = (props as any)[k];
const currentValue = (this as any)[k];
const defaultValue = (this.propertyProvider as any)[k];
const selectedValue = !isNullOrUndefined(value) ? value : defaultValue;
//
(this as any)[k] = selectedValue;
//
if (currentValue !== selectedValue && !applyChanges) {
applyChanges = true;
}
});
//
if (applyChanges) {
this.handleDetecteChanges();
}
}
/**
* Validate Properties ...
*/
validateProperties() {
//
var props = this.getProperties();
this.setProperties(props);
}
//#endregion
//
//#region Separate ...
//
//#region List ...
@Input()
listTabTitle: XResourceIdentifier = this.propertyProvider.listTabTitle;
/**
* When Items Prepared, Show them as a List ...
*/
@Input()
showItemsAsList: boolean = this.propertyProvider.showItemsAsList;
/**
* When Provides items as Page and Non as List, this used to track items ...
*/
@Input()
modelIdentifier: string = this.propertyProvider.modelIdentifier;
/**
* if set showItemsAsList equals to false ...
* all retrieved items shown as standalone item ...
* this property prepared template for each item ...
* NOTE: let-item="item" attached item to template ...
*/
@Input()
itemTemplate: TemplateRef<any> = this.propertyProvider.itemTemplate;
/**
* if set showItemsAsList equals to true ...
* all retrieved items shown as a list ...
* this property prepared template for each list item ...
* NOTE: let-item="item" attached item to template ...
*/
@Input()
listItemTemplate: TemplateRef<any> = this.propertyProvider.listItemTemplate;
@Input()
wrapListTabWithCard = this.propertyProvider.wrapListTabWithCard;
@Input()
listTabCardIsInPage = this.propertyProvider.listTabCardIsInPage;
@Input()
listTabCardTitle: XResourceIdentifier =
this.propertyProvider.listTabCardTitle;
@Input()
listTabCardSubTitle: XResourceIdentifier =
this.propertyProvider.listTabCardSubTitle;
@Input()
listTabCardColor: XColorIdentifier = this.propertyProvider.listTabCardColor;
//
//#region Empty ...
@Input()
showEmpty: boolean = this.propertyProvider.showEmpty;
@Input()
emptyCssClass: Array<string> = this.propertyProvider.emptyCssClass;
@Input()
emptyColor: XColorIdentifier = this.propertyProvider.emptyColor;
@Input()
emptyForegroundColor: boolean = this.propertyProvider.emptyForegroundColor;
@Input()
emptyMessage: XResourceIdentifier = this.propertyProvider.emptyMessage;
@Input()
emptyMessageColor: XColorIdentifier = this.propertyProvider.emptyMessageColor;
@Input()
emptyForegroundMessageColor: boolean =
this.propertyProvider.emptyForegroundMessageColor;
@Input()
emptyHasRefresh: boolean = this.propertyProvider.emptyHasRefresh;
@Input()
emptyRefreshButtonColor: XColorIdentifier =
this.propertyProvider.emptyRefreshButtonColor;
@Input()
emptyForegroundRefreshButtonColor: boolean =
this.propertyProvider.emptyForegroundRefreshButtonColor;
@Input()
emptyRefreshIcon: XIconIdentifier = this.propertyProvider.emptyRefreshIcon;
//#endregion
//
@Input()
query: XQueryDto = undefined;
//
items: Array<any> = [];
queryResult: XQueryResultDto = undefined;
listItems: Array<XSimpleListItem<any>> = [];
//
//#region Search Bar ...
@Input()
showSearchBar: boolean = this.propertyProvider.showSearchBar;
@Input()
searchBarValue: string = this.propertyProvider.searchBarValue;
@Input()
searchBarDebounce: number = this.propertyProvider.searchBarDebounce;
@Input()
searchBarSpellcheck: boolean = this.propertyProvider.searchBarSpellcheck;
@Input() searchBarCssClass: Array<string> =
this.propertyProvider.searchBarCssClass;
@Input()
searchBarColor: XColorIdentifier = this.propertyProvider.searchBarColor;
@Input()
searchBarForegroundColor: boolean =
this.propertyProvider.searchBarForegroundColor;
@Input()
searchBarTitle: XResourceIdentifier = this.propertyProvider.searchBarTitle;
@Input()
searchBarType: XSearchBarTypeIdentifier = this.propertyProvider.searchBarType;
@Input()
searchBarClearButtonIcon: XIconIdentifier =
this.propertyProvider.searchBarClearButtonIcon;
@Input()
searchBarCancelButtonIcon: XIconIdentifier =
this.propertyProvider.searchBarCancelButtonIcon;
@Input()
searchBarSearchButtonIcon: XIconIdentifier =
this.propertyProvider.searchBarSearchButtonIcon;
@Input()
searchBarSearchInputColor: XColorIdentifier =
this.propertyProvider.searchBarSearchInputColor;
@Input()
searchBarForegroundSearchInputColor: boolean =
this.propertyProvider.searchBarForegroundSearchInputColor;
@Input()
searchBarKeyboardType: XSearchBarTypeIdentifier =
this.propertyProvider.searchBarKeyboardType;
@Input()
searchBarCancelButtonTitle: XResourceIdentifier =
this.propertyProvider.searchBarCancelButtonTitle;
@Input()
searchBarClearButtonIconColor: XColorIdentifier =
this.propertyProvider.searchBarClearButtonIconColor;
@Input()
searchBarCancelButtonIconColor: XColorIdentifier =
this.propertyProvider.searchBarCancelButtonIconColor;
@Input()
searchBarSearchButtonIconColor: XColorIdentifier =
this.propertyProvider.searchBarSearchButtonIconColor;
@Input()
searchBarEnterKeyHint: XSearchBarEnterKeyHintIdentifier =
this.propertyProvider.searchBarEnterKeyHint;
@Input()
searchBarShowClearButton: XSearchBarShowButtonIdentifier =
this.propertyProvider.searchBarShowClearButton;
@Input()
searchBarShowCancelButton: XSearchBarShowButtonIdentifier =
this.propertyProvider.searchBarShowCancelButton;
//#endregion
//
//#region Paginator ...
@Input()
showPaginator: boolean = this.propertyProvider.showPaginator;
@Input()
paginatorPosition: VQueryPresenterPaginatorPositionIdentifier =
this.propertyProvider.paginatorPosition;
@Input()
paginatorColor: XColorIdentifier = this.propertyProvider.paginatorColor;
//#endregion
//
showListTabContent: boolean = false;
/**
* Non As List Item's Presenting Tracking ...
*/
itemsTrackBy(index: number, item: any) {
//
if (!isNullOrEmptyString(this.modelIdentifier)) {
return index;
} else {
return item[this.modelIdentifier];
}
}
/**
* Check Can Show Search Bar or not ...
* @returns
*/
canShowSearchBar() {
//
return (
this.showSearchBar &&
!isNullOrUndefined(this.query) &&
!isNullOrUndefined(this.queryResult)
);
}
//
paginatorPageSizeProvider = () => {
return this.queryResult &&
this.queryResult.pageSize &&
this.queryResult.pageSize > 0
? this.queryResult.pageSize
: undefined;
};
paginatorPageSizesProvider = () => {
return [...this.config.pageSizes];
};
paginatorPInfoProvider = (
currentPage: number,
pageSize: number,
totalPages: number,
totalItems: number
) => {
//
if (isNullOrUndefined(currentPage) || isNullOrUndefined(totalPages)) {
return '';
}
//
return this.applyLocale(`${currentPage}/${totalPages}`);
};
//
paginatorCurrent: number = undefined;
paginatorTotalPages: number = undefined;
paginatorTotalItems: number = undefined;
nextButtonDescriptor: VPaginatorActionButton = undefined;
prevButtonDescriptor: VPaginatorActionButton = undefined;
lastButtonDescriptor: VPaginatorActionButton = undefined;
firstButtonDescriptor: VPaginatorActionButton = undefined;
/**
* Handle Page Size Change ...
*
* @param current
* @param allowed
*/
handleChangePageSize = async (current: number, allowed: Array<number>) => {
//
if (!allowed.includes(current)) {
//
allowed.push(current);
//
allowed.sort((a, b) => (a > b ? 1 : a === b ? 0 : -1));
}
//
const options: XPickerColumnOption[] = allowed.map((ps) => {
//
const opt: XPickerColumnOption = {
text: this.applyLocale(ps.toString()),
value: ps,
disabled: ps === current,
};
//
return opt;
});
//
let selected = current;
const modal = await this.managerService.dialogService.presentPicker({
buttons: [
{
text: this.resourceProvider(this.ResourceIDs.accept),
role: XModalButtonRole.Selected,
handler: (value) => {
if (!value) {
return;
}
//
const pageSize = value.pageSize.value;
selected = pageSize;
},
},
{
text: this.resourceProvider(this.ResourceIDs.cancel),
role: XModalButtonRole.Cancel,
},
],
columns: [{ name: 'pageSize', options }],
});
//
await modal.onWillDismiss();
//
if (this.query && selected !== this.query.pageSize) {
//
this.query.pageSize = selected;
this.resetQuery();
}
//
return Promise.resolve(selected);
};
/**
* Apply Search Query and renew Items ...
*
* @param searchValue
* @returns
*/
handleSearchQuryChanged(searchValue: string) {
//
if (
searchValue === this.searchBarValue &&
(searchValue === this.query?.filter || '')
) {
return;
}
//
this.query.page = 1;
this.query.filter = searchValue;
this.sendAction(VQueryPresenterAction.RequestQueryResult, this.query);
}
/**
* Reset Page and Filter and Try to Refetch Items ...
*/
resetQuery() {
//
if (!isNullOrUndefined(this.query)) {
this.query.page = 1;
this.query.filter = '';
}
//
this.items = [];
//
this.sendAction(VQueryPresenterAction.RequestQueryResult, this.query);
}
/**
* Handle Request Next Page ...
*
* @param forceCurrent Specified Reload current if true ...
*/
async handleLoadMore(forceCurrent: boolean = false) {
//
if (
isNullOrUndefined(this.query) ||
isNullOrUndefined(this.queryResult) ||
(!forceCurrent &&
this.queryResult.page === this.queryResult.totalFilteredPages)
) {
return;
}
//
let page = this.query.page;
if (!forceCurrent) {
page = page + 1;
} else {
this.query.page = this.query.page - 1;
}
//
await this.handleLoadSpecifiedPage(page);
}
/**
* Handle Load Specified Page ...
*
* @param page
* @returns
*/
async handleLoadSpecifiedPage(page: number) {
//
// Validate ...
if (
!page ||
page <= 0 ||
isNullOrUndefined(this.query) ||
isNullOrUndefined(this.queryResult) ||
page === this.query.page ||
page > this.queryResult.totalFilteredPages
) {
return;
}
//
this.query.page = page;
this.sendAction(VQueryPresenterAction.RequestQueryResult, this.query);
}
/**
* What to Do when List Tab Opened ...
*/
async handleListTabOpenState() {
//
const requestQueryResult =
!this.showListTabContent && isNullOrUndefined(this.queryResult);
if (requestQueryResult) {
//
this.resetQuery();
return;
}
}
/**
* Handle Query Result Recieved ...
*/
async handleQueryResultRecieved(
query: XQueryDto,
result: XQueryResultDto<any>
) {
//
this.query = query;
//
// Validate ...
if (isNullOrUndefined(result)) {
return;
}
//
// Renew Query Result ...
this.queryResult = {
...result,
};
//
// Change List Tab Visibility ...
if (!this.isTabVisible(VQueryPresenterTabs.List)) {
this.changeTabVisibility(VQueryPresenterTabs.List, true);
}
//
this.preparePaginator();
//
//#region Preparing Items ...
//
// List Items ...
if (this.showItemsAsList) {
//
// Preparing List Items ...
const items = this.queryResult.items.map((i) => {
return {
data: i,
} as XSimpleListItem<any>;
});
//
// Send Request to Validate List Items and also Attach Actions ...
this.sendAction(VQueryPresenterAction.ValidateListItems, items);
}
//
// Non As List Items ...
else {
//
this.listItems = [];
//
if (this.searchBarValue !== query.filter) {
//
this.items = [];
this.detectChanges();
this.searchBarValue = query.filter;
}
//
// Request for Filling Items ...
this.sendAction(VQueryPresenterAction.RequestFillingItems, {
exists: [...this.items],
recieved: [...this.queryResult.items],
});
}
//#endregion
}
/**
* Preparing Paginator ...
*
* @returns
*/
preparePaginator() {
//
// Validate ...
if (isNullOrUndefined(this.query) || isNullOrUndefined(this.queryResult)) {
//
// Filling Paginator items ...
this.paginatorCurrent = undefined;
this.paginatorTotalItems = undefined;
this.paginatorTotalPages = undefined;
return;
}
//
// Check If Hasnt Child Means Remove all Childs ...
if (!this.hasChild(this.queryResult.items)) {
//
// here we Must Remove Data ...
this.paginatorCurrent = undefined;
this.paginatorTotalItems = undefined;
this.paginatorTotalPages = undefined;
return;
}
//
//#region Prepare Paginator ...
//
// Filling Paginator items ...
this.paginatorCurrent = this.queryResult.page;
this.paginatorTotalItems = this.queryResult.totalFilteredItems;
this.paginatorTotalPages = this.queryResult.totalFilteredPages;
//
//#region Preparing Paginator Buttons ...
//
// First ...
if (isNullOrUndefined(this.firstButtonDescriptor)) {
this.firstButtonDescriptor = {};
}
this.firstButtonDescriptor = {
...this.firstButtonDescriptor,
disabled: this.queryResult.page === 1,
};
//
// Prev ...
if (isNullOrUndefined(this.prevButtonDescriptor)) {
this.prevButtonDescriptor = {};
}
this.prevButtonDescriptor = {
...this.prevButtonDescriptor,
disabled: this.queryResult.page === 1,
};
//
// Next ...
if (isNullOrUndefined(this.nextButtonDescriptor)) {
this.nextButtonDescriptor = {};
}
this.nextButtonDescriptor = {
...this.nextButtonDescriptor,
disabled: this.queryResult.page >= this.queryResult.totalFilteredPages,
};
//
// Last ...
if (isNullOrUndefined(this.lastButtonDescriptor)) {
this.lastButtonDescriptor = {};
}
this.lastButtonDescriptor = {
...this.lastButtonDescriptor,
disabled: this.queryResult.page >= this.queryResult.totalFilteredPages,
};
//#endregion
//#endregion
}
/**
* Handle Prepared List Items Recieved ...
* @param result
*/
async handleListItemsRecieved(result: Array<XSimpleListItem<any>>) {
//
this.listItems = undefined;
this.detectChanges();
//
this.listItems = [...result];
this.detectChanges();
//
if (this.showItemsAsList && !this.isTabVisible(VQueryPresenterTabs.List)) {
this.changeTabVisibility(VQueryPresenterTabs.List, true);
}
//
this.detectChanges();
this.preparePaginator();
this.sendListAction(XListAction.Refresh);
this.runTimedOut(() => this.sendListAction(XListAction.Refresh), 100);
this.runTimedOut(() => this.sendListAction(XListAction.Refresh), 300);
this.runTimedOut(() => this.sendListAction(XListAction.Refresh), 500);
this.handleDetecteChanges();
}
/**
* Handle Filling Items Response ...
*
* @param items
*/
async handleFillingItemsRecieved(
model: VQueryFilledItemsProcessingResult<any>
) {
//
if (isNullOrUndefined(model)) {
return;
}
//
let applyChanges = false;
//
// Simplest Mode ...
if (
(isNullOrUndefined(model.changed) || !this.hasChild(model.changed)) &&
(isNullOrUndefined(model.exists) || !this.hasChild(model.exists))
) {
//
applyChanges = true;
this.items = [...model.news];
} else {
//
// Has Change Items ...
//
// Apply Changes ...
if (!isNullOrUndefined(model.changed) && this.hasChild(model.changed)) {
//
model.changed.forEach((changeModel) => {
//
const idx = this.items.findIndex(
(i) => i[model.identifier] === changeModel.identifier
);
if (idx >= 0) {
//
if (!applyChanges) {
applyChanges = true;
}
//
// Change Model ...
this.items[idx] = {
...changeModel.data,
};
}
});
}
//
// Push New Items ...
if (!isNullOrUndefined(model.news) && this.hasChild(model.news)) {
//
if (!applyChanges) {
applyChanges = true;
}
//
this.items.push(...model.news);
}
}
//
if (applyChanges) {
//
this.preparePaginator();
this.handleDetecteChanges();
//
if (!isNullOrUndefined(this.items) && this.hasChild(this.items)) {
//
const ids = this.items.map((i) => i[this.modelIdentifier]);
this.sendAction(VQueryPresenterAction.RefreshViews, ids);
}
}
}
/**
* Request to Do Specified Action based on item ...
*
* @param event
*/
handleListItemOptionsSelected(event: XListItemOptionSelectedEvent) {
//
// Replacing Data by Exact Model ...
event.data = event.data.data;
this.sendAction(VQueryPresenterAction.RequestItemAction, event);
}
//#endregion
//
//#region Details ...
@Input()
detailsTabTitle: XResourceIdentifier = this.propertyProvider.detailsTabTitle;
@Input()
detailsItemTemplate: TemplateRef<any> =
this.propertyProvider.detailsItemTemplate;
showDetailsTabContent: boolean = false;
//#endregion
//
//#region Add ...
@Input()
addTabTitle: XResourceIdentifier = this.propertyProvider.addTabTitle;
@Input()
addItemTemplate: TemplateRef<any> = this.propertyProvider.addItemTemplate;
showAddTabContent: boolean = false;
//#endregion
//
//#region Update ...
@Input()
updateTabTitle: XResourceIdentifier = this.propertyProvider.updateTabTitle;
@Input()
updateItemTemplate: TemplateRef<any> =
this.propertyProvider.updateItemTemplate;
//
showUpdateTabContent: boolean = false;
//#endregion
//#endregion
//
//#region Handlers ...
async handleStateBeforeTabChange(to: VQueryPresenterTabs) {
//
this.sendAction(VQueryPresenterAction.TabRequestState, {
from: this.tabsSelectedIndex,
to: to,
});
}
async handleStateAfterTabChanged() {
//
switch (this.tabsSelectedIndex) {
//
// List ...
case VQueryPresenterTabs.List:
await this.handleListTabOpenState();
break;
//
// Add ...
case VQueryPresenterTabs.Add:
//
// Make Tab Visible if not ...
if (!this.isTabVisible(this.tabsSelectedIndex)) {
this.changeTabVisibility(this.tabsSelectedIndex, true);
}
break;
//
// Update ...
case VQueryPresenterTabs.Update:
//
// Make Tab Visible if not ...
if (!this.isTabVisible(this.tabsSelectedIndex)) {
this.changeTabVisibility(this.tabsSelectedIndex, true);
}
break;
//
// Details ...
case VQueryPresenterTabs.Details:
//
// Make Tab Visible if not ...
if (!this.isTabVisible(this.tabsSelectedIndex)) {
this.changeTabVisibility(this.tabsSelectedIndex, true);
}
break;
}
//
this.sendAction(
VQueryPresenterAction.TabOpenedState,
this.tabsSelectedIndex
);
}
/**
* Handle Change Tab ...
*
* @param to
* @returns
*/
async changeTab(to: VQueryPresenterTabs) {
//
if (to === this.tabsSelectedIndex) {
return;
}
//
// Handle State Before Tabs Open ...
await this.handleStateBeforeTabChange(to);
//
// Since Tab Open State Handled using Event Emitter, here we ignore it ...
this.tabsSelectedIndex = to;
this.sendTabsAction(XTabsAction.TO, to);
this.sendTabsAction(XTabsAction.Refresh);
}
/**
* Hide other Tabs ...
* @param tab
* @returns
*/
hideOtherTabs(tab: number) {
//
if (isNullOrUndefined(tab) || tab !== this.tabsSelectedIndex) {
return;
}
//
switch (tab) {
//
case VQueryPresenterTabs.List:
//
// this.changeTabVisibility(VQueryPresenterTabs.List, true);
//
this.changeTabVisibility(VQueryPresenterTabs.Add, false);
this.changeTabVisibility(VQueryPresenterTabs.Update, false);
this.changeTabVisibility(VQueryPresenterTabs.Details, false);
break;
//
case VQueryPresenterTabs.Add:
//
this.changeTabVisibility(VQueryPresenterTabs.Add, true);
//
// this.changeTabVisibility(VQueryPresenterTabs.List, false);
this.changeTabVisibility(VQueryPresenterTabs.Update, false);
this.changeTabVisibility(VQueryPresenterTabs.Details, false);
break;
//
case VQueryPresenterTabs.Update:
//
this.changeTabVisibility(VQueryPresenterTabs.Update, true);
//
this.changeTabVisibility(VQueryPresenterTabs.Add, false);
// this.changeTabVisibility(VQueryPresenterTabs.List, false);
this.changeTabVisibility(VQueryPresenterTabs.Details, false);
break;
//
case VQueryPresenterTabs.Details:
//
this.changeTabVisibility(VQueryPresenterTabs.Details, true);
//
this.changeTabVisibility(VQueryPresenterTabs.Add, false);
// this.changeTabVisibility(VQueryPresenterTabs.List, false);
this.changeTabVisibility(VQueryPresenterTabs.Update, false);
break;
}
}
//#endregion
//
//#region Private ...
/**
* Handle Detect Changes ...
*/
private handleDetecteChanges() {
//
this.sendTabsAction(XTabsAction.Refresh);
this.sendListAction(XListAction.Refresh);
this.sendListTabCardAction(XCardAction.Refresh);
this.sendSearchBarAction(XSearchBarAction.Refresh);
this.sendPaginatorAction(VPaginatorAction.Refresh);
//
this.detectChanges();
}
/**
* Check Specified Tab is Visible or not ...
*
* @param tab
* @returns
*/
private isTabVisible(tab: VQueryPresenterTabs) {
//
let result = false;
//
switch (tab) {
//
// List ...
case VQueryPresenterTabs.List:
result = this.showListTabContent;
break;
//
// Details ...
case VQueryPresenterTabs.Details:
result = this.showDetailsTabContent;
break;
//
// Add ...
case VQueryPresenterTabs.Add:
result = this.showAddTabContent;
break;
//
// Update ...
case VQueryPresenterTabs.Update:
result = this.showUpdateTabContent;
break;
}
//
return result;
}
/**
* Change Tabs Visibility ...
*
* @param tab
* @param visible
*/
private changeTabVisibility(tab: VQueryPresenterTabs, visible: boolean) {
//
let applyChange = false;
//
switch (tab) {
//
// List ...
case VQueryPresenterTabs.List:
//
applyChange = visible != this.showListTabContent;
this.showListTabContent = visible;
break;
//
// Details ...
case VQueryPresenterTabs.Details:
//
applyChange = visible != this.showDetailsTabContent;
this.showDetailsTabContent = visible;
break;
//
// Add ...
case VQueryPresenterTabs.Add:
//
applyChange = visible != this.showAddTabContent;
this.showAddTabContent = visible;
break;
//
// Update ...
case VQueryPresenterTabs.Update:
//
applyChange = visible != this.showUpdateTabContent;
this.showUpdateTabContent = visible;
break;
}
//
if (applyChange) {
this.handleDetecteChanges();
}
}
/**
* Mnaging State (loading/disabled) ...
*/
private prevState = false;
private async handleStateChanges(state: boolean) {
//
if (state === this.prevState) {
return;
}
//
if (!isNullOrUndefined(this.listItems) && this.hasChild(this.listItems)) {
//
this.listItems = [
...this.listItems.map((li) => {
//
return {
...li,
slideOptions: [
...(isNullOrUndefined(li.slideOptions) ||
!this.hasChild(li.slideOptions)
? []
: li.slideOptions.map((so) => {
//
return {
...so,
disabled: so.disabled || state,
};
})),
],
};
}),
];
//
this.handleDetecteChanges();
}
//
this.prevState = state;
this.sendListAction(XListAction.Refresh);
}
//#endregion
//
//#region Action Providers ...
private handleRegisterListeners() {
//
//#region Tabs Action Provider ...
this.applyTakeUntilWrapper(this.tabsActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isValueInEnum(event.action, Object.assign({}, XTabsAction))
) {
return;
}
//
await this.handleTabsAction(event.action, event.payload);
}
);
//#endregion
//
//#region List Action Provider ...
this.applyTakeUntilWrapper(this.listActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isValueInEnum(event.action, Object.assign({}, XListAction))
) {
return;
}
//
await this.handleListTabCardAction(event.action, event.payload);
}
);
//#endregion
//
//#region SearchBar ...
this.applyTakeUntilWrapper(this.searchBarActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isValueInEnum(event.action, Object.assign({}, XSearchBarAction))
) {
return;
}
//
await this.handleSearchBarAction(event.action, event.payload);
}
);
//#endregion
//
//#region Paginator ...
this.applyTakeUntilWrapper(this.paginatorActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isValueInEnum(event.action, Object.assign({}, VPaginatorAction))
) {
return;
}
//
await this.handlePaginatorAction(event.action, event.payload);
}
);
//#endregion
//
//#region List Tab Card ...
this.applyTakeUntilWrapper(this.listTabCardActionProvider).subscribe(
async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isValueInEnum(event.action, Object.assign({}, XCardAction))
) {
return;
}
//
await this.handleListTabCardAction(event.action, event.payload);
}
);
//#endregion
//
//#region Tab Changed Event Emitter ...
this.applyTakeUntilWrapper(
this.tabsSelectedTabChange.asObservable()
).subscribe(async (event: XTabChangeEvent) => {
//
this.tabsSelectedIndex = event.index;
await this.handleStateAfterTabChanged();
//
// if Changed Tab is List Tab ...
// Handle Permissions or Actions State ...
if (this.tabsSelectedIndex === VQueryPresenterTabs.List) {
//
// If Show Items as List ...
if (this.showItemsAsList && this.hasChild(this.listItems)) {
//
// Re Validate List Items Actions ...
this.sendAction(VQueryPresenterAction.ValidateListItems, [
...this.listItems,
]);
}
}
});
//#endregion
//
//#region Action Provider ...
this.applyTakeUntilWrapper(this.actionProvider).subscribe(async (event) => {
//
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVQueryPresenterAction(event.action)
) {
return;
}
//
await this.handleAction(event.action, event.payload);
});
//#endregion
}
//
//#region Tabs ...
/**
* Send Tabs Action ...
*
* @param action
* @param payload
* @returns
*/
private sendTabsAction(action: XTabsAction, payload?: any) {
//
if (isNullOrUndefined(this.tabsActionProvider)) {
return;
}
//
this.tabsActionProvider.next({ action, payload });
}
/**
* Handle Specified Recieved Actions Of Tabs Action Provider ...
* @param action
* @param payload
*/
private async handleTabsAction(
action: XTabsActionIdentifier,
payload?: any
) {}
//#endregion
//
//#region List ...
/**
* Send List Action ...
*
* @param action
* @param payload
* @returns
*/
private sendListAction(action: XListAction, payload?: any) {
//
if (isNullOrUndefined(this.listActionProvider)) {
return;
}
//
this.listActionProvider.next({ action, payload });
}
/**
* Handle Specified Recieved Actions Of List Action Provider ...
*
* @param action
* @param payload
*/
private async handleListAction(
action: XListActionIdentifier,
payload?: any
) {}
//#endregion
//
//#region SearchBar ...
/**
* Send SearchBar Action ...
*
* @param action
* @param payload
*/
private sendSearchBarAction(action: XSearchBarAction, payload?: any) {
//
if (isNullOrUndefined(this.searchBarActionProvider)) {
return;
}
//
this.searchBarActionProvider.next({ action, payload });
}
/**
* Handle SearchBar Action ...
*
* @param action
* @param payload
*/
private async handleSearchBarAction(
action: XSearchBarActionIdentifier,
payload?: any
) {}
//#endregion
//
//#region Paginator ...
/**
* Send Paginator Action ...
*
* @param action
* @param payload
*/
private sendPaginatorAction(action: VPaginatorAction, payload?: any) {
//
if (isNullOrUndefined(this.paginatorActionProvider)) {
return;
}
//
this.paginatorActionProvider.next({ action, payload });
}
/**
* Handle Paginator Action ...
*
* @param action
* @param payload
*/
private async handlePaginatorAction(
action: VPaginatorActionIdentifier,
payload?: any
) {
//
// Update Buttons ...
if (
action === VPaginatorAction.UpdateButtonDescriptors &&
!isNullOrUndefined(payload)
) {
//
// First ...
if (!isNullOrUndefined(payload.first)) {
//
this.firstButtonDescriptor = {
...this.firstButtonDescriptor,
...payload.first,
};
}
//
// Last ...
if (!isNullOrUndefined(payload.last)) {
//
this.lastButtonDescriptor = {
...this.lastButtonDescriptor,
...payload.last,
};
}
//
// Next ...
if (!isNullOrUndefined(payload.next)) {
//
this.nextButtonDescriptor = {
...this.nextButtonDescriptor,
...payload.next,
};
}
//
// Prev ...
if (!isNullOrUndefined(payload.prev)) {
//
this.prevButtonDescriptor = {
...this.prevButtonDescriptor,
...payload.prev,
};
}
}
//
// Select Page Size ...
if (action === VPaginatorAction.SelectPageSizeRequest) {
await this.handleChangePageSize(payload.current, payload.allowed);
}
//
// To Specified Page ...
if (
action === VPaginatorAction.To &&
!isNullOrUndefined(payload) &&
isNumber(payload)
) {
//
// Validate Payload ...
const toPage = toNumber(payload);
if (
!isNullOrUndefined(this.queryResult) &&
toPage !== this.queryResult.page &&
toPage >= 1 &&
toPage <= this.queryResult.totalFilteredPages
) {
this.handleLoadSpecifiedPage(toPage);
}
}
//
// First ...
if (action === VPaginatorAction.First) {
this.handleLoadSpecifiedPage(1);
}
//
// Last ...
if (action === VPaginatorAction.Last) {
//
if (!isNullOrUndefined(this.queryResult)) {
//
const page = this.queryResult.totalFilteredPages;
this.handleLoadSpecifiedPage(page);
}
}
//
// Next ...
if (action === VPaginatorAction.Next) {
//
if (!isNullOrUndefined(this.queryResult)) {
//
const page = this.queryResult.page + 1;
this.handleLoadSpecifiedPage(page);
}
}
//
// Prev ...
if (action === VPaginatorAction.Prev) {
//
if (!isNullOrUndefined(this.queryResult)) {
//
const page = this.queryResult.page - 1;
this.handleLoadSpecifiedPage(page);
}
}
}
//#endregion
//
//#region ListTabCard ...
/**
* Send ListTabCard Action ...
*
* @param action
* @param payload
*/
private sendListTabCardAction(action: XCardAction, payload?: any) {
//
if (isNullOrUndefined(this.listTabCardActionProvider)) {
return;
}
//
this.listTabCardActionProvider.next({ action, payload });
}
/**
* Handle ListTabCard Action ...
*
* @param action
* @param payload
*/
private async handleListTabCardAction(
action: XListActionIdentifier,
payload?: any
) {}
//#endregion
//
//#region Action Provider ...
/**
* Send Action ...
*
* @param action
* @param payload
*/
private sendAction(action: VQueryPresenterAction, payload?: any) {
//
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.actionProvider.next({ action, payload });
}
/**
* Handle Action ...
*
* @param action
* @param payload
*/
private async handleAction(
action: VQueryPresenterActionIdentifier,
payload?: any
) {
//
// Refresh ...
if (action === VQueryPresenterAction.Refresh) {
//
if (!isNullOrUndefined(payload) && isNumber(payload)) {
this.runTimedOut(() => this.handleDetecteChanges(), toNumber(payload));
} else {
this.handleDetecteChanges();
}
}
//
// LoadMore ...
if (action === VQueryPresenterAction.LoadMore) {
//
const forceCurrent =
!isNullOrUndefined(payload) && isBoolean(payload)
? toBoolean(payload)
: false;
//
await this.handleLoadMore(forceCurrent);
}
//
// RefreshTab ...
if (
action === VQueryPresenterAction.RefreshTab &&
!isNullOrUndefined(payload) &&
isNumber(payload)
) {
//
this.changeTabVisibility(toNumber(payload), false);
this.runTimedOut(
() => this.changeTabVisibility(toNumber(payload), true),
500
);
}
//
// Add Model ...
if (
action === VQueryPresenterAction.AddModel &&
!isNullOrUndefined(payload)
) {
//
// Try to Update Model in Context ...
let isChanged = false;
//
if (
!isNullOrUndefined(this.query) &&
!isNullOrUndefined(this.queryResult) &&
isNullOrEmptyString(this.query.filter) &&
!this.hasChild(this.queryResult.items) &&
this.queryResult.page === 1 &&
this.queryResult.totalFilteredItems < this.queryResult.pageSize
) {
//
this.queryResult.items = [
...this.queryResult.items,
{ ...payload.model },
];
//
if (this.showItemsAsList) {
//
this.listItems = [
...this.listItems,
{
data: payload.model,
slideOptions: payload.actions,
},
];
} else {
this.sendAction(VQueryPresenterAction.RequestFillingItems, {
exists: [...this.items],
recieved: [{ ...payload.model }],
});
}
//
isChanged = true;
}
//
if (isChanged) {
//
this.handleDetecteChanges();
this.runTimedOut(() => this.handleDetecteChanges(), 100);
this.runTimedOut(() => this.handleDetecteChanges(), 300);
this.runTimedOut(() => this.handleDetecteChanges(), 500);
}
}
//
// Update Model ...
if (
action === VQueryPresenterAction.UpdateModel &&
!isNullOrUndefined(payload)
) {
//
// Try to Update Model in Context ...
let idx = -1;
let isChanged = false;
//
if (
!isNullOrUndefined(this.queryResult) &&
this.hasChild(this.queryResult.items)
) {
//
idx = this.queryResult.items.findIndex(
(i) => i[payload.key] === payload.model[payload.key]
);
if (idx >= 0) {
//
isChanged = true;
this.queryResult.items[idx] = {
...payload.model,
};
}
}
//
if (!isNullOrUndefined(this.listItems) && this.hasChild(this.listItems)) {
//
idx = this.listItems.findIndex(
(i) => i.data[payload.key] === payload.model[payload.key]
);
if (idx >= 0) {
//
isChanged = true;
this.listItems[idx] = {
...this.listItems[idx],
data: { ...payload.model },
slideOptions: [...payload.actions],
};
}
}
//
if (!isNullOrUndefined(this.items) && this.hasChild(this.items)) {
//
isChanged = true;
this.sendAction(VQueryPresenterAction.RequestFillingItems, {
exists: [...this.items],
recieved: [{ ...payload.model }],
});
}
//
if (isChanged) {
//
this.handleDetecteChanges();
this.runTimedOut(() => this.handleDetecteChanges(), 100);
this.runTimedOut(() => this.handleDetecteChanges(), 300);
this.runTimedOut(() => this.handleDetecteChanges(), 500);
}
}
//
// Remove Model ...
if (
action === VQueryPresenterAction.RemoveModel &&
!isNullOrUndefined(payload)
) {
//
// Try to Update Model in Context ...
let idx = -1;
let isChanged = false;
//
if (
!isNullOrUndefined(this.queryResult) &&
this.hasChild(this.queryResult.items)
) {
//
idx = this.queryResult.items.findIndex(
(i) => i[payload.key] === payload.model[payload.key]
);
if (idx >= 0) {
//
isChanged = true;
this.queryResult.items = this.queryResult.items.filter(
(i) => i[payload.key] !== payload.model[payload.key]
);
}
}
//
if (!isNullOrUndefined(this.listItems) && this.hasChild(this.listItems)) {
//
idx = this.listItems.findIndex(
(i) => i.data[payload.key] === payload.model[payload.key]
);
if (idx >= 0) {
//
isChanged = true;
this.listItems = this.listItems.filter(
(i) => i.data[payload.key] !== payload.model[payload.key]
);
}
}
//
if (!isNullOrUndefined(this.items) && this.hasChild(this.items)) {
//
idx = this.items.findIndex(
(i) => i[payload.key] === payload.model[payload.key]
);
if (idx >= 0) {
//
isChanged = true;
this.items = this.items.filter(
(i) => i[payload.key] !== payload.model[payload.key]
);
}
}
//
if (isChanged) {
//
this.preparePaginator();
this.handleDetecteChanges();
//
// Re Validate List Items Actions ...
this.sendAction(VQueryPresenterAction.ValidateListItems, [
...this.listItems,
]);
//
if (!isNullOrUndefined(this.items) && this.hasChild(this.items)) {
//
this.sendAction(VQueryPresenterAction.RefreshViews, [
...this.items.map((i) => i[this.modelIdentifier]),
]);
}
}
}
//
// Response Query Result ...
if (
action === VQueryPresenterAction.ResponseQueryResult &&
!isNullOrUndefined(payload)
) {
await this.handleQueryResultRecieved(payload.query, payload.result);
}
//
// Response List Items ...
if (action === VQueryPresenterAction.ResponseListItems) {
await this.handleListItemsRecieved(payload);
}
//
// Change Tab ...
if (
action === VQueryPresenterAction.ChangeTab &&
!isNullOrUndefined(payload)
) {
await this.changeTab(payload);
}
//
// Reset Query ...
if (action === VQueryPresenterAction.ResetQuery) {
//
if (
!isNullOrUndefined(payload) &&
isBoolean(payload) &&
toBoolean(payload) === true
) {
await this.changeTab(VQueryPresenterTabs.List);
}
//
this.resetQuery();
}
//
// Response Filling Items ...
if (action === VQueryPresenterAction.ResponseFillingItems) {
await this.handleFillingItemsRecieved(payload);
}
//
// HideOtherTabs ...
if (
action === VQueryPresenterAction.HideOtherTabs &&
!isNullOrUndefined(payload)
) {
this.hideOtherTabs(payload);
}
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-query-presenter\v-query-presenter.typings.ts
import {
hasChild,
XResourceIDs,
XColorIdentifier,
isNullOrUndefined,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
XTabAlign,
XIconNames,
XSearchBarType,
XIconIdentifier,
XBaseActionModel,
XTabAlignIdentifier,
XTabsHeaderPosition,
XSearchBarShowButton,
XSearchBarEnterKeyHint,
XSearchBarTypeIdentifier,
XTabsHeaderPositionIdentifier,
XSearchBarShowButtonIdentifier,
XSearchBarEnterKeyHintIdentifier,
} from 'x-framework-components';
import { isValueInEnum } from '../tools/v-base.tools';
import { Injectable, TemplateRef } from '@angular/core';
/**
* All Aawailable Tabs ...
*/
export enum VQueryPresenterTabs {
List = 0,
Details = 1,
Add = 2,
Update = 3,
}
//
//#region Paginator Position ...
export enum VQueryPresenterPaginatorPosition {
Above = 'above',
Below = 'below',
Both = 'both',
}
export type VQueryPresenterPaginatorPositionIdentifier =
| VQueryPresenterPaginatorPosition
| string;
export function isVQueryPresenterPaginatorPosition(value: string) {
return isValueInEnum(
value,
Object.assign(VQueryPresenterPaginatorPosition, {})
);
}
//#endregion ...
//
//#region Processing Model ...
/**
* a Changed Items in Processing ...
*/
export interface VQueryFilledItemsProcessingChangedItems<T = any> {
/**
* item identifier value ...
*/
identifier: any;
/**
* Item ...
*/
data: T;
}
/**
* Items Processing Result ...
*/
export interface VQueryFilledItemsProcessingResult<T = any> {
/**
* Exists Items ...
*/
exists: Array<T>;
/**
* Recieved Items ...
*/
recieved: Array<T>;
/**
* Items Uinque Identifier Field Name ...
*/
identifier: keyof T;
/**
* Change Items from Exists ...
*/
changed: Array<VQueryFilledItemsProcessingChangedItems<T>>;
/**
* New Items to Push ...
*/
news: Array<T>;
}
/**
* Process Items based ...
*
* @param model
* @returns
*/
export function processFilledItems<T = any>(
exists: Array<T>,
recieved: Array<T>,
identifier: keyof T
) {
//
let result: VQueryFilledItemsProcessingResult<T> = undefined;
//
// Validate Args ...
if (
isNullOrUndefined(identifier) ||
isNullOrUndefined(recieved) ||
!hasChild(recieved)
) {
return result;
}
//
// Simplest Mode ...
if (isNullOrUndefined(exists) || !hasChild(exists)) {
//
// Has not Exists items ...
result = {
exists,
recieved,
identifier,
changed: [],
news: [...recieved],
};
}
//
// Complex Mode ...
else {
//
// Has Exists Items ...
const news: Array<T> = [];
const changed: Array<VQueryFilledItemsProcessingChangedItems<T>> = [];
//
// Process Exists items ...
recieved.forEach((rItem) => {
//
// Recieved Item ...
const isExists =
exists.findIndex((eItem) => eItem[identifier] === rItem[identifier]) >=
0;
//
// If new Item Recieveds ...
if (!isExists) {
news.push(rItem);
}
//
// If Exists ...
else {
//
const identifierValue = rItem[identifier];
const data: T = {
...rItem,
};
//
changed.push({
identifier: identifierValue,
data,
});
}
});
//
// Changed Model ...
result = {
exists,
recieved,
identifier,
changed,
news,
};
}
//
return result;
}
//#endregion
//
//#region Action Provider ...
/**
* All Actions/Events of Component ...
*/
export enum VQueryPresenterAction {
/**
* Refresh and Detect Change ...
*/
Refresh = 'refresh',
/**
* Call when we Have to Load More ...
* as Ususall Action, loading More means request next page if Exists ...
* except has an error on Previous loading ...
*/
LoadMore = 'LoadMore',
/**
* Refresh Specified Tab ...
*/
RefreshTab = 'RefreshTab',
/**
* Refreshing Views After Update ...
*/
RefreshViews = 'RefreshViews',
/**
* Change Tab ...
*/
ChangeTab = 'ChangeTab',
/**
* Reset Query ...
* if pass true as payload also Change Tab to List ...
*/
ResetQuery = 'ResetQuery',
/**
* Add a Model ...
* payload: {
* model: any; // Data to Update ...
* key: string; // a Field Name to Detect model to Update based on it ...
* actions: XListItemSlideOptions[]; // Provided Slide Options ...
* }
*/
AddModel = 'AddModel',
/**
* Update a Model ...
* payload: {
* model: any; // Data to Update ...
* key: string; // a Field Name to Detect model to Update based on it ...
* actions: XListItemSlideOptions[]; // Provided Slide Options ...
* }
*/
UpdateModel = 'UpdateModel',
/**
* Remove a Model ...
* payload: {
* model: any; // Data to Update ...
* key: string; // a Field Name to Detect model to Update based on it ...
* actions: XListItemSlideOptions[]; // Provided Slide Options ...
* }
*/
RemoveModel = 'RemoveModel',
/**
* Tab Open State ...
*/
TabOpenedState = 'TabOpenedState',
/**
* Request Handle State Before Specified Tabs Open ...
* from: points to current ...
* to: points to next ...
*/
TabRequestState = 'TabRequestState',
/**
* Request Item Action to Done ...
*/
RequestItemAction = 'RequestItemAction',
/**
* Request Execute Query and Retrieve Query Result ...
*/
RequestQueryResult = 'RequestQueryResult',
/**
* Response the Query Result ...
*/
ResponseQueryResult = 'ResponseQueryResult',
/**
* Validate and Provide List Item Options ...
*/
ValidateListItems = 'ValidateListItems',
/**
* Response Validated List Items ...
*/
ResponseListItems = 'ResponseListItems',
/**
* for Non As List Items ...
* payload: {
* current: Array<any> => current Items ...
* recieved: Array<any> => recieved Items ...
* }
*/
RequestFillingItems = 'RequestFillingItems',
ResponseFillingItems = 'ResponseFillingItems',
/**
* invisible tabs ...
* payload: number ...
*/
HideOtherTabs = 'HideOtherTabs',
}
export interface VQueryPresenterActionModel
extends XBaseActionModel<VQueryPresenterAction> {}
export type VQueryPresenterActionIdentifier = VQueryPresenterAction | string;
export function isVQueryPresenterAction(value: string) {
return isValueInEnum(value, Object.assign({}, VQueryPresenterAction));
}
//#endregion
//
//#region Container ...
export interface IVQueryPresenterContainer {
//
//#region Tabs Presenter Props ...
tabsCssClass: Array<string>;
tabsSelectedIndex: number;
tabsShowHeader: boolean;
tabsDisablePagination: boolean;
tabsAlignTabs: XTabAlignIdentifier;
tabsDynamicHeight: boolean;
tabsAnimationDuration: string;
tabsDisableRipple: boolean;
tabsHeaderPosition: XTabsHeaderPositionIdentifier;
tabsLoopTabs: boolean;
tabsSwipable: boolean;
tabsLock: boolean;
tabsColor: XColorIdentifier;
tabsHeaderColor: XColorIdentifier;
tabsActiveTabColor: XColorIdentifier;
tabsTabIndicatorColor: XColorIdentifier;
tabsTabPaginatorColor: XColorIdentifier;
//#endregion
//
//#region Search Bar ...
showSearchBar: boolean;
searchBarValue: string;
searchBarDebounce: number;
searchBarSpellcheck: boolean;
searchBarCssClass: Array<string>;
searchBarColor: XColorIdentifier;
searchBarForegroundColor: boolean;
searchBarTitle: XResourceIdentifier;
searchBarType: XSearchBarTypeIdentifier;
searchBarClearButtonIcon: XIconIdentifier;
searchBarCancelButtonIcon: XIconIdentifier;
searchBarSearchButtonIcon: XIconIdentifier;
searchBarSearchInputColor: XColorIdentifier;
searchBarForegroundSearchInputColor: boolean;
searchBarKeyboardType: XSearchBarTypeIdentifier;
searchBarCancelButtonTitle: XResourceIdentifier;
searchBarClearButtonIconColor: XColorIdentifier;
searchBarCancelButtonIconColor: XColorIdentifier;
searchBarSearchButtonIconColor: XColorIdentifier;
searchBarEnterKeyHint: XSearchBarEnterKeyHintIdentifier;
searchBarShowClearButton: XSearchBarShowButtonIdentifier;
searchBarShowCancelButton: XSearchBarShowButtonIdentifier;
//#endregion
//
//#region Paginator ...
showPaginator: boolean;
paginatorPosition: VQueryPresenterPaginatorPositionIdentifier;
paginatorColor: XColorIdentifier;
//#endregion
//
//#region Tabs ...
//
//#region List ...
listTabTitle: XResourceIdentifier;
showItemsAsList: boolean;
modelIdentifier: string;
itemTemplate: TemplateRef<any>;
listItemTemplate: TemplateRef<any>;
wrapListTabWithCard: boolean;
listTabCardIsInPage: boolean;
listTabCardTitle: XResourceIdentifier;
listTabCardSubTitle: XResourceIdentifier;
listTabCardColor: XColorIdentifier;
//
//#region Empty ...
showEmpty: boolean;
emptyCssClass: Array<string>;
emptyColor: XColorIdentifier;
emptyForegroundColor: boolean;
emptyMessage: XResourceIdentifier;
emptyMessageColor: XColorIdentifier;
emptyForegroundMessageColor: boolean;
emptyHasRefresh: boolean;
emptyRefreshButtonColor: XColorIdentifier;
emptyForegroundRefreshButtonColor: boolean;
emptyRefreshIcon: XIconIdentifier;
//#endregion
//#endregion
//
//#region Details ...
detailsTabTitle: XResourceIdentifier;
detailsItemTemplate: TemplateRef<any>;
//#endregion
//
//#region Add ...
addTabTitle: XResourceIdentifier;
addItemTemplate: TemplateRef<any>;
//#endregion
//
//#region Update ...
updateTabTitle: XResourceIdentifier;
updateItemTemplate: TemplateRef<any>;
//#endregion
//#endregion
}
@Injectable({
providedIn: 'root',
})
export class VQueryPresenterContainer
implements Readonly<IVQueryPresenterContainer>
{
//
//#region Tabs Presenter Props ...
readonly tabsCssClass: Array<string> = [];
readonly tabsSelectedIndex: number = VQueryPresenterTabs.List;
readonly tabsShowHeader: boolean = false;
readonly tabsDisablePagination: boolean = false;
readonly tabsAlignTabs: XTabAlignIdentifier = XTabAlign.STRETCH;
readonly tabsDynamicHeight: boolean = true;
readonly tabsAnimationDuration: string = '500ms';
readonly tabsDisableRipple: boolean = false;
readonly tabsHeaderPosition: XTabsHeaderPositionIdentifier =
XTabsHeaderPosition.ABOVE;
readonly tabsLoopTabs: boolean = true;
readonly tabsSwipable: boolean = false;
readonly tabsLock: boolean = false;
readonly tabsColor: XColorIdentifier = XColorWithBrightness.Transparent;
readonly tabsHeaderColor: XColorIdentifier = XColorWithBrightness.Primary;
readonly tabsActiveTabColor: XColorIdentifier =
XColorWithBrightness.Secondary;
readonly tabsTabIndicatorColor: XColorIdentifier =
XColorWithBrightness.SecondaryTint;
readonly tabsTabPaginatorColor: XColorIdentifier =
XColorWithBrightness.SecondaryShade;
//#endregion
//
//#region Search Bar ...
readonly showSearchBar: boolean = false;
readonly searchBarValue: string = '';
readonly searchBarDebounce: number = 1000;
readonly searchBarSpellcheck: boolean = false;
readonly searchBarCssClass: Array<string> = [];
readonly searchBarColor: XColorIdentifier = XColorWithBrightness.Primary;
readonly searchBarForegroundColor: boolean = false;
readonly searchBarTitle: XResourceIdentifier = XResourceIDs.search;
readonly searchBarType: XSearchBarTypeIdentifier = XSearchBarType.Search;
readonly searchBarClearButtonIcon: XIconIdentifier;
readonly searchBarCancelButtonIcon: XIconIdentifier;
readonly searchBarSearchButtonIcon: XIconIdentifier = XIconNames.search;
readonly searchBarSearchInputColor: XColorIdentifier =
XColorWithBrightness.PrimaryShade;
readonly searchBarForegroundSearchInputColor: boolean = false;
readonly searchBarKeyboardType: XSearchBarTypeIdentifier =
XSearchBarType.Search;
readonly searchBarCancelButtonTitle: XResourceIdentifier =
XResourceIDs.cancel;
readonly searchBarClearButtonIconColor: XColorIdentifier =
XColorWithBrightness.Danger;
readonly searchBarCancelButtonIconColor: XColorIdentifier =
XColorWithBrightness.Danger;
readonly searchBarSearchButtonIconColor: XColorIdentifier =
XColorWithBrightness.Warning;
readonly searchBarEnterKeyHint: XSearchBarEnterKeyHintIdentifier =
XSearchBarEnterKeyHint.Search;
readonly searchBarShowClearButton: XSearchBarShowButtonIdentifier =
XSearchBarShowButton.Always;
readonly searchBarShowCancelButton: XSearchBarShowButtonIdentifier =
XSearchBarShowButton.Never;
//#endregion
//
//#region Paginator ...
readonly showPaginator: boolean = true;
readonly paginatorPosition: VQueryPresenterPaginatorPositionIdentifier =
VQueryPresenterPaginatorPosition.Below;
readonly paginatorColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
//#endregion
//
//#region Tabs ...
//
//#region List ...
readonly listTabTitle: XResourceIdentifier = XResourceIDs.list;
readonly showItemsAsList: boolean = true;
readonly modelIdentifier: string = '';
readonly itemTemplate: TemplateRef<any> = undefined;
readonly listItemTemplate: TemplateRef<any> = undefined;
readonly wrapListTabWithCard: boolean = true;
readonly listTabCardIsInPage: boolean = true;
readonly listTabCardTitle: XResourceIdentifier = undefined;
readonly listTabCardSubTitle: XResourceIdentifier = undefined;
readonly listTabCardColor: XColorIdentifier = XColorWithBrightness.Light;
//
//#region Empty ...
readonly showEmpty: boolean = true;
readonly emptyCssClass: Array<string> = [];
readonly emptyColor: XColorIdentifier = XColorWithBrightness.Light;
readonly emptyForegroundColor: boolean = false;
readonly emptyMessage: XResourceIdentifier = XResourceIDs.no_item;
readonly emptyMessageColor: XColorIdentifier = XColorWithBrightness.Dark;
readonly emptyForegroundMessageColor: boolean = true;
readonly emptyHasRefresh: boolean = true;
readonly emptyRefreshButtonColor: XColorIdentifier =
XColorWithBrightness.Dark;
readonly emptyForegroundRefreshButtonColor: boolean = true;
readonly emptyRefreshIcon: XIconIdentifier = XIconNames.refresh;
//#endregion
//#endregion
//
//#region Details ...
readonly detailsTabTitle: XResourceIdentifier = XResourceIDs.details;
readonly detailsItemTemplate: TemplateRef<any> = undefined;
//#endregion
//
//#region Add ...
readonly addTabTitle: XResourceIdentifier = XResourceIDs.add;
readonly addItemTemplate: TemplateRef<any> = undefined;
//#endregion
//
//#region Update ...
readonly updateTabTitle: XResourceIdentifier = XResourceIDs.update;
readonly updateItemTemplate: TemplateRef<any> = undefined;
//#endregion
//#endregion
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-reference\v-reference.component.html
<v-dto #dtoPresenter *ngIf="showComponent" [dtoContainer]="this">
<!-- Content Injections -->
</v-dto>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-reference\v-reference.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-reference\v-reference.component.ts
import {
toXString,
getFieldNames,
XExceptionIDs,
nameofInstance,
isNullOrUndefined,
isNullOrEmptyString,
} from 'x-framework-core';
import {
XReferenceDto,
defaultReferenceDto,
prepareReferenceDtoFields,
} from 'x-saherelm-api-sdk';
import {
VReferencePresentType,
isVReferencePresentType,
VReferencePresentTypeIdentifier,
} from './v-reference.typings';
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../v-dto/v-dto.typings';
import { Component, Input } from '@angular/core';
@Component({
selector: 'v-reference',
templateUrl: './v-reference.component.html',
styleUrls: ['./v-reference.component.scss'],
})
export class VReferenceComponent extends VBaseDtoComponent<XReferenceDto<any>> {
//
//#region Props ...
readonly defaultModel: XReferenceDto<any> = {
...defaultReferenceDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
readonly ReferencePresentTypes = Object.assign({}, VReferencePresentType);
//
@Input()
presentType: VReferencePresentTypeIdentifier =
this.ReferencePresentTypes.AsView;
//
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// Index ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.index,
value: this.defaultModel.index,
label: this.AppResourceIDs.index,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.applyLocale(value);
},
} as VModelFieldDescriptor<number>,
//
// ProvidedFor ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.providedFor,
type: this.ModelFieldTypes.String,
value: this.defaultModel.providedFor,
label: this.AppResourceIDs.provided_for,
} as VModelFieldDescriptor<string>,
//
// ReferenceTo ...
{
hide: false,
hideLabel: false,
allowFieldForm: false,
key: this.FieldNames.referencedTo,
type: this.ModelFieldTypes.String,
value: this.defaultModel.referencedTo,
label: this.AppResourceIDs.referenced_to,
valueProvider: (value) => {
return toXString(value);
},
} as VModelFieldDescriptor<any>,
];
//#endregion
//
//#region LifeCycles ...
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Present Type ...
isChanged = changeKeys.includes(nameofInstance(this, 'presentType'));
if (isChanged) {
//
if (!isVReferencePresentType(this.presentType)) {
//
applyChanges = true;
this.presentType = this.ReferencePresentTypes.AsView;
}
}
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region Abstraction ...
/**
* Prepare Model Fields ...
*
* @param model
* @returns
*/
prepare(model?: XReferenceDto<any>): XReferenceDto<any> {
return prepareReferenceDtoFields(model);
}
/**
* Handle Model Validation ...
*
* @param model
* @returns
*/
validate(
model?: Partial<XReferenceDto<any>>,
): VModelValidationResult<XReferenceDto<any>> {
//
let result: VModelValidationResult<XReferenceDto<any>> = undefined;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// Index ...
isValid = !isNullOrUndefined(normalized.index) && normalized.index >= 0;
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.index,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// ProvidedFor ...
isValid = !isNullOrEmptyString(normalized.providedFor);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.provided_for,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// ReferenceTo ...
isValid = !isNullOrUndefined(normalized.referencedTo);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.referenced_to,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd =
isNullOrUndefined(normalized.index) || normalized.index < 0;
const isValidForUpdate =
!isNullOrUndefined(normalized.index) && normalized.index > 0;
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-reference\v-reference.configs.ts
//
//#region Localization Configuration ...
export enum ReferenceResourceIDs {
reference = 'reference',
references = 'references',
provided_for = 'provided_for',
referenced_to = 'referenced_to',
}
//
//#region Single Resources ...
//
//#region reference ...
export const reference_fa = {
id: 'reference',
value: 'مرجع',
};
export const reference_en = {
id: 'reference',
value: 'Reference',
};
//#endregion
//
//#region references ...
export const references_fa = {
id: 'references',
value: 'مراجع',
};
export const references_en = {
id: 'references',
value: 'References',
};
//#endregion
//
//#region provided_for ...
export const provided_for_fa = {
id: 'provided_for',
value: 'متصل به',
};
export const provided_for_en = {
id: 'provided_for',
value: 'Provided To',
};
//#endregion
//
//#region referenced_to ...
export const referenced_to_fa = {
id: 'referenced_to',
value: 'شناسه مرجع',
};
export const referenced_to_en = {
id: 'referenced_to',
value: 'Referenced Identifier',
};
//#endregion
//#endregion
//
export const ReferenceLocalizationResourceIDs = Object.assign(
{},
ReferenceResourceIDs
);
//
export const ReferenceLocalizationResources = {
//
reference: {
fa: { ...reference_fa },
en: { ...reference_en },
},
references: {
fa: { ...references_fa },
en: { ...references_en },
},
provided_for: {
fa: { ...provided_for_fa },
en: { ...provided_for_en },
},
referenced_to: {
fa: { ...referenced_to_fa },
en: { ...referenced_to_en },
},
};
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-reference\v-reference.typings.ts
import { isValueInEnum } from '../tools/v-base.tools';
import { isNullOrUndefined, isString } from 'x-framework-core';
import { isXReferenceDto, XReferenceDto } from 'x-saherelm-api-sdk';
//
//#region ReferenceType ...
export type XReferenceType = XReferenceDto<any> | string;
export function isReferenceStringType(reference: XReferenceType) {
//
let result = !isNullOrUndefined(reference) && isString(reference);
//
return result;
}
export function isReferenceSDtoType(reference: XReferenceType) {
//
let result = !isNullOrUndefined(reference) && isXReferenceDto(reference);
//
return result;
}
//#endregion
//
//#region Present Type ...
export enum VReferencePresentType {
AsNone = 'as_none',
AsView = 'as_view',
AsItem = 'as_item',
}
export type VReferencePresentTypeIdentifier = VReferencePresentType | string;
export function isVReferencePresentType(value: string) {
return isValueInEnum(value, Object.assign({}, VReferencePresentType));
}
export function isVisibleVReferencePresentType(
value: VReferencePresentTypeIdentifier,
) {
return (
isVReferencePresentType(value) && value !== VReferencePresentType.AsNone
);
}
//#endregion
//
//#region Action Provider ...
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-video\v-video.component.html
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
[loading]="loading"
[uiDisabled]="uiDisabled"
class="v-card v-component-card"
[class.v-no-margin-card]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[isInPage]="getValue(isInPage) | async"
[showHeader]="
(notEmptyValue(cardTitle) | async) ||
(notEmptyValue(cardSubTitle) | async)
"
[cssClass]="getValue(cardCssClass) | async"
[title]="getResourceValue(cardTitle) | async"
[showFooter]="getValue(cardShowFooter) | async"
[showActions]="getValue(cardShowActions) | async"
[subTitle]="getResourceValue(cardSubTitle) | async"
[foregroundColor]="getValue(cardForegroundColor) | async"
>
<!-- Content -->
<ng-container *ngTemplateOutlet="componentsRef"></ng-container>
<!-- Actions -->
<div class="x-actions" actions *ngIf="getValue(cardShowActions) | async">
<ng-content select="[actions]"></ng-content>
</div>
<!-- Header -->
<div
header
class="x-header"
[isForeground]="true"
[xColor]="getValue(cardColor) | async"
></div>
<!-- Footer -->
<div class="x-footer" footer *ngIf="getValue(cardShowFooter) | async">
<ng-content select="[footer]"></ng-content>
</div>
</x-card>
</ng-container>
<!-- Component's itSelf Template -->
<ng-template #componentsRef>
<section
(keydown)="onKeydown($event)"
(mousemove)="showControlsTemporarily()"
[ngClass]="getArrayValue(cssClass) | async"
class="v-video-container v-component-wrapper unselecteable"
>
<!-- Video Element -->
<video
#videoRef
[src]="src"
class="video"
[poster]="poster"
(ended)="onEnded()"
[srcObject]="srcObject"
(waiting)="onWaiting()"
(canplay)="onCanPlay()"
[attr.preload]="preload"
[attr.playsinline]="true"
(progress)="onProgress()"
(play)="isPlaying = true"
(pause)="isPlaying = false"
(timeupdate)="onTimeUpdate()"
(loadeddata)="onLoadedData()"
(volumechange)="onVolumeChange()"
(loadedmetadata)="onLoadedMetadata()"
[attr.crossorigin]="crossOrigin || null"
></video>
<!-- second video overlay -->
<div
cdkDrag
#pictureInPictureContainer
*ngIf="enablePictureInPicture"
class="v-second-video-draggable"
(click)="$event.stopPropagation()"
cdkDragBoundary=".v-video-container"
>
<video
#secondVideo
autoplay
playsinline
muted
[src]="pictureInPictureSrc"
[srcObject]="pictureInPictureStream"
></video>
</div>
<!-- Top overlay: title -->
<div class="overlay top hidable" [class.hidden]="controlsHidden">
<div
class="title"
*ngIf="showTitle"
[isForeground]="true"
[xColor]="ColorNames.LightShade"
>
{{ applyLocale(title) }}
</div>
</div>
<!-- Center Toggle Button -->
<section
class="overlay center hidable"
*ngIf="loading || showPlayPauseToggler"
[class.hidden]="controlsHidden && !loading"
>
<x-button
[loading]="loading"
[foregroundColor]="true"
(clicked)="togglePlay()"
[type]="ButtonTypes.Icon"
[title]="getPlayPauseTitle()"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[uiDisabled]="uiDisabled || loading"
*ngIf="showPlayPauseToggler && !loading"
[icon]="isPlaying ? IconNames.pause : IconNames.play"
></x-button>
<!-- Loading Spinner -->
<x-spinner
*ngIf="loading"
[name]="loadingSpinner"
[color]="loadingSpinnerColor"
></x-spinner>
</section>
<!-- Controls Area -->
<div class="overlay bottom">
<!-- Speech Regnition -->
<div
[isForeground]="true"
#speechRecognitionContainer
[xColor]="speechRecognitionColor"
class="v-speech-recognition-container"
*ngIf="
speechRecognizerEnabled &&
enableSpeechRecognition &&
(notNullOrUndefinedValue(speechRecognizer) | async)
"
></div>
<!-- Action Controls -->
<div
[class.hidden]="controlsHidden"
class="v-video-controlls-container hidable"
>
<!-- Vide Progress Bar -->
<div class="v-video-progress-row" *ngIf="showProgressBar">
<div
*ngIf="showSeekingBar"
(click)="seekByClick($event)"
class="v-video-progress-track"
>
<div
class="v-video-progress-buffered"
[style.width.%]="bufferedPercent"
></div>
<div
class="v-video-progress-current"
[style.width.%]="currentPercent"
></div>
</div>
<div class="v-video-time" *ngIf="showVideoTime">
{{ formatVideoTime(currentTime) }} / {{ formatVideoTime(duration) }}
</div>
</div>
<!-- Video Actions -->
<div class="v-video-actions-row" *ngIf="showActions">
<!-- In Start Controls -->
<div class="left">
<!-- Play / Pause Control -->
<x-button
[loading]="loading"
[foregroundColor]="true"
(clicked)="togglePlay()"
[type]="ButtonTypes.Icon"
*ngIf="showPlayPauseControl"
[title]="getPlayPauseTitle()"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[uiDisabled]="uiDisabled || loading"
[icon]="isPlaying ? IconNames.pause : IconNames.play"
></x-button>
<!-- Rewind Control -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
*ngIf="showRewindControl"
[icon]="rewindControlIcon"
[title]="rewindControlTitle"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[uiDisabled]="uiDisabled || loading"
(clicked)="seekRelative(-1 * seekingStep)"
></x-button>
<!-- Forward -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
*ngIf="showForwardControl"
[icon]="forwardControlIcon"
[title]="forwardControlTitle"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[uiDisabled]="uiDisabled || loading"
(clicked)="seekRelative(1 * seekingStep)"
></x-button>
<!-- Mute/UnMute Toggler -->
<x-button
[loading]="loading"
[foregroundColor]="true"
(clicked)="toggleMute()"
[type]="ButtonTypes.Icon"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[icon]="getMuteUnMuteToggleIcon()"
[title]="getMuteUnMuteToggleTitle()"
[uiDisabled]="uiDisabled || loading"
*ngIf="showVolumeTogglerControl && !showCallingActions"
></x-button>
<!-- Volume Form Config -->
<x-form
[loading]="loading"
[formConfig]="volumeFormConfig"
[activeColor]="ColorNames.Light"
[cssClass]="'v-video-volume-form'"
[uiDisabled]="uiDisabled || loading"
[actionProvider]="volumeFormActionProvider"
*ngIf="
showVolumeChangerControl &&
(notNullOrUndefinedValue(volumeFormConfig) | async)
"
></x-form>
</div>
<!-- In End Controls -->
<div class="right">
<!-- Current Playback Rate -->
<section
[isForeground]="true"
[xColor]="ColorNames.Light"
class="v-current-playback-rate"
*ngIf="showCurrentPlaybackRate"
>
{{ getPlaybackRatesTitle() }}
</section>
<!-- Playback Rates -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
[icon]="IconNames.expand"
[color]="ColorNames.LightShade"
*ngIf="showPlaybackRatesControl"
[title]="getPlaybackRatesTitle()"
(click)="selectPlaybackRates($event)"
[uiDisabled]="uiDisabled || loading"
>
</x-button>
<!-- Fullscreen Toggler -->
<x-button
[loading]="loading"
[foregroundColor]="true"
[type]="ButtonTypes.Icon"
(clicked)="toggleFullscreen()"
[iconColor]="ColorNames.Light"
[color]="ColorNames.LightShade"
[icon]="getFullscreenToggleIcon()"
[title]="getFullscreenToggleTitle()"
[uiDisabled]="uiDisabled || loading"
*ngIf="showFullscreenTogglerControl && !showCallingActions"
></x-button>
</div>
</div>
<!-- Calling Actions -->
<div
*ngIf="showCallingActions"
class="v-video-calling-actions v-video-actions-row"
>
<x-action-bar
class="v-action-bar"
[loading]="loading"
[uiDisabled]="uiDisabled"
[actions]="callingActions"
[actionProvider]="callingActionsActionProvider"
(actionFired)="handleCallingActionFired($event)"
>
</x-action-bar>
</div>
</div>
</div>
</section>
</ng-template>
<!-- Nothing Present Type Template -->
<ng-template #nothingPresentTypeRef></ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-video\v-video.component.scss
.v-card {
border-radius: 8px;
overflow: hidden;
}
.v-video-container {
position: relative;
width: 100%;
max-width: 960px;
margin: 0 auto;
aspect-ratio: 16 / 9;
background: #000;
}
.video {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
background: #000;
}
.overlay.top {
position: absolute;
top: 8px;
left: 12px;
right: 12px;
display: flex;
justify-content: space-between;
pointer-events: none;
}
.title {
font-size: 14px;
opacity: 0.8;
}
.overlay.center {
position: absolute;
inset: 0;
margin: auto;
width: 64px;
height: 64px;
color: #fff;
opacity: 0.85;
}
//
// Controls Styles ...
.v-speech-recognition-container {
width: 100%;
min-height: 50px;
max-height: 200px;
overflow-y: scroll;
font-size: small;
}
.overlay.bottom {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 8px 12px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
transition: opacity 180ms ease-in-out;
}
.hidable.hidden {
opacity: 0;
pointer-events: none;
}
.v-video-progress-row {
display: flex;
align-items: center;
gap: 12px;
}
.v-video-progress-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.25);
cursor: pointer;
position: relative;
}
.v-video-progress-buffered,
.v-video-progress-current {
position: absolute;
left: 0;
top: 0;
height: 100%;
border-radius: 2px;
}
.v-video-progress-buffered {
background: rgba(255, 255, 255, 0.4);
width: 0%;
}
.v-video-progress-current {
background: #ff4081; /* Angular Material pink accent */
width: 0%;
}
.v-video-time {
font-size: 12px;
opacity: 0.85;
white-space: nowrap;
}
.v-video-actions-row {
margin-top: 6px;
display: flex;
align-items: center;
justify-content: space-between;
}
.v-video-actions-row .left,
.v-video-actions-row .right {
display: flex;
align-items: center;
gap: 8px;
}
.v-video-volume {
width: 140px;
}
.v-action-bar {
width: 100%;
}
.v-second-video-draggable {
right: 16px;
width: 200px;
bottom: 16px;
resize: both;
height: 120px;
overflow: hidden;
border-radius: 8px;
position: absolute;
border: 2px solid white;
background: black;
cursor: move;
z-index: 9999;
video {
width: 100%;
height: 100%;
object-fit: contain; // 👈 show the entire shared window
background: black; // 👈 fill empty space with black bars
}
}
//
//
//
:host ::ng-deep .v-action-bar {
ion-toolbar {
--background: transparent !important;
background: transparent !important;
background-color: transparent !important;
}
}
:host ::ng-deep .v-recording-timer {
//
button {
//
background: transparent !important;
.mat-button-wrapper > .x-action-bar-item-inner {
x-icon {
display: none !important;
}
}
}
//
--background: transparent !important;
background: transparent !important;
background-color: transparent !important;
}
:host ::ng-deep .v-video-volume-form,
:host ::ng-deep .v-video-volume-slider {
padding: 0 !important;
margin: 0 !important;
min-width: 100px !important;
max-width: 200px !important;
* {
padding: 0 !important;
margin: 0 !important;
}
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-video\v-video.component.ts
import {
Input,
NgZone,
Inject,
Component,
ViewChild,
Renderer2,
ElementRef,
HostListener,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
nameof,
isNumber,
XSpinner,
toNumber,
generateUuid,
XStandardType,
getFieldNames,
XOneOrManyType,
setPropertyValue,
getPropertyValue,
XColorIdentifier,
isNullOrUndefined,
XSpinnerIdentifier,
XResourceIdentifier,
isNullOrEmptyString,
XVideoFileExtensions,
downloadArrayBufferedFile,
} from 'x-framework-core';
import {
IVVideoFlags,
IVVideoState,
VVideoAction,
isVVideoAction,
VVideoActionModel,
MediaRecorderType,
defaultAudioPoster,
VVideoVolumeSelectModel,
VVideoComponentContainer,
IVVideoComponentContainer,
} from './v-video.typings';
import {
XCardAction,
XButtonType,
XPopoverOption,
XPopoverService,
XFormBaseAction,
XIconIdentifier,
XFormActionModel,
XFormControlType,
XCardActionModel,
XActionBarAction,
XActionBarItemSlot,
XActionBarActionModel,
XFormControlAppearance,
XComponentCardContainer,
XFormSliderControlConfig,
XActionBarActionFiredModel,
XFormControlValueChangeEventModel,
} from 'x-framework-components';
import {
defaultMuteActionBar,
defaultMicOnActionBar,
defaultUnMuteActionBar,
defaultMicOffActionBar,
defaultVideoOnActionBar,
defaultCallEndActionBar,
defaultVideoOffActionBar,
defaultFullscreenActionBar,
defaultStopRecordActionBar,
defaultStartRecordActionBar,
defaultCallRequestActionBar,
defaultUnfullscreenActionBar,
defaultStopScreenShareActionBar,
defaultStartScreenShareActionBar,
defaultSwitchToBackCameraActionBar,
defaultSwitchToFrontCameraActionBar,
} from '../constants/actionbar.defaults';
import { Subject, Subscription } from 'rxjs';
import { CdkDrag } from '@angular/cdk/drag-drop';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { XManagerService } from 'x-framework-services';
import { VBaseInCardComponent } from '../classes/v-base.component';
import { XSimpleFormConfig } from '../typings/simple.form.typings';
import { SharedService } from 'src/app/shared/services/shared.service';
import { XSimpleActionBarItem } from '../typings/simple.actionbar.typings';
import { XCameraFacing } from 'src/app/shared/services/x-media-devices.service';
/**
* a Media Presenter Component ...
*/
@Component({
selector: 'v-video',
templateUrl: './v-video.component.html',
styleUrls: ['./v-video.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VVideoComponent
extends VBaseInCardComponent
implements IVVideoComponentContainer
{
//
//#region Props ...
readonly Spinners = Object.assign(XSpinner, {});
readonly ButtonTypes = Object.assign(XButtonType, {});
readonly Properties = getFieldNames(this.videoPropertyProvider);
//#endregion
//
//#region Constructor ...
constructor(
public zone: NgZone,
@Inject(X_CONFIG)
public config: XConfig,
public element: ElementRef,
public renderer: Renderer2,
public ruler: ViewportRuler,
public sharedService: SharedService,
public managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
protected propertyProvider: XComponentCardContainer,
protected videoPropertyProvider: VVideoComponentContainer
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService,
propertyProvider
);
}
//#endregion
//
//#region LifeCycles ...
async afterViewInit() {
super.afterViewInit();
//
// Initialize defaults
this.video.nativeElement.volume = this.volume;
this.video.nativeElement.playbackRate = this.playbackRate;
//
await this.handleAutoPlay();
//
// Handle Loading State ...
if (
!this.loading &&
this.hasDataSource() &&
(this.preload === 'auto' || this.preload === 'metadata')
) {
await this.handleLoadingState(true);
}
//
// Attach Event Listener for Detect Fullscreen Mode Changed ...
window.addEventListener('resize', this.windowResizeEventListener);
//
// Detecting Devices States and Capabilities ...
//
this.hasFrontCamera = true;
this.hasBackCamera = false;
this.canSwitchCameras = false;
//
this.subscribeActionProvider();
this.handlePrepareForImageView();
this.handleSourceObjectRecieved();
this.handlePrepareCallingActions();
this.handlePrepareVolumeChangerForm();
this.sendAction(this.ProvidedActions.Inited);
}
async onDestroy(): Promise<void> {
super.onDestroy();
//
if (this.hideTimeout) {
window.clearTimeout(this.hideTimeout);
}
//
// Remove Full Screen Recognizer Event Listener ...
window.removeEventListener('resize', this.windowResizeEventListener);
//
// Stop Playing ...
if (this.isPlaying) {
await this.togglePlay();
}
//
// Fullscreen Mode ...
if (this.fullscreenMode) {
this.toggleFullscreen();
}
//
// Destroy src Object if Exists ...
if (!isNullOrUndefined(this.srcObject)) {
//
this.srcObject.getTracks().forEach((track) => track.stop());
this.srcObject = undefined;
}
//
// Destory Screen Share if it's Exists ...
if (!isNullOrUndefined(this.screenShareStream)) {
//
this.screenShareStream.getTracks().forEach((track) => track.stop());
this.screenShareStream = null;
}
//
// Destroy Strem if Exists ...
if (!isNullOrUndefined(this.srcObject)) {
//
this.srcObject.getTracks().forEach((track) => track.stop());
this.srcObject = null;
}
//
// Destroy Second Video Stream if Exists ...
if (!isNullOrUndefined(this.pictureInPictureStream)) {
//
this.pictureInPictureStream.getTracks().forEach((track) => track.stop());
this.pictureInPictureStream = null;
}
//
// Unsubscribe Action Provider ...
this.unsubscribeActionProvider();
}
async onChange(changeKeys: string[]): Promise<void> {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Default Property Handlers ...
if (this.hasChild(changeKeys)) {
this.validateChangedProperties(changeKeys);
}
//
// Loading ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('loading'));
if (isChanged) {
}
//
// Ui Disabled ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('uiDisabled'));
if (isChanged) {
}
//
// Title ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('title'));
if (isChanged) {
applyChanges = true;
this.cardTitle = this.title;
}
//
// Poster Changed ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('poster'));
if (isChanged) {
//
const isOnlyImage =
!isNullOrEmptyString(this.poster) &&
isNullOrEmptyString(this.src) &&
isNullOrUndefined(this.srcObject);
if (isOnlyImage) {
//
applyChanges = true;
await this.handlePrepareForImageView();
}
}
//
// Calling Actions ...
isChanged = changeKeys.includes(
nameof<VVideoComponent>('showCallingActions')
);
if (isChanged) {
this.handlePrepareCallingActions();
}
//
// srcObject ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('srcObject'));
if (isChanged) {
//
if (!isNullOrUndefined(this.srcObject)) {
this.handleSourceObjectRecieved();
}
}
//
// Volume Change Control ...
isChanged = changeKeys.includes(
nameof<VVideoComponent>('showVolumeChangerControl')
);
if (isChanged && !!this.showVolumeChangerControl) {
//
applyChanges = true;
this.handlePrepareVolumeChangerForm();
}
//
// Action Provider ...
isChanged = changeKeys.includes(nameof<VVideoComponent>('actionProvider'));
if (isChanged) {
//
if (isNullOrUndefined(this.actionProvider)) {
this.actionProvider = new Subject<VVideoActionModel>();
}
//
if (!isNullOrUndefined(this.actionProvider)) {
this.subscribeActionProvider();
}
}
//
if (applyChanges) {
this.handleChangeDetection();
}
//
this.sendAction(this.ProvidedActions.PropertyChanged, changeKeys);
}
//#endregion
//
//#region Properties ...
/**
* Retrieve Component State ...
*
* @returns
*/
getState() {
//
let result: IVVideoState = {};
//
const keys = Object.keys(result);
keys.forEach((k) => {
//
const value = getPropertyValue(this, k);
setPropertyValue(result, k, value);
});
//
return result;
}
/**
* Set Component State ...
* only Apply not Null or Undefined Values per Keys ...
*
* @param state
* @returns
*/
setState(state: IVVideoState) {
//
if (isNullOrUndefined(state)) {
return;
}
//
let applyChanges = false;
const keys = Object.keys(state);
keys.forEach((k) => {
//
const value = (state as any)[k];
if (!isNullOrUndefined(value)) {
//
applyChanges = true;
setPropertyValue(this, k, value);
}
});
//
if (applyChanges) {
this.handleChangeDetection();
}
}
/**
* Reading Flags ...
*/
getFlags() {
//
var result: IVVideoFlags = {
muted: this.muted,
isInCall: this.isInCall,
isPlaying: this.isPlaying,
micEnabled: this.micEnabled,
videoEnabled: this.videoEnabled,
hasBackCamera: this.hasBackCamera,
hasFrontCamera: this.hasFrontCamera,
fullscreenMode: this.fullscreenMode,
isScreenShared: this.isScreenShared,
canSwitchCameras: this.canSwitchCameras,
currentUsingMediaDevice: this.currentUsingMediaDevice,
};
//
return result;
}
/**
* Set Flags ...
*
* @param flags
* @returns
*/
setFlags(flags: IVVideoFlags) {
//
let applyChange = false;
//
if (isNullOrUndefined(flags)) {
return;
}
//
const keys = Object.keys(flags);
if (!this.hasChild(keys)) {
return;
}
//
keys.forEach((k) => {
//
const value = (flags as any)[k];
if (!isNullOrUndefined(value)) {
//
applyChange = true;
setPropertyValue(this, k, value);
}
});
//
if (applyChange) {
this.handleChangeDetection();
}
}
/**
* Retrieve All Setted Properties ...
*
* @returns
*/
getProperties() {
//
let result: Partial<IVVideoComponentContainer> = {};
//
const keys = Object.keys(this.Properties);
keys.forEach((k) => {
//
const value = getPropertyValue(this, k);
setPropertyValue(result, k, value);
});
//
return result;
}
/**
* Set Provided Properties ...
*
* @param properties
*/
setProperties(properties: Partial<IVVideoComponentContainer>) {
//
let applyChanges = false;
const keys = Object.keys(properties);
keys.forEach((k) => {
//
const value = getPropertyValue(properties, k);
if (!isNullOrUndefined(value) && !applyChanges) {
applyChanges = true;
}
const defaultValue = getPropertyValue(this.videoPropertyProvider, k);
const applyedValue = !isNullOrUndefined(value) ? value : defaultValue;
setPropertyValue(this, k, applyedValue);
});
//
if (applyChanges) {
this.handleChangeDetection();
}
}
//#endregion
//
//
// Implementing Separation of Concerns ...
//
//
//
//#region Views ...
/**
* Video Element ...
*/
@ViewChild('videoRef')
video!: ElementRef<HTMLVideoElement>;
/**
* Picture in Picture Video Element ...
*/
@ViewChild('secondVideo')
secondVideo!: ElementRef<HTMLVideoElement>;
/**
* Picture in Picture Video Container Element ...
*/
@ViewChild('pictureInPictureContainer', { read: CdkDrag })
pictureInPictureCDKDrag!: CdkDrag;
/**
* Picture in Picture Video Container Element ...
*/
@ViewChild('pictureInPictureContainer')
pictureInPictureContainer!: ElementRef<HTMLDivElement>;
/**
* Speech Recognition Container ...
*/
@ViewChild('speechRecognitionContainer')
speechRecognitionContainer!: ElementRef<HTMLDivElement>;
//#endregion
//
//#region Common ...
// These Props Doesnt Exists on Property Provider ...
//
@Input()
src = '';
@Input()
poster = '';
@Input()
title = '';
@Input()
preload: 'auto' | 'metadata' | 'none' = 'metadata';
@Input()
crossOrigin?: 'anonymous' | 'use-credentials';
@Input()
srcObject: MediaStream | null | undefined = undefined;
//#endregion
//
//#region Card ...
@Input()
isInPage: XStandardType<boolean> = this.videoPropertyProvider.isInPage;
@Input()
wrapWithCard: XStandardType<boolean> =
this.videoPropertyProvider.wrapWithCard;
@Input()
cardCssClass: XStandardType<XOneOrManyType<string>> =
this.videoPropertyProvider.cardCssClass;
@Input()
cardColor: XStandardType<XColorIdentifier> =
this.videoPropertyProvider.cardColor;
@Input()
cardForegroundColor: XStandardType<boolean> =
this.videoPropertyProvider.cardForegroundColor;
@Input()
cardTitle: XStandardType<XResourceIdentifier> =
this.videoPropertyProvider.cardTitle;
@Input()
cardSubTitle: XStandardType<XResourceIdentifier> =
this.videoPropertyProvider.cardSubTitle;
@Input()
cardShowFooter: XStandardType<boolean> =
this.videoPropertyProvider.cardShowFooter;
@Input()
cardShowActions: XStandardType<boolean> =
this.videoPropertyProvider.cardShowActions;
//#endregion
//
//#region Component Styles ...
/**
* Providing CSS Classes ...
*/
@Input()
cssClass: Array<string> = this.videoPropertyProvider.cssClass;
/**
* Auto Play Media ...
*/
@Input()
autoPlay: boolean = this.videoPropertyProvider.autoPlay;
/**
* Continues Play Media ...
*/
@Input()
continuesPlay: boolean = this.videoPropertyProvider.continuesPlay;
/**
* Specified Loading Spinner Type ...
*/
@Input()
loadingSpinner: XSpinnerIdentifier =
this.videoPropertyProvider.loadingSpinner;
/**
* Specified Loading Spinner Color ...
*/
@Input()
loadingSpinnerColor: XColorIdentifier =
this.videoPropertyProvider.loadingSpinnerColor;
/**
* Show Video Title ...
*/
@Input()
showTitle: boolean = this.videoPropertyProvider.showTitle;
//#endregion
//
//#region Video ...
/**
* Show Play Pause Toggle Button on Center of Screen ...
*/
@Input()
showPlayPauseToggler: boolean =
this.videoPropertyProvider.showPlayPauseToggler;
/**
* Show Video Progress Bar
* including Seeking Bar and Video Time ...
*/
@Input()
showProgressBar: boolean = this.videoPropertyProvider.showProgressBar;
/**
* Show Vide Seeking Bar ...
*/
@Input()
showSeekingBar: boolean = this.videoPropertyProvider.showSeekingBar;
/**
* Steps to Move Forard/Rewind ...
*/
@Input()
seekingStep: number = this.videoPropertyProvider.seekingStep;
/**
* Show Vide Time ...
*/
@Input()
showVideoTime: boolean = this.videoPropertyProvider.showVideoTime;
/**
* Show Video Actions ...
*/
@Input()
showActions: boolean = this.videoPropertyProvider.showActions;
/**
* Enable Handling Keyboard Events ...
*/
@Input()
enableKeyboardEvents: boolean =
this.videoPropertyProvider.enableKeyboardEvents;
/**
* Show Calling Actions ...
*/
@Input()
showCallingActions: boolean = this.videoPropertyProvider.showCallingActions;
/**
* Video Duration ...
*/
duration = 0;
/**
* Current Video Time ...
*/
currentTime = 0;
/**
* Current Loaded Percent ...
*/
currentPercent = 0;
/**
* Buffered Percent ...
*/
bufferedPercent = 0;
/**
* Calling Actions Items ...
*/
callingActions: Array<XSimpleActionBarItem> = [];
/**
* Seeking Media Relative to Current Time ...
*
* @param deltaSeconds
*/
seekRelative(deltaSeconds: number): void {
//
const el = this.video.nativeElement;
el.currentTime = Math.min(
this.duration,
Math.max(0, el.currentTime + deltaSeconds)
);
}
/**
* Seeking Media based on Where user Clicked on Progressbar ...
*
* @param ev
*/
seekByClick(ev: MouseEvent): void {
//
if (this.loading || this.uiDisabled) {
return;
}
//
const track = ev.currentTarget as HTMLElement;
const rect = track.getBoundingClientRect();
const pct = (ev.clientX - rect.left) / rect.width;
const el = this.video.nativeElement;
el.currentTime = pct * this.duration;
}
//#endregion
//
//#region Controls ...
//
controlsHidden = false;
private hideTimeout?: number;
//
//#region Play / Pause Control ...
/**
* Show Play/Pause Control on Video Actions ...
*/
@Input()
showPlayPauseControl: boolean =
this.videoPropertyProvider.showPlayPauseControl;
/**
* Play Control Resource ID ...
*/
@Input()
playControlTitle: XResourceIdentifier =
this.videoPropertyProvider.playControlTitle;
/**
* Play Control Icon Name ...
*/
@Input()
playControlIcon: XIconIdentifier = this.videoPropertyProvider.playControlIcon;
/**
* Pause Control Resource ID ...
*/
@Input()
pauseControlTitle: XResourceIdentifier =
this.videoPropertyProvider.pauseControlTitle;
/**
* Pause Control Icon Name ...
*/
@Input()
pauseControlIcon: XIconIdentifier =
this.videoPropertyProvider.pauseControlIcon;
/**
* Play / Pause Flag ...
*/
isPlaying = false;
/**
* Format Video Time ...
*
* @param seconds
* @returns
*/
formatVideoTime(seconds: number): string {
//
if (!isFinite(seconds)) {
return '00:00';
}
//
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
const hh = h > 0 ? `${h}:` : '';
const mm = h > 0 ? String(m).padStart(2, '0') : String(m);
const ss = String(s).padStart(2, '0');
//
let result = `${hh}${mm}:${ss}`;
result = this.applyLocale(result);
//
return result;
}
/**
* Get Play / Pause Toggle Icon ...
*/
getPlayPauseIcon() {
return this.isPlaying ? this.pauseControlIcon : this.playControlIcon;
}
/**
* Get Play / Pause Toggle Flag ...
*/
getPlayPauseTitle() {
return this.isPlaying ? this.pauseControlTitle : this.playControlTitle;
}
/**
* Handling Auto Play ...
*
* @returns
*/
async handleAutoPlay() {
//
if (
!this.autoPlay ||
this.isPlaying ||
!this.hasDataSource() ||
isNullOrUndefined(this.video)
) {
return;
}
//
await this.togglePlay();
}
/**
* Toggle Video Play/Pause ...
*/
async togglePlay(): Promise<void> {
//
const el = this.video.nativeElement;
if (el.paused) {
//
try {
//
await el.play();
this.isPlaying = true;
this.startRecognition();
this.sendAction(this.ProvidedActions.Played);
this.handlePrepareCallingActions();
} catch {
// Autoplay blocked or error
this.isPlaying = false;
this.stopRecognition();
this.sendAction(this.ProvidedActions.Paused);
this.handlePrepareCallingActions();
}
} else {
//
el.pause();
this.isPlaying = false;
this.stopRecognition();
this.sendAction(this.ProvidedActions.Paused);
this.handlePrepareCallingActions();
}
}
//#endregion
//
//#region Rewind Control ...
/**
* Show Video Rewind Control ...
*/
@Input()
showRewindControl: boolean = this.videoPropertyProvider.showRewindControl;
/**
* Rewind Control Resource ID ...
*/
@Input()
rewindControlTitle: XResourceIdentifier =
this.videoPropertyProvider.rewindControlTitle;
/**
* Rewind Control Icon Name ...
*/
@Input()
rewindControlIcon: XIconIdentifier =
this.videoPropertyProvider.rewindControlIcon;
//#endregion
//
//#region Forward Control ...
/**
* Show Video Forward Control ...
*/
@Input()
showForwardControl: boolean = this.videoPropertyProvider.showForwardControl;
/**
* Forward Control Resource ID ...
*/
@Input()
forwardControlTitle: XResourceIdentifier =
this.videoPropertyProvider.forwardControlTitle;
/**
* Forward Control Icon Name ...
*/
@Input()
forwardControlIcon: XIconIdentifier =
this.videoPropertyProvider.forwardControlIcon;
//#endregion
//
//#region Mute / UnMute Control ...
/**
* Show Volume Mute/Unmute Toggler Control ...
*/
@Input()
showVolumeTogglerControl: boolean =
this.videoPropertyProvider.showVolumeTogglerControl;
/**
* Unmute Control Resource ID ...
*/
@Input()
unmuteControlTitle: XResourceIdentifier =
this.videoPropertyProvider.unmuteControlTitle;
/**
* Unmute Control Icon Name ...
*/
@Input()
unmuteControlIcon: XIconIdentifier =
this.videoPropertyProvider.unmuteControlIcon;
/**
* Mute Control Resource ID ...
*/
@Input()
muteControlTitle: XResourceIdentifier =
this.videoPropertyProvider.muteControlTitle;
/**
* Mute Control Icon Name ...
*/
@Input()
muteControlIcon: XIconIdentifier = this.videoPropertyProvider.muteControlIcon;
/**
* Current Mute State ...
*/
muted = false;
/**
* Toggle Mute / Unmute ...
*/
toggleMute(): void {
//
const el = this.video.nativeElement;
el.muted = !el.muted;
this.onVolumeChange();
}
/**
* Get Mute / Unmute Toggle Icon ...
*
* @returns
*/
getMuteUnMuteToggleIcon() {
return this.muted ? this.unmuteControlIcon : this.muteControlIcon;
}
/**
* Get Mute / UnMute Toggle Title ...
*
* @returns
*/
getMuteUnMuteToggleTitle() {
return this.muted ? this.unmuteControlTitle : this.muteControlTitle;
}
//#endregion
//
//#region Volume Change Control ...
/**
* Show Volume Changer Control ...
*/
@Input()
showVolumeChangerControl: boolean =
this.videoPropertyProvider.showVolumeChangerControl;
/**
* Volume Changer Control Resource ID ...
*/
@Input()
volumeChangerControlTitle: XResourceIdentifier =
this.videoPropertyProvider.volumeChangerControlTitle;
/**
* Current Volume ...
*/
volume = 1;
/**
* Volume Form Action ...
*/
volumeFormConfig: XSimpleFormConfig<VVideoVolumeSelectModel> = undefined;
/**
* Set Volume ...
*
* @param v
* @returns
*/
setVolume(v: number | null): void {
//
if (v == null) {
return;
}
//
const el = this.video.nativeElement;
el.volume = Math.min(1, Math.max(0, v));
el.muted = el.volume === 0;
this.onVolumeChange();
}
//#endregion
//
//#region Fullscreen Toggler Control ...
/**
* Show Fullscreen Toggler Control ...
*/
@Input()
showFullscreenTogglerControl: boolean =
this.videoPropertyProvider.showFullscreenTogglerControl;
/**
* Fullscreen Control Resource ID ...
*/
@Input()
fullscreenControlTitle: XResourceIdentifier =
this.videoPropertyProvider.fullscreenControlTitle;
/**
* Fullscreen Control Icon Name ...
*/
@Input()
fullscreenControlIcon: XIconIdentifier =
this.videoPropertyProvider.fullscreenControlIcon;
/**
* Unfullscreen Control Resource ID ...
*/
@Input()
unfullscreenControlTitle: XResourceIdentifier =
this.videoPropertyProvider.unfullscreenControlTitle;
/**
* Unfullscreen Control Icon Name ...
*/
@Input()
unfullscreenControlIcon: XIconIdentifier =
this.videoPropertyProvider.unfullscreenControlIcon;
/**
* Full Screen Model Flag ...
*/
fullscreenMode = false;
/**
* Handle Change fullscreenmode flag when user manually close the fullscreen mode ...
* @param event
*/
windowResizeEventListener: (event: Event) => void = (event) => {
//
const doc: any = document;
const isFullScreen = !isNullOrUndefined(doc.fullscreenElement);
const isChanged = !!this.fullscreenMode && !isFullScreen;
if (isChanged) {
//
this.fullscreenMode = false;
this.sendAction(this.ProvidedActions.Defaultscreened);
if (
this.enablePictureInPicture &&
!isNullOrUndefined(this.pictureInPictureContainer)
) {
//
this.pictureInPictureContainer.nativeElement.style.width = '';
this.pictureInPictureContainer.nativeElement.style.height = '';
this.pictureInPictureContainer.nativeElement.style.transform = '';
this.pictureInPictureCDKDrag.reset();
}
this.handleChangeDetection();
}
};
/**
* Toggle Media Fullscreen / Default Screen ...
*/
toggleFullscreen(): void {
//
const container =
(this.video.nativeElement.parentElement as HTMLElement) ||
this.video.nativeElement;
const doc: any = document;
const el: any = container;
//
if (!doc.fullscreenElement && el.requestFullscreen) {
//
try {
//
el.requestFullscreen();
this.fullscreenMode = true;
this.sendAction(this.ProvidedActions.Fullscreened);
} catch {
this.fullscreenMode = false;
}
} else if (doc.exitFullscreen) {
//
try {
//
doc.exitFullscreen();
this.fullscreenMode = false;
if (
this.enablePictureInPicture &&
!isNullOrUndefined(this.pictureInPictureContainer)
) {
//
this.pictureInPictureContainer.nativeElement.style.width = '';
this.pictureInPictureContainer.nativeElement.style.height = '';
this.pictureInPictureContainer.nativeElement.style.transform = '';
this.pictureInPictureCDKDrag.reset();
//
this.detectChanges();
}
this.sendAction(this.ProvidedActions.Defaultscreened);
} catch {
this.fullscreenMode = true;
}
}
}
/**
* Get Fullscreen Toggle Icon ...
*
* @returns
*/
getFullscreenToggleIcon() {
return this.fullscreenMode
? this.unfullscreenControlIcon
: this.fullscreenControlIcon;
}
/**
* Get Fullscreen Toggle Title ...
*
* @returns
*/
getFullscreenToggleTitle() {
//
return this.fullscreenMode
? this.unfullscreenControlTitle
: this.fullscreenControlTitle;
}
//#endregion
//
//#region Playback Rate(s) Control ...
/**
* Show Current Playback Rate ...
*/
@Input()
showCurrentPlaybackRate: boolean =
this.videoPropertyProvider.showCurrentPlaybackRate;
/**
* Show Playback Rates Control ...
*/
@Input()
showPlaybackRatesControl: boolean =
this.videoPropertyProvider.showPlaybackRatesControl;
/**
* Current Playback Rate ...
*/
playbackRate = 1;
/**
* Allowed Playback Rates ...
*/
playbackRates = [0.5, 0.75, 1, 1.25, 1.5, 2];
/**
* Set Playback Rate ...
*
* @param rate
*/
setPlaybackRate(rate: number): void {
//
const el = this.video.nativeElement;
this.playbackRate = rate;
el.playbackRate = rate;
//
this.detectChanges();
}
/**
* Select Media Playback Rates ...
*
* @param event
* @returns
*/
async selectPlaybackRates(event: Event) {
//
if (isNullOrUndefined(event)) {
return;
}
//
console.log('s');
//
let options: XPopoverOption[] = [];
//
// Prepare Context Menu ...
options = this.playbackRates.map((pr) => {
//
return {
id: `PlayBackRates_${pr}`,
title: ` ${pr} `,
color: this.ColorNames.Dark,
slot: 'start',
handler: () => {
this.setPlaybackRate(pr);
},
} as XPopoverOption;
});
//
// Present Context Menu ...
await this.popoverService.presentContextMenu(options, event);
}
/**
* Preparing Playback Rates Title ...
*
* @returns
*/
getPlaybackRatesTitle() {
//
let result = `${this.playbackRate}x`;
//
return this.applyLocale(result);
}
//#endregion
//
//#region Camera Switch Control ...
/**
* Show Camera Switch if Device Supported ...
*/
@Input()
showCameraSwitchControl: boolean =
this.videoPropertyProvider.showCameraSwitchControl;
/**
* Front Camera Control Resource ID ...
*/
@Input()
frontCameraTitle: XResourceIdentifier =
this.videoPropertyProvider.frontCameraTitle;
/**
* Front Camera Control Icon Name ...
*/
@Input()
frontCameraIcon: XIconIdentifier = this.videoPropertyProvider.frontCameraIcon;
/**
* Back Camera Control Resource ID ...
*/
@Input()
backCameraTitle: XResourceIdentifier =
this.videoPropertyProvider.backCameraTitle;
/**
* Back Camera Control Icon Name ...
*/
@Input()
backCameraIcon: XIconIdentifier = this.videoPropertyProvider.backCameraIcon;
/**
* Has Back Camera ...
*/
hasBackCamera = false;
/**
* Has Front Camera ...
*/
hasFrontCamera = false;
/**
* Check Can Switch Camera or not ...
*/
canSwitchCameras = false;
/**
* Current Using Camera Facing Mode Device ...
*/
currentUsingMediaDevice: XCameraFacing = XCameraFacing.User;
/**
* Switch Media Device ...
*
* @param type
* @returns
*/
switchMediaDevice(type: XCameraFacing) {
//
if (!this.canSwitchCameras || isNullOrUndefined(type) || this.currentUsingMediaDevice === type) {
return;
}
//
this.sendAction(this.ProvidedActions.RequestSwitchCamera, type);
}
//#endregion
//
//#region Call Control ...
/**
* Show Call Control ...
* Request/End ...
*/
@Input()
showCallControl: boolean = this.videoPropertyProvider.showCallControl;
/**
* Call Request Control Resource ID ...
*/
@Input()
callRequestControlTitle: XResourceIdentifier =
this.videoPropertyProvider.callRequestControlTitle;
/**
* Call Request Control Icon Name ...
*/
@Input()
callRequestControlIcon: XIconIdentifier =
this.videoPropertyProvider.callRequestControlIcon;
/**
* Call End Control Resource ID ...
*/
@Input()
callEndControlTitle: XResourceIdentifier =
this.videoPropertyProvider.callEndControlTitle;
/**
* Cal End Control Icon Name ...
*/
@Input()
callEndControlIcon: XIconIdentifier =
this.videoPropertyProvider.callEndControlIcon;
/**
* Is In Call Flag ...
*/
isInCall = false;
/**
* Send Call Request ...
*/
requestCall() {
//
// Validate ...
if (this.isInCall || this.hasDataSource()) {
return;
}
//
this.isInCall = false;
this.sendAction(this.ProvidedActions.RequestCall);
}
/**
* End Active Call ...
*/
endCall() {
//
if (!this.isInCall || !this.hasDataSource()) {
return;
}
//
this.sendAction(this.ProvidedActions.EndCall);
}
//#endregion
//
//#region Video Call Control ...
/**
* Show Video Call Control ...
*/
@Input()
showVideoCallControl: boolean =
this.videoPropertyProvider.showVideoCallControl;
/**
* Enable Video Control Resource ID ...
*/
@Input()
enableVideoControlTitle: XResourceIdentifier =
this.videoPropertyProvider.enableVideoControlTitle;
/**
* Enable Video Control Icon Name ...
*/
@Input()
enableVideoControlIcon: XIconIdentifier =
this.videoPropertyProvider.enableVideoControlIcon;
/**
* Disable Video Control Resource ID ...
*/
@Input()
disableVideoControlTitle: XResourceIdentifier =
this.videoPropertyProvider.disableVideoControlTitle;
/**
* Disable Video Control Icon ...
*/
@Input()
disableVideoControlIcon: XIconIdentifier =
this.videoPropertyProvider.disableVideoControlIcon;
/**
* Video Enabled Flag ...
*/
videoEnabled = true;
/**
* Toggle Video ...
*/
toggleVideo() {
//
if (isNullOrUndefined(this.srcObject)) {
return;
}
//
if (this.videoEnabled) {
//
this.videoEnabled = false;
this.srcObject.getVideoTracks().forEach((track) => {
track.enabled = false;
});
} else {
//
this.videoEnabled = true;
this.srcObject.getVideoTracks().forEach((track) => {
track.enabled = true;
});
}
//
this.handleChangeDetection();
this.sendAction(this.ProvidedActions.VideoStateChanged, this.videoEnabled);
}
//#endregion
//
//#region Microphone Control ...
/**
* Show Microphone Control ...
*/
@Input()
showMicorphoneControl: boolean =
this.videoPropertyProvider.showMicorphoneControl;
/**
* Enable Microphone Control Resource ID ...
*/
@Input()
enableMicrophoneControlTitle: XResourceIdentifier =
this.videoPropertyProvider.enableMicrophoneControlTitle;
/**
* Enable Microphone Control Icon Name ...
*/
@Input()
enableMicrophoneControlIcon: XIconIdentifier =
this.videoPropertyProvider.enableMicrophoneControlIcon;
/**
* Disable Microphone Control Resource ID ...
*/
@Input()
disableMicrophoneControlTitle: XResourceIdentifier =
this.videoPropertyProvider.disableMicrophoneControlTitle;
/**
* Disable Microphone Control Icon ...
*/
@Input()
disableMicrophoneControlIcon: XIconIdentifier =
this.videoPropertyProvider.disableMicrophoneControlIcon;
/**
* Microphone is Enabled Flag ...
*/
micEnabled = true;
/**
* Toggle Microphone ...
*
* @returns
*/
toggleMic() {
//
if (isNullOrUndefined(this.srcObject)) {
return;
}
//
if (this.micEnabled) {
//
this.micEnabled = false;
this.srcObject.getAudioTracks().forEach((track) => {
track.enabled = false;
});
} else {
//
this.micEnabled = true;
this.srcObject.getAudioTracks().forEach((track) => {
track.enabled = true;
});
}
//
this.handleChangeDetection();
this.sendAction(this.ProvidedActions.MicStateChanged, this.micEnabled);
}
//#endregion
//
//#region ScreenShare Control ...
/**
* Show Share Screen Control ...
*/
@Input()
showShareScreenControl: boolean =
this.videoPropertyProvider.showShareScreenControl;
/**
* Share Screen Start Control Resource ID ...
*/
@Input()
startShareScreenControlTitle: XResourceIdentifier =
this.videoPropertyProvider.startShareScreenControlTitle;
/**
* Share Screen Start Control Icon Name ...
*/
@Input()
startShareScreenControlIcon: XIconIdentifier =
this.videoPropertyProvider.startShareScreenControlIcon;
/**
* Share Screen Stop Control Resource ID ...
*/
@Input()
stopShareScreenControlTitle: XResourceIdentifier =
this.videoPropertyProvider.stopShareScreenControlTitle;
/**
* Share Screen Stop Icon Name ...
*/
@Input()
stopShareScreenControlIcon: XIconIdentifier =
this.videoPropertyProvider.stopShareScreenControlIcon;
/**
* Is on Screen Shared Mode ...
*/
isScreenShared = false;
/**
* Screen Share Mode Media Stream ...
*/
screenShareStream: MediaStream = undefined;
/**
* Stop Screen Sharing ...
*/
stopScreenShare() {
//
if (!this.isScreenShared) {
return;
}
//
this.screenShareStream.getTracks().forEach((track) => track.stop());
this.screenShareStream = undefined;
//
// Handle Screen Share Stop ...
this.isScreenShared = false;
this.sendAction(
this.ProvidedActions.ScreenShareStateChanged,
this.isScreenShared
);
}
/**
* Start Screen Sharing ...
*
* @returns
*/
async startScreenShare() {
//
if (this.isScreenShared) {
return;
}
//
// Initialize Screen Share Stream ...
if (isNullOrUndefined(this.screenShareStream)) {
//
// Access Display Media Stream ...
this.screenShareStream =
await this.sharedService.mediaDevicesService.getDisplayMediaDevice();
//
this.screenShareStream.getVideoTracks()[0].onended = () => {
//
// Force Handle Stop Sharing Screen ...
this.stopScreenShare();
//
// Update Calling Action ...
this.handlePrepareCallingActions();
};
}
//
// Handle Screen Share Stop ...
this.isScreenShared = true;
this.sendAction(
this.ProvidedActions.ScreenShareStateChanged,
this.isScreenShared
);
}
/**
* Toggle Screen Sharing ...
*/
async toggleScreenShare() {
//
if (this.isScreenShared) {
this.stopScreenShare();
} else {
await this.startScreenShare();
}
}
/**
* Request Stop Screen Share ...
*/
async requestStopScreenshare() {
//
if (this.isScreenShared) {
this.sendAction(this.ProvidedActions.RequestStopScreenShare);
}
}
/**
* Request Start Screen Share ...
*/
async requestStartScreenshare() {
//
if (!this.isScreenShared) {
this.sendAction(this.ProvidedActions.RequestStartScreenShare);
}
}
//#endregion
//
//#region Record Control ...
/**
* Show Record Control ...
*/
@Input()
showRecordControl: boolean = this.videoPropertyProvider.showRecordControl;
/**
* Start Record Control Resource ID ...
*/
@Input()
startRecordControlTitle: XResourceIdentifier =
this.videoPropertyProvider.startRecordControlTitle;
/**
* Start Record Control Icon Name ...
*/
@Input()
startRecordControlIcon: XIconIdentifier =
this.videoPropertyProvider.startRecordControlIcon;
/**
* Stop Record Control Resource ID ...
*/
@Input()
stopRecordControlTitle: XResourceIdentifier =
this.videoPropertyProvider.stopRecordControlTitle;
/**
* Stop Record Control Icon Name ...
*/
@Input()
stopRecordControlIcon: XIconIdentifier =
this.videoPropertyProvider.stopRecordControlIcon;
/**
* Recording Timer Color ...
*/
@Input()
recordingTimerColor: XColorIdentifier =
this.videoPropertyProvider.recordingTimerColor;
/**
* Is Recording Flag ...
*/
isRecording = false;
/**
* Media Recorder Class ...
*/
mediaRecorder: any = null;
/**
* Recorded Chuncs ...
*/
recordedChunks: Array<any> = [];
/**
* Recording Elappsed Time String ...
*/
recordingTimeElappsed: string = '';
/**
* Recording Start Time ...
*/
startRecordingTime: number = undefined;
/**
* Recording Started Timer Intervale ...
*/
recordingIntervale: NodeJS.Timeout = undefined;
/**
* Start Recording ...
*
* @returns
*/
startRecord() {
//
// Prevent if Recorder Control not Supported ...
if (
!this.isPlaying ||
!this.showRecordControl ||
isNullOrUndefined(this.video)
) {
return;
}
//
// Initialize Media Recorder ...
if (isNullOrUndefined(this.mediaRecorder)) {
//
const stream = (this.video.nativeElement as any).captureStream();
if (!isNullOrUndefined(stream)) {
//
this.mediaRecorder = new (MediaRecorder as MediaRecorderType)(stream, {
mimeType: 'video/webm',
});
//
if (!isNullOrUndefined(this.mediaRecorder)) {
//
// Filling Recorded Chunks ...
this.mediaRecorder.ondataavailable = (event: any) => {
//
if (event.data.size > 0) {
this.recordedChunks.push(event.data);
}
};
//
// Attach Stop Listener ...
this.mediaRecorder.onstop = () => {
//
// Destroy Intervale ...
clearInterval(this.recordingIntervale);
this.recordingIntervale = undefined;
//
const blob = new Blob(this.recordedChunks, { type: 'video/webm' });
const fileName =
generateUuid(this.config.appInstanceName) +
XVideoFileExtensions.Webm;
downloadArrayBufferedFile(fileName, blob);
};
}
}
}
//
if (isNullOrUndefined(this.mediaRecorder)) {
return;
}
//
// Check Recording State ...
if (this.isRecording) {
return;
}
//
// Start Recording ...
//
this.isRecording = true;
this.mediaRecorder.start();
this.startRecordingTime = Date.now();
this.recordingIntervale = setInterval(() => {
//
const elapsed = Date.now() - this.startRecordingTime;
this.recordingTimeElappsed = this.formatRecordingTime(elapsed);
//
// Handling apply Changes on Action Bar ...
if (
!isNullOrUndefined(this.callingActions) &&
this.hasChild(this.callingActions)
) {
//
const idx = this.callingActions.findIndex(
(ca) => ca.id === 'RecordingTime'
);
if (idx >= 0) {
//
this.callingActions[idx].title = this.recordingTimeElappsed;
this.callingActionsActionProvider.next({
action: XActionBarAction.Refresh,
});
}
}
}, 1000);
//
this.sendAction(
this.ProvidedActions.RecordingStateChanged,
this.isRecording
);
}
/**
* Stop Recording ...
*
* @returns
*/
stopRecord() {
//
// Validate Recording State ...
if (
!this.isPlaying ||
!this.isRecording ||
!this.showRecordControl ||
isNullOrUndefined(this.mediaRecorder)
) {
return;
}
//
// Handle Stop Recording ...
//
this.isRecording = false;
this.mediaRecorder.stop();
this.recordedChunks = [];
//
this.sendAction(
this.ProvidedActions.RecordingStateChanged,
this.isRecording
);
}
/**
* Toggle Record ...
*
* @returns
*/
toggleRecord() {
//
// Validate ...
if (!this.showRecordControl) {
return;
}
//
if (this.isRecording) {
this.stopRecord();
} else {
this.startRecord();
}
}
/**
* Format Recording Times ...
*
* @param ellapsed
* @returns
*/
private formatRecordingTime(ellapsed: number) {
//
const totalSeconds = Math.floor(ellapsed / 1000);
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
const seconds = String(totalSeconds % 60).padStart(2, '0');
//
const result: string = this.applyLocale(`${minutes}:${seconds}`);
return result;
}
//#endregion
//#endregion
//
//#region Abilities ...
//
//#region Speech Recognition ...
/**
* Enable Speech Recognition if Supported ...
*/
@Input()
enableSpeechRecognition: boolean =
this.videoPropertyProvider.enableSpeechRecognition;
/**
* Speech Recognition Language ...
*/
@Input()
speechRecognitionLanguage: string =
this.videoPropertyProvider.speechRecognitionLanguage;
/**
* Speech Recognition Transcripted Text Colors ...
*/
@Input()
speechRecognitionColor: XColorIdentifier =
this.videoPropertyProvider.speechRecognitionColor;
/**
* Speech Recognition Flag ...
*/
speechRecognizerEnabled = false;
/**
* Speech Recognizer Class ...
*/
speechRecognizer: any = undefined;
/**
* Stop Speech Recognizer if provided ...
*
* @returns
*/
stopRecognition() {
//
if (
!this.enableSpeechRecognition ||
isNullOrUndefined(this.speechRecognizer)
) {
return;
}
//
this.speechRecognizer.stop();
this.speechRecognizerEnabled = false;
this.sendAction(
this.ProvidedActions.SpeechRecognitionStateChanged,
this.speechRecognizerEnabled
);
}
/**
* Start Speech Recognizer if provided ...
*
* @returns
*/
startRecognition() {
//
if (!this.enableSpeechRecognition) {
return;
}
//
// Initialize Speech Recognizer ...
if (isNullOrUndefined(this.speechRecognizer)) {
//
// Normalize Recognition Language ...
if (isNullOrEmptyString(this.speechRecognitionLanguage)) {
this.speechRecognitionLanguage = this.managerService.currentLocale;
}
//
// Initialize speech recognition
const windowAny = window as any;
this.speechRecognizer = new (windowAny.SpeechRecognition ||
windowAny.webkitSpeechRecognition)();
if (isNullOrUndefined(this.speechRecognizer)) {
return;
}
//
// Setting Speech Recognizer Properties ...
this.speechRecognizer.continuous = true;
this.speechRecognizer.interimResults = true;
this.speechRecognizer.lang = this.speechRecognitionLanguage;
//
// Attach On Result Event Listener ...
this.speechRecognizer.onresult = (event: any) => {
//
let text = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
text += event.results[i][0].transcript;
}
//
this.speechRecognitionContainer.nativeElement.innerText = text;
this.detectChanges();
};
//
}
//
this.speechRecognizer.start();
this.speechRecognizerEnabled = true;
this.sendAction(
this.ProvidedActions.SpeechRecognitionStateChanged,
this.speechRecognizerEnabled
);
}
/**
* Toggle Speech Recognizer if provided ...
*
* @returns
*/
toggleRecognition() {
//
if (
isNullOrUndefined(this.speechRecognizer) ||
!this.enableSpeechRecognition
) {
return;
}
//
if (this.speechRecognizerEnabled) {
this.stopRecognition();
} else {
this.startRecognition();
}
}
//#endregion
//
//#region Picture in Picture ...
/**
* Enable Picture in Picture Mode ...
*/
@Input()
enablePictureInPicture: boolean =
this.videoPropertyProvider.enablePictureInPicture;
/**
* Picture In Picture Element Source Url ...
*/
@Input()
pictureInPictureSrc: string = this.videoPropertyProvider.pictureInPictureSrc;
/**
* Picutre in Picture Element Source Strem ...
*/
@Input()
pictureInPictureStream: MediaStream =
this.videoPropertyProvider.pictureInPictureStream;
//#endregion
//#endregion
//
//#region Action Providers ...
/**
* Card Action Provider ...
*/
cardActionProvider = new Subject<XCardActionModel>();
/**
* Volume Form Action Provider ...
*/
volumeFormActionProvider = new Subject<XFormActionModel>();
/**
* Calling Actions Action Provider ...
*/
callingActionsActionProvider = new Subject<XActionBarActionModel>();
//#endregion
//
//#region Handlers ...
//
//#region Video Element Event Handlers ...
/**
* Handle Can Play State ...
*/
async onCanPlay() {
//
if (this.loading) {
await this.handleLoadingState(false);
}
//
// Handle Auto Play ...
await this.handleAutoPlay();
}
/**
* Calls when Video Element Start Loadng ...
*/
async onWaiting() {
//
if (this.hasDataSource()) {
await this.handleLoadingState(true);
}
}
/**
* Calls When Data Loaded ...
*/
async onLoadedData() {
//
if (this.hasDataSource() && this.loading) {
await this.handleLoadingState(false);
}
}
/**
* Calls When Metadata Loaded ...
*/
async onLoadedMetadata(): Promise<void> {
//
const el = this.video.nativeElement;
this.duration = el.duration || 0;
//
if (this.loading) {
await this.handleLoadingState(false);
}
//
// Set Audio Default Poster if Provided Data is Audio ...
const isAudio = !this.isVideoProvided();
if (isAudio && isNullOrEmptyString(this.poster)) {
//
this.poster = defaultAudioPoster;
this.detectChanges();
}
}
/**
* Calls When Time Update Video ...
*/
onTimeUpdate(): void {
//
const el = this.video.nativeElement;
this.currentTime = el.currentTime || 0;
this.currentPercent = this.duration
? (this.currentTime / this.duration) * 100
: 0;
//
this.sendAction(this.ProvidedActions.TimeUpdated, {
currentTime: this.currentTime,
currentPercent: this.currentPercent,
duration: this.duration,
});
}
/**
* Calls When Video Progressing ...
*/
onProgress(): void {
//
const el = this.video.nativeElement;
//
// Use last buffered range as reference
const buf = el.buffered;
if (buf && buf.length) {
//
const end = buf.end(buf.length - 1);
this.bufferedPercent = this.duration ? (end / this.duration) * 100 : 0;
//
this.sendAction(this.ProvidedActions.Progressed, {
end,
bufferedPercent: this.bufferedPercent,
});
}
}
/**
* Calls When Volume Changes ...
*/
onVolumeChange(): void {
//
const el = this.video.nativeElement;
this.volume = el.volume;
this.muted = el.muted || this.volume === 0;
//
this.handleChangeDetection();
//
this.sendAction(this.ProvidedActions.VolumeChanged, this.volume);
//
if (this.muted) {
this.sendAction(this.ProvidedActions.Muted);
}
}
/**
* Calls When Play Ended ...
*/
onEnded(): void {
//
this.isPlaying = false;
this.currentPercent = 100;
//
if (this.continuesPlay) {
this.togglePlay();
}
//
this.sendAction(this.ProvidedActions.Ended);
}
//#endregion
/**
* Detect Loaded Data is Video or Not ...
*
* @returns
*/
isVideoProvided() {
//
let result = false;
//
if (
!isNullOrUndefined(this.video) &&
!isNullOrUndefined(this.video.nativeElement)
) {
//
const el = this.video.nativeElement;
result = el.videoWidth > 0 && el.videoHeight > 0;
}
//
return result;
}
/**
* Check Has Data Source ...
*
* @returns
*/
hasDataSource() {
return !isNullOrEmptyString(this.src) || !isNullOrUndefined(this.srcObject);
}
/**
* Show Action Controls Temparory ...
*/
showControlsTemporarily() {
//
this.controlsHidden = false;
if (this.hideTimeout) {
window.clearTimeout(this.hideTimeout);
}
this.hideTimeout = window.setTimeout(
() => (this.controlsHidden = true),
2000
);
}
/**
* Handle Keyboard Events to Controlling Over Video Element ...
*
* @param event
* @returns
*/
@HostListener('window:keydown', ['$event'])
onKeydown(event: KeyboardEvent) {
//
let applyChange = false;
if (!this.enableKeyboardEvents) {
event.preventDefault();
return;
}
//
// Space: play/pause, arrows: seek, up/down: volume
switch (event.key) {
case ' ':
case 'k':
//
applyChange = true;
event.preventDefault();
this.togglePlay();
break;
case 'ArrowLeft':
//
applyChange = true;
event.preventDefault();
this.seekRelative(-1 * this.seekingStep);
break;
case 'ArrowRight':
//
applyChange = true;
event.preventDefault();
this.seekRelative(1 * this.seekingStep);
break;
case 'ArrowUp':
//
applyChange = true;
event.preventDefault();
this.setVolume(Math.min(1, this.volume + 0.05));
break;
case 'ArrowDown':
//
applyChange = true;
event.preventDefault();
this.setVolume(Math.max(0, this.volume - 0.05));
break;
case 'm':
//
applyChange = true;
event.preventDefault();
this.toggleMute();
break;
case 'f':
//
applyChange = true;
event.preventDefault();
this.toggleFullscreen();
break;
}
//
if (applyChange) {
this.showControlsTemporarily();
}
}
/**
* Handle Loading Change ...
*
* @param state
* @returns
*/
async handleLoadingState(state: boolean) {
//
// Validate new State ...
if (this.loading === state) {
return;
}
//
// Set Loading ...
await this.setLoadingState(state);
//
// TODO ...
// Implement Required Preparation Actions here ...
//
// Notify Change State ...
this.sendAction(this.ProvidedActions.LoadingStateChanged, this.loading);
}
/**
* Handle Change Detection ...
*/
handleChangeDetection() {
//
this.callingActionsActionProvider.next({
action: XActionBarAction.Refresh,
});
this.cardActionProvider.next({ action: XCardAction.Refresh });
//
this.handlePrepareCallingActions();
this.handlePrepareVolumeChangerForm();
this.volumeFormActionProvider.next({ action: XFormBaseAction.Refresh });
//
this.detectChanges();
}
/**
* Validate Changed Properties ...
*
* @param keys
*/
private validateChangedProperties(keys: string[]) {
//
let applyChanges = false;
const allowedKeys = Object.keys(this.Properties);
keys.forEach((k) => {
//
const isExists = allowedKeys.includes(k);
if (isExists) {
//
const value = (this as any)[k];
const defaultValue = (this.videoPropertyProvider as any)[k];
if (isNullOrUndefined(value)) {
//
applyChanges = true;
setPropertyValue(this, k, defaultValue);
}
}
});
//
if (applyChanges) {
this.detectChanges();
}
}
/**
* Handle Volume Change Form Config ...
*/
private handlePrepareVolumeChangerForm() {
//
if (!this.showVolumeChangerControl) {
return;
}
//
this.volumeFormConfig = undefined;
this.detectChanges();
//
this.volumeFormConfig = {
name: 'Volume_Form',
model: {
volume: this.volume,
},
controls: [
//
// Volume Control ...
{
index: 0,
propName: 'volume',
type: {
type: XFormControlType.Slider,
config: {
min: 0,
max: 1,
step: 0.05,
thumbLabel: false,
vertical: this.showCallingActions,
formatter: (value: number) =>
this.applyLocale((value * 100).toFixed()),
} as XFormSliderControlConfig,
},
appearance: {
label: '',
disabled: this.muted,
cssClass: ['v-video-volume-slider'],
tooltip: this.volumeChangerControlTitle,
appearance: XFormControlAppearance.None,
},
eventHandlers: {
valueChanged: (event: XFormControlValueChangeEventModel) => {
//
// Check Value Must Different to Current ...
if (event.value !== this.volume) {
//
// Handle Change Volume ...
this.setVolume(event.value);
}
},
},
},
],
};
//
this.detectChanges();
}
/**
* Prepare Component States as an Image Viewer ...
* Note: for this purpose you had to only Provides poster input ...
*
* @returns
*/
private async handlePrepareForImageView() {
//
// Validate State ...
if (
!isNullOrUndefined(this.srcObject) ||
!isNullOrEmptyString(this.src) ||
isNullOrEmptyString(this.poster)
) {
return;
}
//
if (this.loading) {
//
await this.setLoadingState(false);
this.sendAction(this.ProvidedActions.LoadingStateChanged, false);
}
//
this.showActions = false;
this.showProgressBar = false;
this.showCallingActions = false;
//
this.handleChangeDetection();
}
/**
* Preparing Calling Actions ...
*/
private handlePrepareCallingActions() {
//
if (!this.showCallingActions) {
return;
}
//
this.callingActions = [];
//
// Preparing Calling Actions ...
//
const actionBarItems: Array<XSimpleActionBarItem> = [];
//
// Connect / Disconnect Call ...
let startIDX = 1;
//
// Start Slot ...
//
// Call Request / End ...
if (this.showCallControl) {
//
// End Call ...
if (this.isInCall) {
//
actionBarItems.push({
...defaultCallEndActionBar,
index: startIDX,
icon: this.callEndControlIcon,
title: this.callEndControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
//
// Request Call ...
else {
//
actionBarItems.push({
...defaultCallRequestActionBar,
index: startIDX,
icon: this.callRequestControlIcon,
title: this.callRequestControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
}
//
// Mute / UnMute ...
if (this.showVolumeTogglerControl) {
//
// UnMute ...
if (this.muted) {
//
actionBarItems.push({
...defaultUnMuteActionBar,
index: startIDX,
icon: this.unmuteControlIcon,
title: this.unmuteControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
//
// Unmute ...
else {
//
actionBarItems.push({
...defaultMuteActionBar,
index: startIDX,
icon: this.muteControlIcon,
title: this.muteControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
}
//
// Camera Switch Button ...
if (this.showCameraSwitchControl && this.canSwitchCameras) {
//
// Check Current Camera In Used ...
const isFrontCameraInUse =
this.currentUsingMediaDevice === XCameraFacing.User;
const isBackCameraInUse =
this.currentUsingMediaDevice === XCameraFacing.Environment;
//
// Switch to Back Camera ...
if (isFrontCameraInUse) {
//
actionBarItems.push({
...defaultSwitchToBackCameraActionBar,
index: startIDX,
icon: this.backCameraIcon,
title: this.backCameraTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
//
// Switch to Front Camera ...
if (isBackCameraInUse) {
//
actionBarItems.push({
...defaultSwitchToFrontCameraActionBar,
index: startIDX,
icon: this.frontCameraIcon,
title: this.frontCameraTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
}
//
// Screen Share ...
if (this.showShareScreenControl) {
//
// Disable ...
if (this.isScreenShared) {
//
actionBarItems.push({
...defaultStopScreenShareActionBar,
index: startIDX,
icon: this.stopShareScreenControlIcon,
title: this.stopShareScreenControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
//
// Enable ...
else {
//
actionBarItems.push({
...defaultStartScreenShareActionBar,
index: startIDX,
icon: this.startShareScreenControlIcon,
title: this.startShareScreenControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
startIDX++;
}
}
//
// End Slot ...
let endIDX = 1; // startIDX;
//
// Recorder ...
if (this.showRecordControl) {
//
// Recording Time ...
if (this.isRecording) {
//
actionBarItems.push({
...defaultStopRecordActionBar,
index: endIDX,
icon: undefined,
onlyIcon: false,
id: 'RecordingTime',
slot: XActionBarItemSlot.END,
cssClass: ['v-recording-timer'],
color: this.recordingTimerColor,
title: this.recordingTimeElappsed,
titleColor: this.ColorNames.Warning,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
//
// Stop Record ...
if (this.isRecording) {
//
actionBarItems.push({
...defaultStopRecordActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.stopRecordControlIcon,
title: this.stopRecordControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
//
// Start Record ...
else {
//
actionBarItems.push({
...defaultStartRecordActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.startRecordControlIcon,
title: this.startRecordControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
}
//
// Video On / Video off ...
if (this.showVideoCallControl) {
//
// Video Off ...
if (this.videoEnabled) {
//
actionBarItems.push({
...defaultVideoOffActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.disableVideoControlIcon,
title: this.disableVideoControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
//
// Video On ...
else {
//
actionBarItems.push({
...defaultVideoOnActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.enableVideoControlIcon,
title: this.enableVideoControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
}
//
// Mic On / Mic off ...
if (this.showMicorphoneControl) {
//
// Mic Off ...
if (this.micEnabled) {
//
actionBarItems.push({
...defaultMicOffActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.disableMicrophoneControlIcon,
title: this.disableMicrophoneControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
//
// Mic On ...
else {
//
actionBarItems.push({
...defaultMicOnActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.enableMicrophoneControlIcon,
title: this.enableMicrophoneControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
}
//
// Full Screen Toggler ...
if (this.showFullscreenTogglerControl) {
//
// Defaultscreen ...
if (this.fullscreenMode) {
//
actionBarItems.push({
...defaultUnfullscreenActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.unfullscreenControlIcon,
title: this.unfullscreenControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
//
// Fullscreen ...
else {
//
actionBarItems.push({
...defaultFullscreenActionBar,
index: endIDX,
slot: XActionBarItemSlot.END,
icon: this.fullscreenControlIcon,
title: this.fullscreenControlTitle,
disabled: this.loading || this.uiDisabled,
});
//
endIDX++;
}
}
//
this.callingActions = [...actionBarItems];
//
this.detectChanges();
}
/**
* When Source Object Recieved ...
*
* @returns
*/
private handleSourceObjectRecieved() {
//
// Validate ...
if (isNullOrUndefined(this.video) || isNullOrUndefined(this.srcObject)) {
return;
}
//
// Here we are going to Handle Microphone ...
const audioTracks = this.srcObject.getAudioTracks();
const hasAudioTrack =
!isNullOrUndefined(audioTracks) && this.hasChild(audioTracks);
if (hasAudioTrack) {
this.micEnabled = true;
}
//
// Check Video Tracks ...
const videoTracks = this.srcObject.getVideoTracks();
const hasVideoTrack =
!isNullOrUndefined(videoTracks) && this.hasChild(videoTracks);
if (hasVideoTrack) {
this.videoEnabled = true;
}
//
this.isInCall = this.micEnabled || this.videoEnabled;
//
this.handleChangeDetection();
}
/**
* Handle Action Bar Calling Actions ...
*
* @param event
*/
async handleCallingActionFired(event: XActionBarActionFiredModel) {
//
console.log('Action Bar Action Fired: ', event);
//
let isKnownAction = false;
//
//#region Request Call ...
isKnownAction = event.action === defaultCallRequestActionBar.id;
if (isKnownAction) {
this.requestCall();
}
//#endregion
//
//#region End Call ...
isKnownAction = event.action === defaultCallEndActionBar.id;
if (isKnownAction) {
this.endCall();
}
//#endregion
//
//#region Mute ...
isKnownAction = event.action === defaultMuteActionBar.id;
if (isKnownAction) {
//
if (!this.muted) {
this.toggleMute();
}
}
//#endregion
//
//#region UnMute ...
isKnownAction = event.action === defaultUnMuteActionBar.id;
if (isKnownAction) {
//
if (this.muted) {
this.toggleMute();
}
}
//#endregion
//
//#region Fullscreen ...
isKnownAction = event.action === defaultFullscreenActionBar.id;
if (isKnownAction) {
//
if (!this.fullscreenMode) {
this.toggleFullscreen();
}
}
//#endregion
//
//#region UnFullscreen ...
isKnownAction = event.action === defaultUnfullscreenActionBar.id;
if (isKnownAction) {
//
if (this.fullscreenMode) {
this.toggleFullscreen();
}
}
//#endregion
//
//#region MicOn ...
isKnownAction = event.action === defaultMicOnActionBar.id;
if (isKnownAction) {
//
if (!this.micEnabled) {
this.toggleMic();
}
}
//#endregion
//
//#region MicOff ...
isKnownAction = event.action === defaultMicOffActionBar.id;
if (isKnownAction) {
//
if (this.micEnabled) {
this.toggleMic();
}
}
//#endregion
//
//#region VideoOn ...
isKnownAction = event.action === defaultVideoOnActionBar.id;
if (isKnownAction) {
//
if (!this.videoEnabled) {
this.toggleVideo();
}
}
//#endregion
//
//#region VideoOff ...
isKnownAction = event.action === defaultVideoOffActionBar.id;
if (isKnownAction) {
//
if (this.videoEnabled) {
this.toggleVideo();
}
}
//#endregion
//
//#region Switch To Front Camera ...
isKnownAction = event.action === defaultSwitchToFrontCameraActionBar.id;
if (isKnownAction) {
this.switchMediaDevice(XCameraFacing.User);
}
//#endregion
//
//#region Switch To Back Camera ...
isKnownAction = event.action === defaultSwitchToBackCameraActionBar.id;
if (isKnownAction) {
this.switchMediaDevice(XCameraFacing.Environment);
}
//#endregion
//
//#region Start Recording ...
isKnownAction = event.action === defaultStartRecordActionBar.id;
if (isKnownAction) {
//
if (!this.isRecording) {
this.toggleRecord();
}
}
//#endregion
//
//#region Stop Recording ...
isKnownAction = event.action === defaultStopRecordActionBar.id;
if (isKnownAction) {
//
if (this.isRecording) {
this.toggleRecord();
}
}
//#endregion
//
//#region Start Screen Share ...
isKnownAction = event.action === defaultStartScreenShareActionBar.id;
if (isKnownAction) {
// await this.startScreenShare();
this.requestStartScreenshare();
}
//#endregion
//
//#region Stop Screen Share ...
isKnownAction = event.action === defaultStopScreenShareActionBar.id;
if (isKnownAction) {
// this.stopScreenShare();
this.requestStopScreenshare();
}
//#endregion
//
this.handlePrepareCallingActions();
}
//
//#region Action Provider ...
@Input()
actionProvider: Subject<VVideoActionModel>;
readonly ProvidedActions = Object.assign(VVideoAction, {});
private ACTION_PROVDER_SUBSCRIPTION: Subscription = undefined;
private unsubscribeActionProvider() {
//
if (isNullOrUndefined(this.ACTION_PROVDER_SUBSCRIPTION)) {
return;
}
//
this.ACTION_PROVDER_SUBSCRIPTION.unsubscribe();
this.ACTION_PROVDER_SUBSCRIPTION = undefined;
}
private subscribeActionProvider() {
//
this.unsubscribeActionProvider();
if (isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.ACTION_PROVDER_SUBSCRIPTION = this.actionProvider.subscribe(
(action) => {
//
if (isNullOrUndefined(action) || !isVVideoAction(action.action)) {
return;
}
//
// Handle Actions ...
//
// Refresh ...
// Change Detection ...
if (action.action === this.ProvidedActions.Refresh) {
//
if (!isNullOrUndefined(action.payload) && isNumber(action.payload)) {
//
this.runTimedOut(
() => this.handleChangeDetection(),
toNumber(action.payload)
);
} else {
this.handleChangeDetection();
}
}
//
// Set State ...
else if (action.action === this.ProvidedActions.SetState) {
//
// Validate Payload ...
if (!isNullOrUndefined(action.payload)) {
this.setState(action.payload);
}
}
//
// Set Flags ...
else if (action.action === this.ProvidedActions.SetFlags) {
//
// Validate Payload ...
if (!isNullOrUndefined(action.payload)) {
this.setFlags(action.payload);
}
}
//
// Mute ...
else if (action.action === this.ProvidedActions.Mute) {
//
if (!this.muted) {
this.toggleMute();
}
}
//
// UnMute ...
else if (action.action === this.ProvidedActions.UnMute) {
//
if (this.muted) {
this.toggleMute();
}
}
//
// Play ...
else if (action.action === this.ProvidedActions.Play) {
//
if (!this.isPlaying) {
this.togglePlay();
}
}
//
// Pause ...
else if (action.action === this.ProvidedActions.Pause) {
//
if (this.isPlaying) {
this.togglePlay();
}
}
//
// Rewind ...
else if (action.action === this.ProvidedActions.Rewind) {
this.seekRelative(-1 * this.seekingStep);
}
//
// Forward ...
else if (action.action === this.ProvidedActions.Forward) {
this.seekRelative(1 * this.seekingStep);
}
//
// IncreaseVolume ...
else if (action.action === this.ProvidedActions.IncreaseVolume) {
this.setVolume(Math.min(1, this.volume + 0.05));
}
//
// DecreaseVolume ...
else if (action.action === this.ProvidedActions.DecreaseVolume) {
this.setVolume(Math.max(0, this.volume - 0.05));
}
//
// TogglePlayPause ...
else if (action.action === this.ProvidedActions.TogglePlayPause) {
this.togglePlay();
}
//
// ToggleMuteUnmute ...
else if (action.action === this.ProvidedActions.ToggleMuteUnmute) {
this.toggleMute();
}
//
// ToggleFullDefaultScreen ...
else if (
action.action === this.ProvidedActions.ToggleFullDefaultScreen
) {
this.toggleFullscreen();
}
//
// Start Record ...
else if (action.action === this.ProvidedActions.StartRecord) {
this.startRecord();
}
//
// Stop Record ...
else if (action.action === this.ProvidedActions.StopRecord) {
this.stopRecord();
}
//
// Requests ...
//
// Flags ...
else if (action.action === this.ProvidedActions.RequestFlags) {
//
var flags = this.getFlags();
this.sendAction(this.ProvidedActions.ResponseFlags, flags);
}
//
// State ...
else if (action.action === this.ProvidedActions.RequestState) {
//
var state = this.getState();
this.sendAction(this.ProvidedActions.ResponseState, state);
}
//
// Properties ...
else if (action.action === this.ProvidedActions.RequestProperties) {
//
var props = this.getProperties();
this.sendAction(this.ProvidedActions.ResponseProperties, props);
}
}
);
}
private sendAction(action: VVideoAction, payload?: any) {
//
if (!isVVideoAction(action) || isNullOrUndefined(this.actionProvider)) {
return;
}
//
this.actionProvider.next({ action, payload });
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-video\v-video.configs.ts
export enum VideoResourceIDs {
play = 'play',
mute = 'mute',
pause = 'pause',
unmute = 'unmute',
volume = 'volume',
rewind = 'rewind',
forward = 'forward',
fullscreen = 'fullscreen',
unfullscreen = 'unfullscreen',
change_volume = 'change_volume',
//
// Calling Actions ...
mic_on = 'mic_on',
mic_off = 'mic_off',
video_on = 'video_on',
call_end = 'call_end',
video_off = 'video_off',
stop_record = 'stop_record',
start_record = 'start_record',
call_request = 'call_request',
stop_share_screen = 'stop_share_screen',
start_share_screen = 'start_share_screen',
switch_to_back_camera = 'switch_to_back_camera',
switch_to_front_camera = 'switch_to_front_camera',
}
//
//#region Single Resources ...
//
export const mute_fa = {
id: 'mute',
value: 'سکوت',
};
export const unmute_fa = {
id: 'unmute',
value: 'غیرفعال کردن حالت سکوت',
};
export const play_fa = {
id: 'play',
value: 'پخش',
};
export const pause_fa = {
id: 'pause',
value: 'توقف',
};
export const volume_fa = {
id: 'volume',
value: 'حجم صدا',
};
export const rewind_fa = {
id: 'rewind',
value: 'رو به عقب',
};
export const forward_fa = {
id: 'forward',
value: 'رو به جلو',
};
export const fullscreen_fa = {
id: 'fullscreen',
value: 'نمایش تمام صفحه',
};
export const unfullscreen_fa = {
id: 'unfullscreen',
value: 'نمایش پیش فرض',
};
export const change_volume_fa = {
id: 'change_volume',
value: 'تغییر حجم صدا',
};
//
// Call Actions ...
export const mic_on_fa = {
id: 'mic_on',
value: 'ارسال صدا',
};
export const mic_off_fa = {
id: 'mic_off',
value: 'قطع ارسال صدا',
};
export const video_on_fa = {
id: 'video_on',
value: 'ارسال تصویر',
};
export const video_off_fa = {
id: 'video_off',
value: 'قطع ارسال تصویر',
};
export const stop_record_fa = {
id: 'stop_record',
value: 'اتمام ضبط',
};
export const start_record_fa = {
id: 'start_record',
value: 'ضبط',
};
export const call_end_fa = {
id: 'call_end',
value: 'قطع تماس',
};
export const call_request_fa = {
id: 'call_request',
value: 'برقراری تماس',
};
export const stop_share_screen_fa = {
id: 'stop_share_screen',
value: 'اتمام اشتراک گذاری صفحه',
};
export const start_share_screen_fa = {
id: 'start_share_screen',
value: 'اشتراک گذاری صفحه',
};
export const switch_to_back_camera_fa = {
id: 'switch_to_back_camera',
value: 'فعالسازی دوربین محیط',
};
export const switch_to_front_camera_fa = {
id: 'switch_to_front_camera',
value: 'فعالسازی دوربین مقابل',
};
//
export const mute_en = {
id: 'mute',
value: 'Mute',
};
export const unmute_en = {
id: 'unmute',
value: 'Unmute',
};
export const play_en = {
id: 'play',
value: 'Play',
};
export const pause_en = {
id: 'pause',
value: 'Pause',
};
export const volume_en = {
id: 'volume',
value: 'Volume',
};
export const rewind_en = {
id: 'rewind',
value: 'Rewind',
};
export const forward_en = {
id: 'forward',
value: 'Forward',
};
export const fullscreen_en = {
id: 'fullscreen',
value: 'Fullscreen',
};
export const unfullscreen_en = {
id: 'unfullscreen',
value: 'Default Size',
};
export const change_volume_en = {
id: 'change_volume',
value: 'Change Volume',
};
//
// Call Actions ...
export const mic_on_en = {
id: 'mic_on',
value: 'Mic On',
};
export const mic_off_en = {
id: 'mic_off',
value: 'Mic Off',
};
export const video_on_en = {
id: 'video_on',
value: 'Video On',
};
export const video_off_en = {
id: 'video_off',
value: 'Video Off',
};
export const stop_record_en = {
id: 'stop_record',
value: 'Stop Record',
};
export const start_record_en = {
id: 'start_record',
value: 'Record',
};
export const call_end_en = {
id: 'call_end',
value: 'Call End',
};
export const call_request_en = {
id: 'call_request',
value: 'Call',
};
export const stop_share_screen_en = {
id: 'stop_share_screen',
value: 'Stop Screen Share',
};
export const start_share_screen_en = {
id: 'start_share_screen',
value: 'Start Screen Share',
};
export const switch_to_back_camera_en = {
id: 'switch_to_back_camera',
value: 'Switch to Back Camera',
};
export const switch_to_front_camera_en = {
id: 'switch_to_front_camera',
value: 'Switch to Front Camera',
};
//#endregion
//
export const VideoLocalizationResourceIDs = Object.assign(VideoResourceIDs, {});
//
export const VideoLocalizationResources = {
mute: {
fa: { ...mute_fa },
en: { ...mute_en },
},
play: {
fa: { ...play_fa },
en: { ...play_en },
},
pause: {
fa: { ...pause_fa },
en: { ...pause_en },
},
unmute: {
fa: { ...unmute_fa },
en: { ...unmute_en },
},
volume: {
fa: { ...volume_fa },
en: { ...volume_en },
},
rewind: {
fa: { ...rewind_fa },
en: { ...rewind_en },
},
forward: {
fa: { ...forward_fa },
en: { ...forward_en },
},
fullscreen: {
fa: { ...fullscreen_fa },
en: { ...fullscreen_en },
},
unfullscreen: {
fa: { ...unfullscreen_fa },
en: { ...unfullscreen_en },
},
change_volume: {
fa: { ...change_volume_fa },
en: { ...change_volume_en },
},
//
// Calling Actions ...
mic_on: {
fa: { ...mic_on_fa },
en: { ...mic_on_en },
},
mic_off: {
fa: { ...mic_off_fa },
en: { ...mic_off_en },
},
video_on: {
fa: { ...video_on_fa },
en: { ...video_on_en },
},
video_off: {
fa: { ...video_off_fa },
en: { ...video_off_en },
},
stop_record: {
fa: { ...stop_record_fa },
en: { ...stop_record_en },
},
start_record: {
fa: { ...start_record_fa },
en: { ...start_record_en },
},
call_end: {
fa: { ...call_end_fa },
en: { ...call_end_en },
},
call_request: {
fa: { ...call_request_fa },
en: { ...call_request_en },
},
stop_share_screen: {
fa: { ...stop_share_screen_fa },
en: { ...stop_share_screen_en },
},
start_share_screen: {
fa: { ...start_share_screen_fa },
en: { ...start_share_screen_en },
},
switch_to_back_camera: {
fa: { ...switch_to_back_camera_fa },
en: { ...switch_to_back_camera_en },
},
switch_to_front_camera: {
fa: { ...switch_to_front_camera_fa },
en: { ...switch_to_front_camera_en },
},
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\v-video\v-video.typings.ts
import {
XSpinner,
XStandardType,
XOneOrManyType,
XColorIdentifier,
XSpinnerIdentifier,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
XIconNames,
XIconIdentifier,
XBaseActionModel,
} from 'x-framework-components';
import { Injectable } from '@angular/core';
import { VideoResourceIDs } from './v-video.configs';
import { BooleanInput } from '@angular/cdk/coercion';
import { isValueInEnum } from '../tools/v-base.tools';
import { SharedService } from 'src/app/shared/services/shared.service';
import { XCameraFacing } from 'src/app/shared/services/x-media-devices.service';
/**
* Default Audio Poster ...
*/
export const defaultAudioPoster = '/assets/image/audio-poster-3.gif';
/**
* Volume Changer Form's Model ...
*/
export interface VVideoVolumeSelectModel {
volume: number;
}
//
//#region Action Provider ...
export enum VVideoAction {
//
// Actions ...
/**
* Play Video if Provided ...
*/
Play = 'Play',
/**
* Pause Video if Played ...
*/
Pause = 'Pause',
/**
* Rewind Video ...
*/
Rewind = 'Rewind',
/**
* Forward Video ...
*/
Forward = 'Forward',
/**
* Refresh Component ...
*/
Refresh = 'refresh',
/**
* Mute ...
*/
Mute = 'Mute',
/**
* UnMute ...
*/
UnMute = 'UnMute',
/**
* Increase Volume ...
*/
IncreaseVolume = 'IncreaseVolume',
/**
* Decrease Volume ...
*/
DecreaseVolume = 'DecreaseVolume',
/**
* Toggle Play / Pause Video Action ...
*/
TogglePlayPause = 'TogglePlayPause',
/**
* Toggle Mute / UnMute Action ...
*/
ToggleMuteUnmute = 'ToggleMuteUnmute',
/**
* Request Switch Device Camera by Providing Facing Mode ...
* XCameraFacing:
* User: Request Default Media Device (Front) ...
* Screen: Request Screen Sharing Media Device ...
* Environment: Request Back Camera Device (if Provided) ...
*/
SwitchMediaDevices = 'SwitchMediaDevices',
/**
* Toggle Fullscreen / Defaultscreen ...
*/
ToggleFullDefaultScreen = 'ToggleFullDefaultScreen',
/**
* Request Call ...
*/
RequestCall = 'RequestCall',
/**
* End Call ...
*/
EndCall = 'EndCall',
/**
* Start Media Recording ...
*/
StartRecord = 'StartRecord',
/**
* Stop Media Recording ...
*/
StopRecord = 'StopRecord',
/**
* Request Switch Camera ...
*/
RequestSwitchCamera = 'RequestSwitchCamera',
/**
* Request Start Screen Share ...
*/
RequestStartScreenShare = 'RequestStartScreenShare',
/**
* Request Stop Screen Share ...
*/
RequestStopScreenShare = 'RequestStopScreenShare',
/**
* Set Component State by Providing IVVideoState ...
*/
SetState = 'SetState',
/**
* Set Component Flags by Providing IVVideoFlag ...
*/
SetFlags = 'SetFlags',
//
RequestFlags = 'RequestFlags',
RequestState = 'RequestState',
RequestProperties = 'RequestProperties',
//
ResponseFlags = 'ResponseFlags',
ResponseState = 'ResponseState',
ResponseProperties = 'ResponseProperties',
//
// Events ...
Ended = 'Ended',
Muted = 'Muted',
Inited = 'Inited',
Played = 'Played',
Paused = 'Paused',
Progressed = 'Progressed',
TimeUpdated = 'TimeUpdated',
Fullscreened = 'Fullscreened',
VolumeChanged = 'VolumeChanged',
PropertyChanged = 'PropertyChanged',
Defaultscreened = 'Defaultscreened',
MicStateChanged = 'MicStateChanged',
VideoStateChanged = 'VideoStateChanged',
LoadingStateChanged = 'LoadingStateChanged',
RecordingStateChanged = 'RecordingStateChanged',
ScreenShareStateChanged = 'ScreenShareStateChanged',
SpeechRecognitionStateChanged = 'SpeechRecognitionStateChanged',
}
export type VVideoActionIdentifier = VVideoAction | 'refresh' | string;
export function isVVideoAction(value: string) {
return isValueInEnum(value, Object.assign(VVideoAction, {}));
}
export interface VVideoActionModel extends XBaseActionModel<VVideoAction> {}
//#endregion
//
//#region Medi Recorder ...
export type MediaRecorderType = {
new (stream: MediaStream, options?: MediaRecorderOptions): any;
isTypeSupported?(type: string): boolean;
};
export interface MediaRecorderOptions {
mimeType?: string;
audioBitsPerSecond?: number;
videoBitsPerSecond?: number;
bitsPerSecond?: number;
}
export interface BlobEvent extends Event {
data: Blob;
}
//#endregion
//
//#region VVideo Container ...
export interface IVVideoFlags {
muted?: boolean;
isInCall?: boolean;
isPlaying?: boolean;
micEnabled?: boolean;
videoEnabled?: boolean;
hasBackCamera?: boolean;
hasFrontCamera?: boolean;
fullscreenMode?: boolean;
isScreenShared?: boolean;
canSwitchCameras?: boolean;
currentUsingMediaDevice?: XCameraFacing;
}
/**
* All Available Controls Properties Based on VVideo Component ...
*/
export interface IVVideoControlsContainer {
//
//#region Play / Pause Control ...
/**
* Show Play/Pause Control on Video Actions ...
*/
showPlayPauseControl: boolean;
/**
* Play Control Resource ID ...
*/
playControlTitle: XResourceIdentifier;
/**
* Play Control Icon Name ...
*/
playControlIcon: XIconIdentifier;
/**
* Pause Control Resource ID ...
*/
pauseControlTitle: XResourceIdentifier;
/**
* Pause Control Icon Name ...
*/
pauseControlIcon: XIconIdentifier;
//#endregion
//
//#region Rewind Control ...
/**
* Show Video Rewind Control ...
*/
showRewindControl: boolean;
/**
* Rewind Control Resource ID ...
*/
rewindControlTitle: XResourceIdentifier;
/**
* Rewind Control Icon Name ...
*/
rewindControlIcon: XIconIdentifier;
//#endregion
//
//#region Forward Control ...
/**
* Show Video Forward Control ...
*/
showForwardControl: boolean;
/**
* Forward Control Resource ID ...
*/
forwardControlTitle: XResourceIdentifier;
/**
* Forward Control Icon Name ...
*/
forwardControlIcon: XIconIdentifier;
//#endregion
//
//#region Mute / UnMute Control ...
/**
* Show Volume Mute/Unmute Toggler Control ...
*/
showVolumeTogglerControl: BooleanInput;
/**
* Unmute Control Resource ID ...
*/
unmuteControlTitle: XResourceIdentifier;
/**
* Unmute Control Icon Name ...
*/
unmuteControlIcon: XIconIdentifier;
/**
* Mute Control Resource ID ...
*/
muteControlTitle: XResourceIdentifier;
/**
* Mute Control Icon Name ...
*/
muteControlIcon: XIconIdentifier;
//#endregion
//
//#region Volume Change Control ...
/**
* Show Volume Changer Control ...
*/
showVolumeChangerControl: boolean;
/**
* Volume Changer Control Resource ID ...
*/
volumeChangerControlTitle: XResourceIdentifier;
//#endregion
//
//#region Fullscreen Toggler Control ...
/**
* Show Fullscreen Toggler Control ...
*/
showFullscreenTogglerControl: boolean;
/**
* Fullscreen Control Resource ID ...
*/
fullscreenControlTitle: XResourceIdentifier;
/**
* Fullscreen Control Icon Name ...
*/
fullscreenControlIcon: XIconIdentifier;
/**
* Unfullscreen Control Resource ID ...
*/
unfullscreenControlTitle: XResourceIdentifier;
/**
* Unfullscreen Control Icon Name ...
*/
unfullscreenControlIcon: XIconIdentifier;
//#endregion
//
//#region Playback Rate(s) Control ...
/**
* Show Current Playback Rate ...
*/
showCurrentPlaybackRate: boolean;
/**
* Show Playback Rates Control ...
*/
showPlaybackRatesControl: Boolean;
//#endregion
//
//#region Camera Switch Control ...
/**
* Show Camera Switch if Device Supported ...
*/
showCameraSwitchControl: boolean;
/**
* Front Camera Control Resource ID ...
*/
frontCameraTitle: XResourceIdentifier;
/**
* Front Camera Control Icon Name ...
*/
frontCameraIcon: XIconIdentifier;
/**
* Back Camera Control Resource ID ...
*/
backCameraTitle: XResourceIdentifier;
/**
* Back Camera Control Icon Name ...
*/
backCameraIcon: XIconIdentifier;
//#endregion
//
//#region Call Control ...
/**
* Show Call Control ...
* Request/End ...
*/
showCallControl: boolean;
/**
* Call Request Control Resource ID ...
*/
callRequestControlTitle: XResourceIdentifier;
/**
* Call Request Control Icon Name ...
*/
callRequestControlIcon: XIconIdentifier;
/**
* Call End Control Resource ID ...
*/
callEndControlTitle: XResourceIdentifier;
/**
* Cal End Control Icon Name ...
*/
callEndControlIcon: XIconIdentifier;
//#endregion
//
//#region Video Call Control ...
/**
* Show Video Call Control ...
*/
showVideoCallControl: boolean;
/**
* Enable Video Control Resource ID ...
*/
enableVideoControlTitle: XResourceIdentifier;
/**
* Enable Video Control Icon Name ...
*/
enableVideoControlIcon: XIconIdentifier;
/**
* Disable Video Control Resource ID ...
*/
disableVideoControlTitle: XResourceIdentifier;
/**
* Disable Video Control Icon ...
*/
disableVideoControlIcon: XIconIdentifier;
//#endregion
//
//#region Microphone Control ...
showMicorphoneControl: boolean;
/**
* Enable Microphone Control Resource ID ...
*/
enableMicrophoneControlTitle: XResourceIdentifier;
/**
* Enable Microphone Control Icon Name ...
*/
enableMicrophoneControlIcon: XIconIdentifier;
/**
* Disable Microphone Control Resource ID ...
*/
disableMicrophoneControlTitle: XResourceIdentifier;
/**
* Disable Microphone Control Icon ...
*/
disableMicrophoneControlIcon: XIconIdentifier;
//#endregion
//
//#region ScreenShare Control ...
/**
* Show Share Screen Control ...
*/
showShareScreenControl: boolean;
/**
* Share Screen Start Control Resource ID ...
*/
startShareScreenControlTitle: XResourceIdentifier;
/**
* Share Screen Start Control Icon Name ...
*/
startShareScreenControlIcon: XIconIdentifier;
/**
* Share Screen Stop Control Resource ID ...
*/
stopShareScreenControlTitle: XResourceIdentifier;
/**
* Share Screen Stop Icon Name ...
*/
stopShareScreenControlIcon: XIconIdentifier;
//#endregion
//
//#region Record Control ...
/**
* Show Record Control ...
*/
showRecordControl: boolean;
/**
* Start Record Control Resource ID ...
*/
startRecordControlTitle: XResourceIdentifier;
/**
* Start Record Control Icon Name ...
*/
startRecordControlIcon: XIconIdentifier;
/**
* Stop Record Control Resource ID ...
*/
stopRecordControlTitle: XResourceIdentifier;
/**
* Stop Record Control Icon Name ...
*/
stopRecordControlIcon: XIconIdentifier;
/**
* Recording Timer Color ...
*/
recordingTimerColor: XColorIdentifier;
//#endregion
}
/**
* Abilities of IVVideo Component ...
*/
export interface IVVideoAbilitiesContainer {
//
//#region Speech Recognition ...
/**
* Enable Speech Recognition if Supported ...
*/
enableSpeechRecognition: boolean;
/**
* Speech Recognition Language ...
*/
speechRecognitionLanguage: string;
/**
* Speech Recognition Transcripted Text Colors ...
*/
speechRecognitionColor: XColorIdentifier;
//#endregion
//
//#region Picture in Picture ...
/**
* Enable Picture in Picture Mode ...
*/
enablePictureInPicture: boolean;
/**
* Picture In Picture Element Source Url ...
*/
pictureInPictureSrc: string;
/**
* Picutre in Picture Element Source Strem ...
*/
pictureInPictureStream: MediaStream;
//#endregion
}
/**
* Video Component Card Properties ...
*/
export interface IVVideoCardContainer {
/**
* Specified Component's Card is In Page or not ...
*/
isInPage: XStandardType<boolean>;
/**
* Specified Wrap Component by Card or not ...
*/
wrapWithCard: XStandardType<boolean>;
/**
* Specified Card's Component CSS Classes ...
*/
cardCssClass: XStandardType<XOneOrManyType<string>>;
/**
* Specified Component's Card Color ...
*/
cardColor: XStandardType<XColorIdentifier>;
/**
* Specified Color Applies to Component's Card Foreground or not ...
*/
cardForegroundColor: XStandardType<boolean>;
/**
* Specified Component's Cards Title Resource ...
*/
cardTitle: XStandardType<XResourceIdentifier>;
/**
* Specified Component's Cards SubTitle Resource ...
*/
cardSubTitle: XStandardType<XResourceIdentifier>;
/**
* Specified Component's Card Show Footer or not ...
*/
cardShowFooter: XStandardType<boolean>;
/**
* Specified Component's Card Show Actions or not ...
*/
cardShowActions: XStandardType<boolean>;
}
/**
* Specified Type of VVideo Component State ...
*/
interface VVideoStateType
extends IVVideoControlsContainer,
IVVideoAbilitiesContainer {}
export interface IVVideoState extends Partial<VVideoStateType> {}
/**
* Provides all Available Properties ...
*/
export interface IVVideoComponentContainer
extends IVVideoCardContainer,
IVVideoControlsContainer,
IVVideoAbilitiesContainer {
//
//#region Component Styles ...
/**
* Providing CSS Classes ...
*/
cssClass: Array<string>;
/**
* Auto Play Media ...
*/
autoPlay: boolean;
/**
* Continues Play Media ...
*/
continuesPlay: boolean;
/**
* Specified Loading Spinner Type ...
*/
loadingSpinner: XSpinnerIdentifier;
/**
* Specified Loading Spinner Color ...
*/
loadingSpinnerColor: XColorIdentifier;
/**
* Show Video Title ...
*/
showTitle: boolean;
//#endregion
//
//#region Video ...
/**
* Show Play Pause Toggle Button on Center of Screen ...
*/
showPlayPauseToggler: boolean;
/**
* Show Video Progress Bar
* including Seeking Bar and Video Time ...
*/
showProgressBar: boolean;
/**
* Show Vide Seeking Bar ...
*/
showSeekingBar: boolean;
/**
* Steps to Move Forard/Rewind ...
*/
seekingStep: number;
/**
* Show Vide Time ...
*/
showVideoTime: boolean;
/**
* Show Video Actions ...
*/
showActions: boolean;
/**
* Enable Handling Keyboard Events ...
*/
enableKeyboardEvents: boolean;
/**
* Show Calling Actions ...
*/
showCallingActions: boolean;
//#endregion
//
}
/**
* All Default Property Values Provided Here ...
*/
@Injectable({
providedIn: 'root',
})
export class VVideoComponentContainer
implements Readonly<IVVideoComponentContainer>
{
//
//#region Constructor ...
constructor(private sharedService: SharedService) {}
//#endregion
//
//#region Card ...
/**
* Specified Component's Card is In Page or not ...
*/
readonly isInPage: XStandardType<boolean> = false;
/**
* Specified Wrap Component by Card or not ...
*/
readonly wrapWithCard: XStandardType<boolean> = false;
/**
* Specified Card's Component CSS Classes ...
*/
readonly cardCssClass: XStandardType<XOneOrManyType<string>> = [];
/**
* Specified Component's Card Color ...
*/
readonly cardColor: XStandardType<XColorIdentifier> =
XColorWithBrightness.Dark;
/**
* Specified Color Applies to Component's Card Foreground or not ...
*/
readonly cardForegroundColor: XStandardType<boolean> = false;
/**
* Specified Component's Cards Title Resource ...
*/
readonly cardTitle: XStandardType<XResourceIdentifier> = '';
/**
* Specified Component's Cards SubTitle Resource ...
*/
readonly cardSubTitle: XStandardType<XResourceIdentifier> = '';
/**
* Specified Component's Card Show Footer or not ...
*/
readonly cardShowFooter: XStandardType<boolean> = false;
/**
* Specified Component's Card Show Actions or not ...
*/
readonly cardShowActions: XStandardType<boolean> = false;
//#endregion
//
//#region Component Styles ...
/**
* Providing CSS Classes ...
*/
readonly cssClass: Array<string> = [];
/**
* Auto Play Media ...
*/
readonly autoPlay: boolean = false;
/**
* Continues Play Media ...
*/
readonly continuesPlay: boolean = false;
/**
* Specified Loading Spinner Type ...
*/
readonly loadingSpinner: XSpinnerIdentifier = XSpinner.Circles;
/**
* Specified Loading Spinner Color ...
*/
readonly loadingSpinnerColor: XColorIdentifier = XColorWithBrightness.Warning;
/**
* Show Video Title ...
*/
readonly showTitle: boolean = true;
//#endregion
//
//#region Video ...
/**
* Show Play Pause Toggle Button on Center of Screen ...
*/
readonly showPlayPauseToggler: boolean = false;
/**
* Show Video Progress Bar
* including Seeking Bar and Video Time ...
*/
readonly showProgressBar: boolean = true;
/**
* Show Vide Seeking Bar ...
*/
readonly showSeekingBar: boolean = true;
/**
* Steps to Move Forard/Rewind ...
*/
readonly seekingStep: number = 10;
/**
* Show Vide Time ...
*/
readonly showVideoTime: boolean = true;
/**
* Show Video Actions ...
*/
readonly showActions: boolean = true;
/**
* Enable Handling Keyboard Events ...
*/
readonly enableKeyboardEvents: boolean = false;
/**
* Show Calling Actions ...
*/
readonly showCallingActions: boolean = false;
//#endregion
//
//#region Controls ...
//
//#region Play / Pause Control ...
/**
* Show Play/Pause Control on Video Actions ...
*/
readonly showPlayPauseControl: boolean = true;
/**
* Play Control Resource ID ...
*/
readonly playControlTitle: XResourceIdentifier = VideoResourceIDs.play;
/**
* Play Control Icon Name ...
*/
readonly playControlIcon: XIconIdentifier = XIconNames.play;
/**
* Pause Control Resource ID ...
*/
readonly pauseControlTitle: XResourceIdentifier = VideoResourceIDs.pause;
/**
* Pause Control Icon Name ...
*/
readonly pauseControlIcon: XIconIdentifier = XIconNames.pause;
//#endregion
//
//#region Rewind Control ...
/**
* Show Video Rewind Control ...
*/
readonly showRewindControl: boolean = true;
/**
* Rewind Control Resource ID ...
*/
readonly rewindControlTitle: XResourceIdentifier = VideoResourceIDs.rewind;
/**
* Rewind Control Icon Name ...
*/
readonly rewindControlIcon: XIconIdentifier = XIconNames.fast_rewind;
//#endregion
//
//#region Forward Control ...
/**
* Show Video Forward Control ...
*/
readonly showForwardControl: boolean = true;
/**
* Forward Control Resource ID ...
*/
readonly forwardControlTitle: XResourceIdentifier = VideoResourceIDs.forward;
/**
* Forward Control Icon Name ...
*/
readonly forwardControlIcon: XIconIdentifier = XIconNames.fast_forward;
//#endregion
//
//#region Mute / UnMute Control ...
/**
* Show Volume Mute/Unmute Toggler Control ...
*/
readonly showVolumeTogglerControl: boolean = true;
/**
* Unmute Control Resource ID ...
*/
readonly unmuteControlTitle: XResourceIdentifier = VideoResourceIDs.unmute;
/**
* Unmute Control Icon Name ...
*/
readonly unmuteControlIcon: XIconIdentifier = XIconNames.volume_high;
/**
* Mute Control Resource ID ...
*/
readonly muteControlTitle: XResourceIdentifier = VideoResourceIDs.mute;
/**
* Mute Control Icon Name ...
*/
readonly muteControlIcon: XIconIdentifier = XIconNames.volume_mute;
//#endregion
//
//#region Volume Change Control ...
/**
* Show Volume Changer Control ...
*/
readonly showVolumeChangerControl: boolean = true;
/**
* Volume Changer Control Resource ID ...
*/
readonly volumeChangerControlTitle: XResourceIdentifier =
VideoResourceIDs.change_volume;
//#endregion
//
//#region Fullscreen Toggler Control ...
/**
* Show Fullscreen Toggler Control ...
*/
readonly showFullscreenTogglerControl: boolean = true;
/**
* Fullscreen Control Resource ID ...
*/
readonly fullscreenControlTitle: XResourceIdentifier =
VideoResourceIDs.fullscreen;
/**
* Fullscreen Control Icon Name ...
*/
readonly fullscreenControlIcon: XIconIdentifier = XIconNames.fullscreen;
/**
* Unfullscreen Control Resource ID ...
*/
readonly unfullscreenControlTitle: XResourceIdentifier =
VideoResourceIDs.unfullscreen;
/**
* Unfullscreen Control Icon Name ...
*/
readonly unfullscreenControlIcon: XIconIdentifier =
XIconNames.close_fullscreen;
//#endregion
//
//#region Playback Rate(s) Control ...
/**
* Show Current Playback Rate ...
*/
readonly showCurrentPlaybackRate: boolean = true;
/**
* Show Playback Rates Control ...
*/
readonly showPlaybackRatesControl: boolean = true;
//#endregion
//
//#region Camera Switch Control ...
/**
* Show Camera Switch if Device Supported ...
*/
readonly showCameraSwitchControl: boolean = true;
/**
* Front Camera Control Resource ID ...
*/
readonly frontCameraTitle: XResourceIdentifier =
VideoResourceIDs.switch_to_front_camera;
/**
* Front Camera Control Icon Name ...
*/
readonly frontCameraIcon: XIconIdentifier = XIconNames.video_camera_front;
/**
* Back Camera Control Resource ID ...
*/
readonly backCameraTitle: XResourceIdentifier =
VideoResourceIDs.switch_to_back_camera;
/**
* Back Camera Control Icon Name ...
*/
readonly backCameraIcon: XIconIdentifier = XIconNames.video_camera_back;
//#endregion
//
//#region Call Control ...
/**
* Show Call Control ...
* Request/End ...
*/
readonly showCallControl: boolean = true;
/**
* Call Request Control Resource ID ...
*/
readonly callRequestControlTitle: XResourceIdentifier =
VideoResourceIDs.call_request;
/**
* Call Request Control Icon Name ...
*/
readonly callRequestControlIcon: XIconIdentifier = XIconNames.phone_enabled;
/**
* Call End Control Resource ID ...
*/
readonly callEndControlTitle: XResourceIdentifier = VideoResourceIDs.call_end;
/**
* Cal End Control Icon Name ...
*/
readonly callEndControlIcon: XIconIdentifier = XIconNames.phone_disabled;
//#endregion
//
//#region Video Call Control ...
/**
* Show Video Call Control ...
*/
readonly showVideoCallControl: boolean = true;
/**
* Enable Video Control Resource ID ...
*/
readonly enableVideoControlTitle: XResourceIdentifier =
VideoResourceIDs.video_on;
/**
* Enable Video Control Icon Name ...
*/
readonly enableVideoControlIcon: XIconIdentifier = XIconNames.videocam;
/**
* Disable Video Control Resource ID ...
*/
readonly disableVideoControlTitle: XResourceIdentifier =
VideoResourceIDs.video_off;
/**
* Disable Video Control Icon ...
*/
readonly disableVideoControlIcon: XIconIdentifier =
XIconNames.video_camera_front_off;
//#endregion
//
//#region Microphone Control ...
readonly showMicorphoneControl: boolean = true;
/**
* Enable Microphone Control Resource ID ...
*/
readonly enableMicrophoneControlTitle: XResourceIdentifier =
VideoResourceIDs.mic_on;
/**
* Enable Microphone Control Icon Name ...
*/
readonly enableMicrophoneControlIcon: XIconIdentifier = XIconNames.mic;
/**
* Disable Microphone Control Resource ID ...
*/
readonly disableMicrophoneControlTitle: XResourceIdentifier =
VideoResourceIDs.mic_off;
/**
* Disable Microphone Control Icon ...
*/
readonly disableMicrophoneControlIcon: XIconIdentifier = XIconNames.mic_off;
//#endregion
//
//#region ScreenShare Control ...
/**
* Show Share Screen Control ...
*/
readonly showShareScreenControl: boolean = true;
/**
* Share Screen Start Control Resource ID ...
*/
readonly startShareScreenControlTitle: XResourceIdentifier =
VideoResourceIDs.start_share_screen;
/**
* Share Screen Start Control Icon Name ...
*/
readonly startShareScreenControlIcon: XIconIdentifier =
XIconNames.screen_share;
/**
* Share Screen Stop Control Resource ID ...
*/
readonly stopShareScreenControlTitle: XResourceIdentifier =
VideoResourceIDs.stop_share_screen;
/**
* Share Screen Stop Icon Name ...
*/
readonly stopShareScreenControlIcon: XIconIdentifier =
XIconNames.stop_screen_share;
//#endregion
//
//#region Record Control ...
/**
* Show Record Control ...
*/
readonly showRecordControl: boolean = false;
/**
* Start Record Control Resource ID ...
*/
readonly startRecordControlTitle: XResourceIdentifier =
VideoResourceIDs.start_record;
/**
* Start Record Control Icon Name ...
*/
readonly startRecordControlIcon: XIconIdentifier = XIconNames.screen_record;
/**
* Stop Record Control Resource ID ...
*/
readonly stopRecordControlTitle: XResourceIdentifier =
VideoResourceIDs.stop_record;
/**
* Stop Record Control Icon Name ...
*/
readonly stopRecordControlIcon: XIconIdentifier = XIconNames.stop;
/**
* Recording Timer Color ...
*/
recordingTimerColor: XColorIdentifier = XColorWithBrightness.Warning;
//#endregion
//#endregion
//
//#region Abilities ...
//
//#region Speech Recognition ...
/**
* Enable Speech Recognition if Supported ...
*/
readonly enableSpeechRecognition: boolean = false;
/**
* Speech Recognition Language ...
*/
readonly speechRecognitionLanguage: string =
this.sharedService.managerService.currentLocale;
/**
* Speech Recognition Transcripted Text Colors ...
*/
readonly speechRecognitionColor: XColorIdentifier =
XColorWithBrightness.Warning;
//#endregion
//
//#region Picture in Picture ...
/**
* Enable Picture in Picture Mode ...
*/
readonly enablePictureInPicture: boolean = false;
/**
* Picture In Picture Element Source Url ...
*/
readonly pictureInPictureSrc: string = '';
/**
* Picutre in Picture Element Source Strem ...
*/
readonly pictureInPictureStream: MediaStream = undefined;
//#endregion
//#endregion
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar.configs.ts
import { XIconNames } from 'x-framework-components';
import { isValueInEnum } from '../tools/v-base.tools';
import { XSimplePage } from '../typings/simple.page.typings';
import { getXException, XException, XExceptionIDs } from 'x-framework-core';
//
//#region Localization Configuration ...
export enum WebinarResourceIDs {
webinar = 'webinar',
webinars = 'webinars',
close_webinar = 'close_webinar',
webinar_owner = 'webinar_owner',
webinar_joiner = 'webinar_joiner',
new_webinar_created = 'new_webinar_created',
webinars_page_title = 'webinars_page_title',
visitor_joined_message = 'visitor_joined_message',
visitor_leaved_message = 'visitor_leaved_message',
webinar_closed_message = 'webinar_closed_message',
webinars_page_description = 'webinars_page_description',
webinars_explorer_page_title = 'webinars_explorer_page_title',
webinars_management_page_title = 'webinars_management_page_title',
webinars_explorer_page_description = 'webinars_explorer_page_description',
webinars_management_page_description = 'webinars_management_page_description',
}
//
//#region Single Resources ...
//
//#region webinar ...
export const webinar_fa = {
id: 'webinar',
value: 'وبینار',
};
export const webinar_en = {
id: 'webinar',
value: 'Webinar',
};
//#endregion
//
//#region webinars ...
export const webinars_fa = {
id: 'webinars',
value: 'وبینارها',
};
export const webinars_en = {
id: 'webinars',
value: 'Webinars',
};
//#endregion
//
//#region close_webinar ...
export const close_webinar_fa = {
id: 'close_webinar',
value: 'بستن وبینار',
};
export const close_webinar_en = {
id: 'close_webinar',
value: 'Close Webinar',
};
//#endregion
//
//#region webinar_owner
export const webinar_owner_fa = {
id: 'webinar_owner',
value: 'برگزار کننده',
};
export const webinar_owner_en = {
id: 'webinar_owner',
value: 'Presenter',
};
//#endregion
//
//#region webinar_joiner
export const webinar_joiner_fa = {
id: 'webinar_joiner',
value: 'مشاهده کننده',
};
export const webinar_joiner_en = {
id: 'webinar_joiner',
value: 'Viewer',
};
//#endregion
//
//#region new_webinar_created ...
export const new_webinar_created_fa = {
id: 'new_webinar_created',
value: 'وبینار جدیدی ایجاد شده است ...',
};
export const new_webinar_created_en = {
id: 'new_webinar_created',
value: 'new Webinar Created ...',
};
//#endregion
//
// #region webinars_page_title ...
export const webinars_page_title_fa = {
id: 'webinars_page_title',
value: 'وبینار',
};
export const webinars_page_title_en = {
id: 'webinars_page_title',
value: 'Webinars',
};
//#endregion
//
//#region visitor_joined_message ...
export const visitor_joined_message_fa = {
id: 'visitor_joined_message',
value: '${TMP1}& به وبینار پیوست',
};
export const visitor_joined_message_en = {
id: 'visitor_joined_message',
value: '${TMP1}& Joined to Webinar',
};
//#endregion
//
//#region visitor_leaved_message ...
export const visitor_leaved_message_fa = {
id: 'visitor_leaved_message',
value: '${TMP1}& وبینار را ترک کرد',
};
export const visitor_leaved_message_en = {
id: 'visitor_leaved_message',
value: '${TMP1}& Leaved Webina',
};
//#endregion
//
//#region webinar_closed_message ...
export const webinar_closed_message_fa = {
id: 'webinar_closed_message',
value: 'وبینار بسته شده است ...',
};
export const webinar_closed_message_en = {
id: 'webinar_closed_message',
value: 'Webinar is Closed ...',
};
//#endregion
//
//#region webinars_page_description ...
export const webinars_page_description_fa = {
id: 'webinars_page_description',
value: 'ابزارهای مدیریت وبینارها',
};
export const webinars_page_description_en = {
id: 'webinars_page_description',
value: 'Tools for Webinars ',
};
//#endregion
//
//#region webinars_management_page_title ...
export const webinars_management_page_title_fa = {
id: 'webinars_management_page_title',
value: 'مدیریت وبینارها',
};
export const webinars_management_page_title_en = {
id: 'webinars_management_page_title',
value: 'Webinars Management',
};
//#endregion
//
//#region webinars_management_page_description
export const webinars_management_page_description_fa = {
id: 'webinars_management_page_description',
value: 'مدیریت وبینارها',
};
export const webinars_management_page_description_en = {
id: 'webinars_management_page_description',
value: 'Webinars Management',
};
//#endregion
//
//#region webinars_explorer_page_title ...
export const webinars_explorer_page_title_fa = {
id: 'webinars_explorer_page_title',
value: 'وبینارها',
};
export const webinars_explorer_page_title_en = {
id: 'webinars_explorer_page_title',
value: 'Webinars',
};
//#endregion
//
//#region webinars_explorer_page_description ...
export const webinars_explorer_page_description_fa = {
id: 'webinars_explorer_page_description',
value: 'مرورگر وبینار',
};
export const webinars_explorer_page_description_en = {
id: 'webinars_explorer_page_description',
value: 'Webinar Explorer',
};
//#endregion
//#endregion
//
export const WebinarLocalizationResourceIDs = Object.assign(WebinarResourceIDs, {});
//
export const WebinarLocalizationResources = {
//
webinar: {
fa: { ...webinar_fa },
en: { ...webinar_en },
},
webinars: {
fa: { ...webinars_fa },
en: { ...webinars_en },
},
close_webinar: {
fa: { ...close_webinar_fa },
en: { ...close_webinar_en },
},
webinar_owner: {
fa: { ...webinar_owner_fa },
en: { ...webinar_owner_en },
},
webinar_joiner: {
fa: { ...webinar_joiner_fa },
en: { ...webinar_joiner_en },
},
new_webinar_created: {
fa: { ...new_webinar_created_fa },
en: { ...new_webinar_created_en },
},
webinars_page_title: {
fa: { ...webinars_page_title_fa },
en: { ...webinars_page_title_en },
},
visitor_joined_message: {
fa: { ...visitor_joined_message_fa },
en: { ...visitor_joined_message_en },
},
visitor_leaved_message: {
fa: { ...visitor_leaved_message_fa },
en: { ...visitor_leaved_message_en },
},
webinar_closed_message: {
fa: { ...webinar_closed_message_fa },
en: { ...webinar_closed_message_en },
},
webinars_page_description: {
fa: { ...webinars_page_description_fa },
en: { ...webinars_page_description_en },
},
//
webinars_management_page_title: {
fa: { ...webinars_management_page_title_fa },
en: { ...webinars_management_page_title_en },
},
webinars_management_page_description: {
fa: { ...webinars_management_page_description_fa },
en: { ...webinars_management_page_description_en },
},
webinars_explorer_page_title: {
fa: { ...webinars_explorer_page_title_fa },
en: { ...webinars_explorer_page_title_en },
},
webinars_explorer_page_description: {
fa: { ...webinars_explorer_page_description_fa },
en: { ...webinars_explorer_page_description_en },
},
};
//#endregion
//
//#region Page Configuration ...
//
export enum WebinarRoutes {
Default = 'webinars',
WebinarsExplorer = 'webinars-explorer',
WebinarsManagement = 'webinars-management',
}
//
export const Webinars: XSimplePage = {
id: `${WebinarRoutes.Default}`,
name: `${WebinarRoutes.Default}`,
title: WebinarResourceIDs.webinars_page_title,
description: WebinarResourceIDs.webinars_page_description,
baseRoute: `${WebinarRoutes.Default}`,
route: ['/', `${WebinarRoutes.Default}`],
icon: XIconNames.collapse,
childs: [
//
//#region Webinars Management ...
{
id: `${WebinarRoutes.WebinarsManagement}`,
name: `${WebinarRoutes.WebinarsManagement}`,
title: WebinarResourceIDs.webinars_management_page_title,
description: WebinarResourceIDs.webinars_management_page_description,
baseRoute: `${WebinarRoutes.WebinarsManagement}`,
route: [
'/',
`${WebinarRoutes.Default}`,
`${WebinarRoutes.WebinarsManagement}`,
],
icon: XIconNames.live_tv,
},
//#endregion
//
//#region Webinars Explorer ...
{
id: `${WebinarRoutes.WebinarsExplorer}`,
name: `${WebinarRoutes.WebinarsExplorer}`,
title: WebinarResourceIDs.webinars_explorer_page_title,
description: WebinarResourceIDs.webinars_explorer_page_description,
baseRoute: `${WebinarRoutes.WebinarsExplorer}`,
route: [
'/',
`${WebinarRoutes.Default}`,
`${WebinarRoutes.WebinarsExplorer}`,
],
icon: XIconNames.live_tv,
},
//#endregion
],
};
//#endregion
//
//#region Exceptions ...
export enum XWebinarExceptionIDs {
CallEndError = 'CallEndError',
OpenWebinarError = 'OpenWebinarError',
JoinWebinarError = 'JoinWebinarError',
LocalStreamError = 'LocalStreamError',
UnKnownVisitorError = 'UnKnownVisitorError',
ServiceConnectError = 'ServiceConnectError',
RTCPeerConnectionError = 'RTCPeerConnectionError',
}
export type XWebinarExceptionIdentifier =
| XWebinarExceptionIDs
| keyof typeof XWebinarExceptionIDs
| string;
export function isXWebinarExceptionIDs(value: string) {
return isValueInEnum(value, Object.assign(XWebinarExceptionIDs, {}));
}
export function getXWebinarException(id: XWebinarExceptionIdentifier) {
//
// Prepare Default Exception ...
let result: XException = getXException(XExceptionIDs.ActionFailed);
//
if (isXWebinarExceptionIDs(id)) {
//
if (id === XWebinarExceptionIDs.CallEndError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.OpenWebinarError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.JoinWebinarError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.LocalStreamError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.UnKnownVisitorError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.ServiceConnectError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
} else if (id === XWebinarExceptionIDs.RTCPeerConnectionError) {
//
// result = {
// ...result,
// id: 99990,
// message: '',
// };
}
}
//
return result;
}
//#endregion
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar.tools.ts
import {
XLocale,
hasChild,
isSameObject,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
} from 'x-framework-core';
import {
XWebinarDto,
XWebinarType,
preparePersonDtoFields,
} from 'x-saherelm-api-sdk';
import { VModelValidationResult } from '../v-dto/v-dto.typings';
import { AppResourceIDs } from 'src/app/config/localization.config';
import { defaultVWebinarViewDto, VWebinarViewDto } from './v-webinar.typings';
export function ToWebinarViewDto(
model: XWebinarDto,
forLanguage: XLocale | string,
) {
//
// Validate ...
if (isNullOrUndefined(model) || isNullOrEmptyString(forLanguage)) {
return undefined;
}
//
// Detect for Language Locales ...
//
// Title ...
const titleLocale = model.title.locales.find(
(m) => m.language == forLanguage,
);
//
// Description ...
const descriptionLocale = model.description.locales.find(
(m) => m.language == forLanguage,
);
//
const result: VWebinarViewDto = {
id: model.id,
type: model.type,
owner: model.owner,
language: forLanguage,
ownerId: model.ownerId,
enabled: model.enabled,
createdOn: model.createdOn,
title: isNullOrUndefined(titleLocale) ? '' : titleLocale.value,
description: isNullOrUndefined(descriptionLocale)
? ''
: descriptionLocale.value,
};
//
return result;
}
export function ToWebinarDto(model: VWebinarViewDto, dto?: XWebinarDto) {
//
let result: XWebinarDto = undefined;
//
model = prepareVWebinarViewDtoFields(model);
//
result = {
id: model.id,
type: model.type,
ownerId: model.ownerId,
enabled: model.enabled,
title: {
resource:
dto && dto.title && dto.title.resource ? dto.title.resource : '',
locales: [
{
value: model.title,
language: model.language,
},
],
},
description: {
resource:
dto && dto.description && dto.description.resource
? dto.description.resource
: '',
locales: [
{
value: model.description,
language: model.language,
},
],
},
createdOn: model.createdOn,
};
//
if (
isNullOrEmptyString(result.id) &&
!isNullOrUndefined(dto) &&
!isNullOrEmptyString(dto.id)
) {
//
result.id = dto.id;
}
//
return result;
}
export function prepareVWebinarViewDtoFields(model?: VWebinarViewDto) {
//
if (isNullOrUndefined(model)) {
model = {
...defaultVWebinarViewDto,
};
}
//
model = {
type: model.type >= 0 ? model.type : defaultVWebinarViewDto.type,
id: !isNullOrEmptyString(model.id) ? model.id : defaultVWebinarViewDto.id,
title: !isNullOrEmptyString(model.title)
? model.title
: defaultVWebinarViewDto.title,
enabled: !isNullOrUndefined(model.enabled)
? model.enabled
: defaultVWebinarViewDto.enabled,
ownerId: !isNullOrEmptyString(model.ownerId)
? model.ownerId
: defaultVWebinarViewDto.ownerId,
language: !isNullOrEmptyString(model.language)
? model.language
: defaultVWebinarViewDto.language,
description: !isNullOrEmptyString(model.description)
? model.description
: defaultVWebinarViewDto.description,
owner: !isNullOrUndefined(model.owner)
? preparePersonDtoFields(model.owner)
: defaultVWebinarViewDto.owner,
createdOn: !isNullOrUndefined(model.createdOn)
? model.createdOn
: defaultVWebinarViewDto.createdOn,
};
//
return model;
}
export function isDefaultVWebinarViewDto(model?: VWebinarViewDto) {
return isSameObject(
prepareVWebinarViewDtoFields(model),
defaultVWebinarViewDto,
);
}
export function validateVWebinarViewDtoForAction(model?: VWebinarViewDto) {
//
let result: VModelValidationResult<VWebinarViewDto> = undefined;
//
let isValid = false;
const validationErrors: string[] = [];
const normalized = prepareVWebinarViewDtoFields(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !isDefaultVWebinarViewDto(normalized);
if (!isValid) {
//
validationErrors.push('InvalidArgs');
} else {
//
// Check Other Validations ...
//
// ID ...
//
// Language ...
isValid = !isNullOrEmptyString(normalized.language);
if (!isValid) {
//
validationErrors.push('InvalidArgs');
}
//
// Title ...
isValid = !isNullOrEmptyString(normalized.title);
if (!isValid) {
//
validationErrors.push('InvalidArgs');
}
//
// Description ...
isValid = !isNullOrEmptyString(normalized.description);
if (!isValid) {
//
validationErrors.push('InvalidArgs');
}
//
// Type ...
isValid = normalized.type >= 0;
if (!isValid) {
//
validationErrors.push('InvalidArgs');
}
}
//
// Handle Result ...
//
isValid = !hasChild(validationErrors);
//
const isValidForAdd = isNullOrEmptyString(normalized.id);
const isValidForUpdate = !isNullOrEmptyString(normalized.id);
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
export function getWebinarResourceID(value: number) {
//
let result: XResourceIdentifier = undefined;
//
switch (value) {
//
case XWebinarType.Private:
result = AppResourceIDs.private;
break;
//
case XWebinarType.Public:
result = AppResourceIDs.public;
break;
}
//
return result;
}
export function getWebinarTypeValues() {
return [XWebinarType.Public, XWebinarType.Private];
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar.typings.ts
import { XBaseDto } from 'x-framework-core';
import { XPersonDto, XWebinarType } from 'x-saherelm-api-sdk';
export interface VWebinarViewDto extends XBaseDto {
id: string;
title: string;
ownerId: string;
createdOn: Date;
enabled: boolean;
language: string;
type: XWebinarType;
owner?: XPersonDto;
description: string;
}
export const defaultVWebinarViewDto: VWebinarViewDto = {
id: '',
title: '',
ownerId: '',
language: '',
enabled: false,
description: '',
type: undefined,
owner: undefined,
createdOn: undefined,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar\v-webinar.component.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar\v-webinar.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar\v-webinar.component.ts
import {
VBaseDtoComponent,
VModelFieldDescriptor,
VModelValidationResult,
} from '../../v-dto/v-dto.typings';
import { getFieldNames } from 'x-framework-core';
import { defaultWebinarDto, XWebinarDto } from 'x-mabsut-api-sdk';
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: 'v-webinar',
templateUrl: './v-webinar.component.html',
styleUrls: ['./v-webinar.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VWebinarComponent extends VBaseDtoComponent<XWebinarDto> {
//
//#region Props ...
readonly defaultModel: XWebinarDto = {
...defaultWebinarDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
defaultFieldDescriptors: VModelFieldDescriptor<any>[];
//
// ID ...
//
// Owner ID ...
//
// Type ...
//
// Enabled ...
//
// Title ...
//
// Description ...
//#endregion
//
//#region Abstract ...
validate(model?: Partial<XWebinarDto>): VModelValidationResult<XWebinarDto> {
throw new Error('Method not implemented.');
}
prepare(model?: XWebinarDto): XWebinarDto {
throw new Error('Method not implemented.');
}
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar\v-webinar.typings.ts
export enum VWebinarAction {
Refresh = 'Refresh',
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-connect\v-webinar-connect.component.html
<section
#vBaseDialogContainer
class="v-base-dialog"
[isForeground]="false"
[xColor]="getValue(backgroundColor) | async"
>
<!-- Title Toolbar -->
<section #vBaseDialogToolbar>
<!-- Toolbar -->
<ion-toolbar
#vToolbar
class="v-base-dialog-toolbar"
[xColor]="getValue(toolbarColor) | async"
>
<!-- Slots -->
<div class="v-base-dialog-toolbar-area">
<x-slotter>
<!-- Start -->
<x-slot
[name]="SlotNames.START"
[cssClass]="['ion-text-start', 'ion-float-start']"
>
<div
matRipple
(click)="handleBackPressed()"
[matTooltip]="getResourceValue(ResourceIDs.back) | async"
class="v-base-dialog-toolbar-item v-base-dialog-toolbar-button center-vertical-flex"
>
<x-icon
[name]="backIcon"
[loading]="loading"
[color]="'cb-inherit'"
[uiDisabled]="uiDisabled"
></x-icon>
</div>
</x-slot>
<!-- Center -->
<x-slot [name]="SlotNames.CENTER" [cssClass]="'ion-text-center'">
<ion-title class="v-base-dialog-toolbar-item center-vertical-flex">
<h3>{{ getToolbarTitle() }}</h3>
</ion-title>
</x-slot>
<!-- End -->
<x-slot
[name]="SlotNames.END"
[cssClass]="['ion-text-end', 'ion-float-end']"
>
</x-slot>
</x-slotter>
</div>
</ion-toolbar>
<!-- Progressbar -->
<ion-progress-bar
type="indeterminate"
*ngIf="getValue(showProgrssbar) | async"
[color]="getValue(progressbarColor) | async"
></ion-progress-bar>
</section>
<!-- Components -->
<section class="v-base-dialog-content">
<!-- Content -->
<ng-container *ngTemplateOutlet="contentRef"></ng-container>
</section>
</section>
<!-- -->
<!-- Template -->
<!-- -->
<!-- Content Template -->
<ng-template #contentRef>
<div class="v-webinar-screen">
<!-- Webinar Card -->
<x-card
class="v-card v-no-margin-card"
[showHeader]="true"
[title]="getTitle() | async"
[subTitle]="getSubTitle() | async"
>
<!-- Slotting -->
<x-slotter
[hasCenterSlot]="false"
[layout]="
(isMobileUi$ | async) ? SlotLayouts.VERTICAL : SlotLayouts.HORIZONTAL
"
[actionProvider]="slotterActionProvider"
>
<!-- End -->
<!-- Chat Screen -->
<x-slot [name]="SlotNames.END">
<ng-container *ngTemplateOutlet="webinarChatRef"></ng-container>
</x-slot>
<!-- Start -->
<!-- Video -->
<x-slot [name]="SlotNames.START">
<div class="v-webinar-video-container">
<ng-container *ngTemplateOutlet="webinarScreenRef"></ng-container>
</div>
</x-slot>
</x-slotter>
</x-card>
</div>
</ng-template>
<!-- Webinar Chat Template -->
<ng-template #webinarChatRef> Chats ... </ng-template>
<!-- Webinar Video Template -->
<ng-template #webinarScreenRef>
<!-- Owner -->
<ng-container *ngIf="isOwner() | async">
<!-- VVideo Implementing -->
<v-video
#localVideo
[autoPlay]="true"
[loading]="loading"
[showActions]="true"
[showTitle]="showTitle"
[showVideoTime]="false"
[uiDisabled]="uiDisabled"
[isInPage]="isInPageCard"
[showCallControl]="false"
[showProgressBar]="false"
[showRewindControl]="false"
[showRecordControl]="false"
[showCallingActions]="true"
[showForwardControl]="false"
[title]="getTitle() | async"
[wrapWithCard]="wrapWithCard"
[srcObject]="localVideoSource"
[showPlayPauseControl]="false"
[showVideoCallControl]="false"
[showMicorphoneControl]="false"
[showShareScreenControl]="false"
[showCurrentPlaybackRate]="false"
[showCameraSwitchControl]="false"
[showVolumeTogglerControl]="false"
[showVolumeChangerControl]="false"
[showPlaybackRatesControl]="false"
[showFullscreenTogglerControl]="false"
[cardSubTitle]="getSubTitle() | async"
[actionProvider]="localVideoActionProvider"
[pictureInPictureStream]="screenShareStream"
></v-video>
</ng-container>
<!-- Subscriber -->
<ng-container *ngIf="isSubscriber() | async">
<!-- VVideo Implementing -->
<v-video
#remoteVideo
[autoPlay]="true"
[loading]="loading"
[showActions]="true"
[showTitle]="showTitle"
[showVideoTime]="false"
[uiDisabled]="uiDisabled"
[showCallControl]="false"
[showProgressBar]="false"
[isInPage]="isInPageCard"
[showRewindControl]="false"
[showRecordControl]="false"
[showCallingActions]="true"
[title]="getTitle() | async"
[showForwardControl]="false"
[wrapWithCard]="wrapWithCard"
[showPlayPauseControl]="false"
[showVideoCallControl]="false"
[srcObject]="remoteVideoSource"
[showMicorphoneControl]="false"
[showShareScreenControl]="false"
[showCurrentPlaybackRate]="false"
[showCameraSwitchControl]="false"
[showVolumeTogglerControl]="false"
[showVolumeChangerControl]="false"
[showPlaybackRatesControl]="false"
[showFullscreenTogglerControl]="false"
[cardSubTitle]="getSubTitle() | async"
[actionProvider]="remoteVideoActionProvider"
[pictureInPictureStream]="screenShareStream"
></v-video>
</ng-container>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-connect\v-webinar-connect.component.scss
@import "../../../../theme/theme.scss";
:host {
--dialog-toolbar-height: 0px;
--background: var(--x-color-light);
}
::ng-deep.fullscreen {
--width: 100% !important;
--height: 100% !important;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 !important;
}
.v-base-dialog {
width: 100%;
height: 100%;
// height: calc(100% - var(--dialog-toolbar-height));
// margin-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-content {
height: 100%;
//
padding-bottom: var(--dialog-toolbar-height);
}
.v-base-dialog-toolbar-area {
margin: 8px;
}
.v-base-dialog-toolbar-item {
width: auto;
height: 48px;
}
.v-base-dialog-toolbar-button {
width: 32px;
margin: {
left: 4px;
right: 4px;
}
}
.v-webinar-screen {
padding: 16px;
}
//
// Non Global ...
.v-avatar-image {
//
margin: auto;
display: block;
max-width: 100%;
max-height: 100%;
//
clip-path: circle();
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-connect\v-webinar-connect.component.ts
import {
Input,
NgZone,
Inject,
Renderer2,
Component,
ViewChild,
ElementRef,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '@angular/core';
import {
XExceptionIDs,
XColorIdentifier,
isNullOrUndefined,
isNullOrEmptyString,
XResourceIdentifier,
XColorWithBrightness,
} from 'x-framework-core';
import {
XWebinarExceptionIDs,
getXWebinarException,
XWebinarExceptionIdentifier,
} from '../v-webinar.configs';
import { Subject } from 'rxjs';
import {
XSlotName,
XIconNames,
XSlotLayout,
XPopoverService,
XSlotterAction,
XSlotterActionModel,
} from 'x-framework-components';
import {
VWebinarCssVar,
VWebinarSubscriber,
VWebinarModalContext,
VWebinarModalContextIdentifier,
VWebinarReturnActionIdentifier,
} from './v-webinar-connect.typings';
import {
IVVideoState,
VVideoAction,
IVVideoFlags,
isVVideoAction,
VVideoActionModel,
} from '../../v-video/v-video.typings';
import { ToastButton } from '@ionic/angular';
import { map, takeUntil } from 'rxjs/operators';
import { X_CONFIG } from 'src/app/config/x.config';
import { XConfig } from 'src/app/config/app.config';
import { ViewportRuler } from '@angular/cdk/scrolling';
import { VWebinarViewDto } from '../v-webinar.typings';
import { XManagerService } from 'x-framework-services';
import { XHubConnectionDto } from 'x-framework-push-service';
import { VBaseComponent } from '../../classes/v-base.component';
import { SharedService } from 'src/app/shared/services/shared.service';
import {
isXCameraFacing,
XCameraFacing,
} from 'src/app/shared/services/x-media-devices.service';
@Component({
selector: 'v-webinar-connect',
templateUrl: './v-webinar-connect.component.html',
styleUrls: ['./v-webinar-connect.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VWebinarConnectComponent extends VBaseComponent {
//
//#region Properties ...
//
readonly SlotNames = Object.assign({}, XSlotName);
readonly SlotLayouts = Object.assign({}, XSlotLayout);
readonly VideoProvidedActions = Object.assign({}, VVideoAction);
/**
* retrieve Toolbar container ViewChild
* to Calculate Toolbar Height ...
*/
@ViewChild('vBaseDialogToolbar')
vBaseDialogToolbarContainer!: ElementRef;
/**
* Specified Modal Type ...
*/
@Input()
context: VWebinarModalContextIdentifier;
@Input()
model: VWebinarViewDto;
/**
* Background Color of Component ...
*/
@Input()
backgroundColor: XColorIdentifier = XColorWithBrightness.Light;
@Input()
toolbarTitle: XResourceIdentifier = '';
/**
* Toolbar Color ...
*/
@Input()
toolbarColor: XColorIdentifier = XColorWithBrightness.PrimaryTint;
/**
* Progressbar Color ...
*/
@Input()
progressbarColor: XColorIdentifier = XColorWithBrightness.Warning;
showProgrssbar = false;
/**
* Action Icon Names Specifiecations ...
*/
@Input()
cancelIcon = XIconNames.close;
@Input()
backIcon = XIconNames.arrow_back;
//
//#region Commone VVideo Props ...
@Input()
wrapWithCard: boolean = false;
@Input()
isInPageCard: boolean = false;
@Input()
showTitle: boolean = true;
//#endregion
//
slotterActionProvider = new Subject<XSlotterActionModel>();
//
//#region Custom Props ...
//
hasBackCamera = false;
webinaPushServiceUnSubscriber = new Subject();
loadingHandlerTiemout: NodeJS.Timeout = undefined;
//
isScreenShared = false;
screenShareStream: MediaStream = undefined;
//
isWebinarConnected = false;
isWebinarServiceConnected = false;
//
localStream: MediaStream;
currentMediaDevice: XCameraFacing = XCameraFacing.User;
//
remotePeer: RTCPeerConnection = null;
visitors: Array<XHubConnectionDto> = [];
viewerPeers = new Map<string, VWebinarSubscriber>();
//
localVideoSource: MediaStream = null;
localVideoActionProvider = new Subject<VVideoActionModel>();
//
remoteVideoSource: MediaStream = null;
remoteVideoActionProvider = new Subject<VVideoActionModel>();
//#endregion
//#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 managerService: XManagerService,
public changeDetector: ChangeDetectorRef,
protected popoverService: XPopoverService,
) {
super(
zone,
config,
element,
renderer,
ruler,
sharedService,
managerService,
changeDetector,
popoverService, //
);
}
//#endregion
//
//#region LifeCycles ...
async afterViewInit(): Promise<void> {
super.afterViewInit();
//
this.applyContentSize();
this.registerActionProviders();
//
// Handle Starting Up Connection ...
await this.handlePushServiceConnection();
if (this.isWebinarServiceConnected) {
await this.startUpWebinarConnect();
}
}
async onDestroy(): Promise<void> {
super.onDestroy();
//
this.detachEventListeners();
await this.destroyResources();
this.unregisterConnectionChecker();
//
// Handle Webinar Close if it's Open ...
const isOwner = await this.getValueAsync(this.isOwner());
if (isOwner) {
//
try {
//
const connectionId =
this.sharedService.saherElmAPIService.webinars.webinarPushService
.connectionId;
const entity = await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.get(
this.model.id,
),
);
if (!isNullOrUndefined(entity) && !!entity.enabled) {
//
entity.enabled = false;
await this.getValueAsync(
this.sharedService.saherElmAPIService.webinars.webinarProviderService.update(
entity,
connectionId,
),
);
}
} catch {}
}
}
//#endregion
//
//#region UI Providers ...
getToolbarTitle() {
//
let result = '';
//
if (!isNullOrUndefined(this.model)) {
result = this.model.title;
}
//
if (!isNullOrUndefined(this.toolbarTitle)) {
result = this.resourceProvider(this.toolbarTitle);
}
//
return result;
}
isOwner() {
return this.sharedService.userId$.pipe(
map(
(userId) =>
!isNullOrEmptyString(userId) && userId === this.model.ownerId,
),
);
}
isSubscriber() {
return this.sharedService.userId$.pipe(
map(
(userId) =>
!isNullOrEmptyString(userId) && userId !== this.model.ownerId,
),
);
}
/**
* Present Webinar Video Connection Title ...
*
* @returns
*/
getTitle() {
//
return this.isOwner().pipe(
map((isOwner) => {
//
let result = '';
if (!this.model) {
return result;
}
//
let type = this.resourceProvider(
isOwner
? this.AppResourceIDs.webinar_owner
: this.AppResourceIDs.webinar_joiner,
);
result = `${this.model.title}`;
return this.applyLocale(result);
}),
);
}
/**
* Present Webinar Video Connection Sub Title ...
*
* @returns
*/
getSubTitle() {
//
return this.isOwner().pipe(
map((isOwner) => {
//
let result = '';
if (!this.model) {
return result;
}
//
result = this.resourceProvider(
isOwner
? this.AppResourceIDs.webinar_owner
: this.AppResourceIDs.webinar_joiner,
);
return this.applyLocale(result);
}),
);
}
//#endregion
//
//#region UI Handlers ...
/**
* Use for Handle Modela Closing Action based on Context ...
* @returns
*/
async handleBackPressed(action?: VWebinarReturnActionIdentifier) {
//
if (
this.loading ||
this.uiDisabled ||
!isNullOrUndefined(this.error) ||
isNullOrUndefined(this.context) ||
isNullOrEmptyString(this.context)
) {
return;
}
//
if (!this.lock && !this.isWebinarConnected) {
await this.handleCloseDialog();
} else {
//
await this.handleEndCall();
}
}
//#endregion
//
//#region Handlers ...
//#endregion
//
//#region Webinar Push Service ...
/**
* Handle Push Service Connection ...
*/
async handlePushServiceConnection(force: boolean = false) {
//
//#region Connect to Push Service ...
await this.handleLoadingState(true);
//
if (
force &&
(this.isWebinarServiceConnected ||
this.sharedService.saherElmAPIService.webinars.webinarPushService.isConnected())
) {
//
try {
await this.sharedService.saherElmAPIService.webinars.webinarPushService.disconnect();
} catch {}
//
this.isWebinarServiceConnected = false;
}
//
if (
this.sharedService.saherElmAPIService.webinars.webinarPushService.isConnected()
) {
//
await this.handleLoadingState(false);
this.isWebinarServiceConnected = true;
} else {
try {
//
await this.sharedService.saherElmAPIService.webinars.webinarPushService.connect();
this.isWebinarServiceConnected = true;
await this.handleLoadingState(false);
} catch {
//
await this.handleConnectionCheck(
XWebinarExceptionIDs.ServiceConnectError,
);
}
}
//#endregion
//
if (this.isWebinarServiceConnected) {
//
// Detach ...
if (force) {
this.detachEventListeners();
}
//
this.attachEventListeners();
}
}
/**
* Handle Startup Webinar Connection ...
*/
async startUpWebinarConnect(force: boolean = false) {
//
// Detect Type ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
//
await this.handleConnectionCheck(
XWebinarExceptionIDs.UnKnownVisitorError,
);
return;
}
//
// Handle Forces ...
if (force) {
//
// Reset Push Service Connection ...
await this.handlePushServiceConnection(force);
//
// Reset All Exists Resources ...
await this.destroyResources();
}
//
// Enable Loading Mode ...
await this.handleLoadingState(true);
//
// Register Connection Checker ...
this.registerConnectionChecker();
//
// Check Webinar Push Service Connected ...
if (!this.isWebinarServiceConnected) {
//
await this.handleConnectionCheck(
XWebinarExceptionIDs.ServiceConnectError,
);
return;
}
//
// Here we have to Start Webinar Action ...
//
// Handle Owner based Starting ...
if (isOwner) {
//
// For Owner, we have to Prepare Local Device Stream ...
// which Sends Media Tracks through Peer Connections ...
//
// Select Media Device ...
// for Local Stream ...
try {
//
this.localStream =
await this.sharedService.mediaDevicesService.getMediaDevice(
this.currentMediaDevice,
);
//
this.hasBackCamera =
await this.sharedService.mediaDevicesService.hasBackCamera();
} catch {
//
await this.handleConnectionCheck(XWebinarExceptionIDs.LocalStreamError);
return;
}
//
// Since we neew to Owner see himself on Video Element ...
// here we fill localVideoSource as Local Stream ...
// const localStreamTracks = this.localStream.getTracks().map(t => t.clone());
// this.localVideoSource = new MediaStream(localStreamTracks);
this.localVideoSource = this.localStream;
this.sendLocalVideoAction(this.VideoProvidedActions.Mute);
//
if (this.hasBackCamera) {
//
const flags: IVVideoFlags = {
hasBackCamera: this.hasBackCamera,
canSwitchCameras: this.hasBackCamera,
hasFrontCamera: !isNullOrUndefined(this.localStream),
};
//
console.log('hasBack: ', this.hasBackCamera);
//
this.sendLocalVideoAction(this.VideoProvidedActions.SetFlags, flags);
}
//
// After this, we have to Open Webinar and Notify clients which this webinar is Opened
// for Subscribers to Join ...
try {
//
this.isWebinarConnected = true;
await this.sharedService.saherElmAPIService.webinars.webinarPushService.openWebinar(
this.model.id,
);
} catch {
//
this.isWebinarConnected = false;
await this.handleConnectionCheck(XWebinarExceptionIDs.OpenWebinarError);
return;
}
}
//
// Handle Subscriber based Starting ...
if (isSubscriber) {
//
// Subscriber must have a Peer Connection by Owner ...
// so First step is Create Peer ...
try {
//
// Create a Remote Peer ...
this.remotePeer =
this.sharedService.saherElmAPIService.webinars.webinarPushService.rtcPeerService.createConnection();
//
// Then we have to Attach Peer Connection On Track Event Listener
// for Passing Recieve Tracks to remoteVideoSource ...
this.remotePeer.ontrack = (event: RTCTrackEvent) => {
//
// Validate ...
if (
!isNullOrUndefined(event) &&
!isNullOrUndefined(event.streams) &&
this.hasChild(event.streams)
) {
//
// Set Default Stream ...
if (
isNullOrUndefined(this.remoteVideoSource) ||
this.remoteVideoSource.id === event.streams[0].id
) {
this.remoteVideoSource = event.streams[0];
}
//
// Set Screen Stream ...
else {
//
const track = event.track;
//
// Handle Enable Picture in Picture ...
this.screenShareStream = event.streams[0];
//
// Destroy Screenshare Stream ...
this.screenShareStream.onremovetrack = (event) => {
//
// Set State ...
let state: IVVideoState = {
enablePictureInPicture: false,
};
this.sendRemoteVideoAction(
this.VideoProvidedActions.SetState,
state,
);
//
// Destroy Screen Share Stream ...
if (!isNullOrUndefined(this.screenShareStream)) {
this.screenShareStream.getTracks().forEach((t) => t.stop());
}
this.screenShareStream = undefined;
};
//
// Set State ...
let state: IVVideoState = {
enablePictureInPicture: true,
};
this.sendRemoteVideoAction(
this.VideoProvidedActions.SetState,
state,
);
}
//
this.handleDetectChanges();
}
};
//
// After this we have to Attach ICE Candidate Events Handler ...
this.remotePeer.onicecandidate = async (
event: RTCPeerConnectionIceEvent,
) => {
//
// Validate ...
if (
!isNullOrUndefined(event) &&
!isNullOrUndefined(event.candidate)
) {
//
// Converts Candidate to Json ...
const candidateJson = JSON.stringify(event.candidate);
//
// Send Recieved Candidate through Signalling Service ...
// since we send only this candidate to webinar owner, there is no neew to Specified To ...
await this.sharedService.saherElmAPIService.webinars.webinarPushService.candidate(
candidateJson,
'',
);
}
};
} catch {
//
await this.handleConnectionCheck(
XWebinarExceptionIDs.RTCPeerConnectionError,
);
return;
}
//
// Final Step is Notify Clients which a Viewer Joined ...
try {
//
this.isWebinarConnected = true;
await this.sharedService.saherElmAPIService.webinars.webinarPushService.joinWebinar(
this.model.id,
);
} catch {
//
this.isWebinarConnected = false;
await this.handleConnectionCheck(XWebinarExceptionIDs.JoinWebinarError);
return;
}
}
//
// in Both Senarios we have to check Webinar is Connected or not ...
if (!this.isWebinarConnected) {
//
if (isOwner) {
await this.handleConnectionCheck(XWebinarExceptionIDs.OpenWebinarError);
} else {
await this.handleConnectionCheck(XWebinarExceptionIDs.JoinWebinarError);
}
//
return;
}
/**
* Locking Component ...
*/
this.lock = true;
/**
* Clear any possible errors ...
*/
this.error = undefined;
/**
* Unregister Connection Checker ...
*/
this.unregisterConnectionChecker();
/**
* Update Webianr Visitors ...
*/
await this.updateVisitors();
/**
* Disable Loading State ...
*/
await this.handleLoadingState(false);
/**
* Preparing Views ...
*/
await this.preparingViews();
}
//
//
//
/**
* Handle Viewer Joined ...
*
* @param viewerId
* @param webinarId
* @param userId
* @param username
*/
async onViewerJoined(
viewerId: string,
webinarId: string,
userId: string,
username?: string,
) {
//
// Reading Our Connection Id ...
const connectionId =
this.sharedService.saherElmAPIService.webinars.webinarPushService
.connectionId;
//
// Validate ...
if (
isNullOrEmptyString(userId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(viewerId) ||
isNullOrEmptyString(webinarId) ||
isNullOrEmptyString(connectionId) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
if (isOwner) {
//
// if a Viewer Joined ...
//
try {
//
// First ...
// we Have to Create a Peer Connection for it ...
const pc =
this.sharedService.saherElmAPIService.webinars.webinarPushService.rtcPeerService.createConnection();
//
// Second ...
// Add Required Event Listeners to Peer Connection ...
//
// Signaliing Recieve ICE Candidates of Peer Connection ...
pc.onicecandidate = async (e: RTCPeerConnectionIceEvent) => {
//
// Check Candidate Exists ...
if (!isNullOrUndefined(e) && !isNullOrUndefined(e.candidate)) {
//
// Prepare Model to Signalling ...
const json = JSON.stringify(e.candidate);
await this.sharedService.saherElmAPIService.webinars.webinarPushService.candidate(
json,
viewerId,
);
}
};
//
// Handle Offering ...
pc.onnegotiationneeded = async () => {
//
try {
//
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
//
const offerJson = JSON.stringify(offer);
await this.sharedService.saherElmAPIService.webinars.webinarPushService.offer(
offerJson,
viewerId,
);
} catch {}
};
//
// Third ...
// Attach Local Streams Tracks to Peer Connection for Streaming ...
this.localStream
.getTracks()
.forEach((t) => pc.addTrack(t, this.localStream));
//
// if Screen Shared, Handle it's Tracks ...
if (!isNullOrUndefined(this.screenShareStream)) {
this.screenShareStream
.getTracks()
.forEach((t) => pc.addTrack(t, this.screenShareStream));
}
//
// Store Viewer Peer Connection ...
const subscriber: VWebinarSubscriber = {
userId,
username,
peer: pc,
webinarId,
connectionId: viewerId,
};
this.viewerPeers.set(viewerId, subscriber);
} catch {
//
await this.handleConnectionCheck(
XWebinarExceptionIDs.RTCPeerConnectionError,
);
return;
}
}
//
// Update Visitors ...
await this.updateVisitors();
//
const message = this.applyLocale(
this.resourceProvider(this.AppResourceIDs.visitor_joined_message).replace(
'${TMP1}&',
username,
),
);
await this.managerService.notificationService.presentInfoNotification({
message,
dissmissable: true,
});
this.detectChanges();
}
/**
* Handle Viewer Leaved ...
*
* @param viewerId
* @param webinarId
* @param userId
* @param username
*/
async onViewerLeaved(
viewerId: string,
webinarId: string,
userId: string,
username?: string,
) {
//
// Reading Our Connection Id ...
const connectionId =
this.sharedService.saherElmAPIService.webinars.webinarPushService
.connectionId;
//
// Validate ...
if (
isNullOrEmptyString(userId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(viewerId) ||
isNullOrEmptyString(webinarId) ||
isNullOrEmptyString(connectionId) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
if (isOwner) {
//
const has = this.viewerPeers.has(viewerId);
if (has) {
//
const pc = this.viewerPeers.get(viewerId).peer;
this.viewerPeers.delete(viewerId);
//
pc.close();
}
}
//
// Update Visitors ...
await this.updateVisitors();
//
const message = this.applyLocale(
this.resourceProvider(this.AppResourceIDs.visitor_leaved_message).replace(
'${TMP1}&',
username,
),
);
await this.managerService.notificationService.presentInfoNotification({
message,
dissmissable: true,
});
this.detectChanges();
}
/**
* Handle Webinar Open ...
*
* @param webinarId
* @returns
*/
async onWebinarOpened(webinarId: string) {
//
if (
isNullOrEmptyString(webinarId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
await this.startUpWebinarConnect(true);
}
/**
* Webinar Closed Push Event Listener ...
*/
async onWebinarClosed(webinarId: string) {
//
if (
isNullOrEmptyString(webinarId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
// Handle Close Event ...
//
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (isSubscriber) {
//
// Destroy Remote Video Source ...
if (!isNullOrUndefined(this.remoteVideoSource)) {
//
this.remoteVideoSource.getTracks().forEach((t) => t.stop());
this.remoteVideoSource = undefined;
}
//
// Destroy remote Peer ...
if (
!isNullOrUndefined(this.remotePeer) &&
(this.remotePeer.connectionState === 'connected' ||
this.remotePeer.connectionState === 'connecting')
) {
this.remotePeer.close();
}
this.remotePeer = undefined;
//
this.isWebinarConnected = false;
//
// Set Remote Video Flags ...
const flags: IVVideoFlags = { isInCall: false };
this.sendRemoteVideoAction(this.VideoProvidedActions.SetFlags, flags);
this.detectChanges();
//
await this.managerService.notificationService.presentWarningNotification({
dissmissable: true,
message: this.resourceProvider(
this.AppResourceIDs.webinar_closed_message,
),
});
await this.handleCloseDialog();
}
}
/**
* Handle Offer Recieved ...
*
* @param offer
* @returns
*/
async onOfferRecieved(offer: any) {
//
// Validate ...
if (isNullOrUndefined(offer)) {
return;
}
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
if (isSubscriber) {
//
if (!isNullOrUndefined(this.remotePeer)) {
//
// Retrieve Offering Model ...
const offerModel: RTCSessionDescriptionInit = JSON.parse(offer);
this.remotePeer.setRemoteDescription(
new RTCSessionDescription(offerModel),
);
//
// Preparing Answer ...
const answer = await this.remotePeer.createAnswer();
await this.remotePeer.setLocalDescription(answer);
//
// Signalling Answer ...
const answerJsno = JSON.stringify(answer);
await this.sharedService.saherElmAPIService.webinars.webinarPushService.answer(
answerJsno,
);
}
}
}
/**
* Handle Answer Recieved ...
*
* @param answer
* @param requesterId
* @returns
*/
async onAnswerRecieved(answer: any, requesterId: string) {
//
// Validate ...
if (isNullOrUndefined(answer)) {
return;
}
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
if (isOwner) {
//
// Validate Requester Id ...
if (
!isNullOrEmptyString(requesterId) &&
this.viewerPeers.has(requesterId)
) {
//
const answerModel: RTCSessionDescriptionInit = JSON.parse(answer);
await this.viewerPeers
.get(requesterId)
.peer.setRemoteDescription(new RTCSessionDescription(answerModel));
}
}
}
/**
* Handle Candidate Recieved ...
*
* @param candidate
* @param senderId
* @returns
*/
async onCandidateRecieved(candidate: any, senderId: string) {
//
// Validate ...
if (isNullOrUndefined(candidate)) {
return;
}
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
if (isOwner) {
//
// Validate SenderId ...
if (!isNullOrEmptyString(senderId) && this.viewerPeers.has(senderId)) {
//
const candidateModel: RTCIceCandidateInit = JSON.parse(candidate);
await this.viewerPeers
.get(senderId)
.peer.addIceCandidate(candidateModel);
}
}
//
if (isSubscriber) {
//
// Validate SenderId ...
if (!isNullOrUndefined(this.remotePeer)) {
const candidateModel: RTCIceCandidateInit = JSON.parse(candidate);
await this.remotePeer.addIceCandidate(candidateModel);
}
}
}
//#endregion
//
//#region Private ...
/**
* Handle Loading State ...
*
* @param state
*/
private async handleLoadingState(state: boolean) {
//
this.loading = state;
this.uiDisabled = state;
this.showProgrssbar = state;
//
this.handleDetectChanges();
}
/**
* Handle Change Detetction ...
*/
private handleDetectChanges() {
//
this.sendLocalVideoAction(this.VideoProvidedActions.Refresh);
this.sendRemoteVideoAction(this.VideoProvidedActions.Refresh);
this.slotterActionProvider.next({ action: XSlotterAction.Refresh });
//
this.detectChanges();
}
/**
* Calculate Toolbar Height and add a CSSVar to
* use it for Content Sizing ...
*
* @returns
*/
private async applyContentSize() {
//
const height =
this.vBaseDialogToolbarContainer.nativeElement.getBoundingClientRect()
.height;
if (height === 0) {
setTimeout(() => this.applyContentSize(), 100);
return;
}
//
this.setCssVar(VWebinarCssVar.DialogToolbarHeight, `${height}px`);
this.detectChanges();
}
/**
* Preparing Views Based on Connection type ...
*/
private async preparingViews() {
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
var falseState: IVVideoState = {
//
showRewindControl: false,
showForwardControl: false,
showPlayPauseControl: false,
showVolumeChangerControl: false,
showPlaybackRatesControl: false,
showFullscreenTogglerControl: false,
//
showCallControl: false,
showRecordControl: false,
showVideoCallControl: false,
showMicorphoneControl: false,
showShareScreenControl: false,
showCameraSwitchControl: false,
//
enablePictureInPicture: false,
enableSpeechRecognition: false,
};
//
if (isOwner) {
//
// Preparing Views as Owner Connection ...
//
var state: IVVideoState = {
//
...falseState,
//
showCallControl: true,
showVideoCallControl: true,
showMicorphoneControl: true,
showShareScreenControl: true,
showFullscreenTogglerControl: true,
showCameraSwitchControl: this.hasBackCamera,
};
//
this.sendLocalVideoAction(VVideoAction.SetState, state);
//
// We Have to Mute Local Video ...
this.sendLocalVideoAction(VVideoAction.Mute);
} else if (isSubscriber) {
//
// Preparing Views as Subscriber Connection ...
//
var state: IVVideoState = {
//
...falseState,
//
showCallControl: true,
showVolumeTogglerControl: true,
showVolumeChangerControl: true,
showFullscreenTogglerControl: true,
};
//
this.sendRemoteVideoAction(VVideoAction.SetState, state);
}
}
/**
* Update Webinar Visitors ...
*/
private async updateVisitors() {
//
// Validate ...
if (isNullOrUndefined(this.model) || isNullOrEmptyString(this.model.id)) {
return;
}
//
let visitors: Array<XHubConnectionDto> = [];
try {
visitors =
await this.sharedService.saherElmAPIService.webinars.webinarPushService.webinarVisitors(
this.model.id,
);
} catch {}
//
this.visitors = [...visitors];
this.handleDetectChanges();
}
/**
* Check Connection Or Errors ...
*
* @param error
*/
private async handleConnectionCheck(
error: XWebinarExceptionIdentifier = undefined,
) {
//
if (!isNullOrUndefined(this.loadingHandlerTiemout)) {
return;
}
//
// Dispose / Unref Interval ...
this.unregisterConnectionChecker();
//
console.log('connection check error: ', error);
//
this.error = getXWebinarException(error);
await this.handleLoadingState(false);
//
await this.managerService.notificationService.presentErrorNotification({
dissmissable: false,
exception: XExceptionIDs.ActionFailed,
opt: {
buttons: [
{
icon: this.IconNames.refresh,
side: 'end',
handler: async () => {
await this.startUpWebinarConnect(true);
},
} as ToastButton,
{
icon: this.IconNames.close,
side: 'end',
handler: async () => {
//
this.error = undefined;
await this.handleCloseDialog();
},
} as ToastButton,
],
},
});
}
/**
* Register Connection Checker ...
*/
private registerConnectionChecker() {
//
if (!isNullOrUndefined(this.loadingHandlerTiemout)) {
return;
}
//
this.loadingHandlerTiemout = setInterval(async () => {
await this.handleConnectionCheck();
}, 20000);
}
/**
* Unregister Connection Checker ...
*/
private unregisterConnectionChecker() {
//
if (isNullOrUndefined(this.loadingHandlerTiemout)) {
return;
}
//
clearInterval(this.loadingHandlerTiemout);
this.loadingHandlerTiemout = undefined;
}
/**
* Detach Webinar Push Service Event Listeners ...
*/
private detachEventListeners() {
//
if (isNullOrUndefined(this.webinaPushServiceUnSubscriber)) {
return;
}
//
this.webinaPushServiceUnSubscriber.next();
this.webinaPushServiceUnSubscriber.complete();
this.webinaPushServiceUnSubscriber = undefined;
}
/**
* Attach Webinar Push Service Event Listeners ...
*/
private attachEventListeners() {
//
this.detachEventListeners();
this.webinaPushServiceUnSubscriber = new Subject();
//
// Webinar Opened ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onWebinarOpened$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((webinarId) => {
//
if (
isNullOrEmptyString(webinarId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
this.onWebinarOpened(webinarId);
});
//
// Webinar Closed ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onWebinarClosed$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((webinarId) => {
//
if (
isNullOrEmptyString(webinarId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
webinarId !== this.model.id
) {
return;
}
//
this.onWebinarClosed(webinarId);
});
//
// Viewer Joined ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onViewerJoined$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((data) => {
//
if (
isNullOrUndefined(data) ||
isNullOrEmptyString(data.viewerId) ||
isNullOrEmptyString(data.webinarId) ||
isNullOrEmptyString(data.userId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
data.webinarId !== this.model.id
) {
return;
}
//
this.onViewerJoined(
data.viewerId,
data.webinarId,
data.userId,
data.username,
);
});
//
// Viewer Leaved ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onViewerLeaved$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((data) => {
//
if (
isNullOrUndefined(data) ||
isNullOrEmptyString(data.viewerId) ||
isNullOrEmptyString(data.webinarId) ||
isNullOrEmptyString(data.userId) ||
isNullOrUndefined(this.model) ||
isNullOrEmptyString(this.model.id) ||
data.webinarId !== this.model.id
) {
return;
}
//
this.onViewerLeaved(
data.viewerId,
data.webinarId,
data.userId,
data.username,
);
});
//
// Offer Recieved ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onOfferRecieved$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((offer) => {
//
if (isNullOrUndefined(offer)) {
return;
}
//
this.onOfferRecieved(offer);
});
//
// Answer Recieved ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onAnswerRecieved$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((data) => {
//
if (isNullOrUndefined(data) || isNullOrUndefined(data.answer)) {
return;
}
//
this.onAnswerRecieved(data.answer, data.requesterId);
});
//
// Candidate Recieved ...
this.sharedService.saherElmAPIService.webinars.webinarPushService.onCandidateRecieved$
.pipe(takeUntil(this.webinaPushServiceUnSubscriber))
.subscribe((data) => {
//
if (isNullOrUndefined(data) || isNullOrUndefined(data.candidate)) {
return;
}
//
this.onCandidateRecieved(data.candidate, data.senderId);
});
}
/**
* Destroy All Resources ...
*/
private async destroyResources() {
//
// Destroy Local Stream ...
if (!isNullOrUndefined(this.localStream)) {
this.localStream.getTracks().forEach((t) => t.stop());
}
this.localStream = undefined;
//
// Destroy Local Video Source ...
if (!isNullOrUndefined(this.localVideoSource)) {
this.localVideoSource.getTracks().forEach((t) => t.stop());
}
this.localVideoSource = undefined;
//
// Destroy Remote Video Source ...
if (!isNullOrUndefined(this.remoteVideoSource)) {
this.remoteVideoSource.getTracks().forEach((t) => t.stop());
}
this.remoteVideoSource = undefined;
//
// Destroy Screenshare Stream ...
if (!isNullOrUndefined(this.screenShareStream)) {
this.screenShareStream.getTracks().forEach((t) => t.stop());
}
this.screenShareStream = undefined;
//
// Destroy Remote Peer ...
if (
!isNullOrUndefined(this.remotePeer) &&
(this.remotePeer.connectionState === 'connected' ||
this.remotePeer.connectionState === 'connecting')
) {
try {
this.remotePeer.close();
} catch {}
}
this.remotePeer = undefined;
//
// Destroy Peers ...
if (!isNullOrUndefined(this.viewerPeers) && this.viewerPeers.size > 0) {
//
this.viewerPeers.forEach((i) => {
//
if (
!isNullOrUndefined(i.peer) &&
(i.peer.connectionState === 'connected' ||
i.peer.connectionState === 'connecting')
) {
try {
i.peer.close();
} catch {}
}
});
this.viewerPeers.clear();
}
//
// Destroy Viewers ...
this.visitors = [];
//
this.handleDetectChanges();
}
/**
* Use for Handle Modal Closing Action based on Context ...
*
* @returns
*/
private async handleCloseDialog(action?: VWebinarReturnActionIdentifier) {
//
if (isNullOrUndefined(this.context) || isNullOrEmptyString(this.context)) {
return;
}
//
switch (this.context) {
//
case VWebinarModalContext.Modal:
await this.managerService.dialogService.modalController.dismiss(action);
break;
//
case VWebinarModalContext.PopOver:
await this.managerService.dialogService.popoverController.dismiss(
action,
);
break;
}
}
//
//#region Action Provider Handlers ...
/**
* Register Action Providers ...
*/
private registerActionProviders() {
//
// Register Action Providers ...
//
// Local Video ...
if (!isNullOrUndefined(this.localVideoActionProvider)) {
//
this.applyTakeUntilWrapper(
this.localVideoActionProvider.asObservable(),
).subscribe(async (event) => {
//
// Validate Action ...
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVVideoAction(event.action)
) {
return;
}
//
// Handle Provided Actions ...
const action = event.action;
//
// Response ...
//
// Flags ...
if (action === this.VideoProvidedActions.ResponseFlags) {
}
//
// State ...
else if (action === this.VideoProvidedActions.ResponseState) {
}
//
// Properties ...
else if (action === this.VideoProvidedActions.ResponseProperties) {
}
//
// End Call ...
else if (action === this.VideoProvidedActions.EndCall) {
this.handleEndCall();
}
//
// Request Call ...
else if (action === this.VideoProvidedActions.RequestCall) {
this.handleRequestCall();
}
//
// Request Switch Camera ...
else if (action === this.VideoProvidedActions.RequestSwitchCamera) {
//
if (
!isNullOrUndefined(event.payload) &&
isXCameraFacing(event.payload)
) {
await this.handleRequestSwitchCamera(event.payload);
}
}
//
// Request Start Screenshare ...
else if (action === this.VideoProvidedActions.RequestStartScreenShare) {
await this.handleStartScreenshare();
}
//
// Request Stop Screenshare ...
else if (action === this.VideoProvidedActions.RequestStopScreenShare) {
await this.handleStopScreenshare();
}
});
}
//
// Remote Video ...
if (!isNullOrUndefined(this.remoteVideoActionProvider)) {
//
this.applyTakeUntilWrapper(
this.remoteVideoActionProvider.asObservable(),
).subscribe((event) => {
//
// Validate Action ...
if (
isNullOrUndefined(event) ||
isNullOrEmptyString(event.action) ||
!isVVideoAction(event.action)
) {
return;
}
//
// Handle Provided Actions ...
//
// Handle Provided Actions ...
const acttion = event.action;
//
// Response ...
//
// Flags ...
if (acttion === this.VideoProvidedActions.ResponseFlags) {
}
//
// State ...
else if (acttion === this.VideoProvidedActions.ResponseState) {
}
//
// Properties ...
else if (acttion === this.VideoProvidedActions.ResponseProperties) {
}
//
// End Call ...
else if (acttion === this.VideoProvidedActions.EndCall) {
this.handleEndCall();
}
//
// Request Call ...
else if (acttion === this.VideoProvidedActions.RequestCall) {
this.handleRequestCall();
}
});
}
}
/**
* Send Actions to Local Video Component ...
*
* @param action
* @param payload
* @returns
*/
private sendLocalVideoAction(action: VVideoAction, payload?: any) {
//
if (isNullOrUndefined(this.localVideoActionProvider)) {
return;
}
//
this.localVideoActionProvider.next({ action, payload });
}
/**
* Send Actions to Remote Video Component ...
*
* @param action
* @param payload
* @returns
*/
private sendRemoteVideoAction(action: VVideoAction, payload?: any) {
//
if (isNullOrUndefined(this.remoteVideoActionProvider)) {
return;
}
//
this.remoteVideoActionProvider.next({ action, payload });
}
//
//#region Implemented Provided Actions ...
/**
* Handle End Call ...
*
* @returns
*/
private async handleEndCall() {
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
// Check Connection Exists ...
if (!this.isWebinarConnected) {
return;
}
//
await this.doConfirmedAction(async () => {
//
try {
//
if (isOwner) {
//
// Owner Close Webinar ...
await this.sharedService.saherElmAPIService.webinars.webinarPushService.closeWebinar(
this.model.id,
);
//
// Destroy Streams ...
//
// Local ...
if (!isNullOrUndefined(this.localStream)) {
//
this.localStream.getTracks().forEach((t) => t.stop());
this.localStream = undefined;
}
//
// Local Video Source ...
if (!isNullOrUndefined(this.localVideoSource)) {
//
this.localVideoSource.getTracks().forEach((t) => t.stop());
this.localVideoSource = undefined;
}
//
// Close all Peers ...
if (
!isNullOrUndefined(this.viewerPeers) &&
this.viewerPeers.size > 0
) {
//
this.viewerPeers.forEach((viewer) => {
//
if (
viewer.peer.connectionState === 'connected' ||
viewer.peer.connectionState === 'connecting'
) {
viewer.peer.close();
}
});
this.viewerPeers.clear();
}
}
//
if (isSubscriber) {
//
// Subscriber Leave Webinar ...
await this.sharedService.saherElmAPIService.webinars.webinarPushService.leaveWebinar(
this.model.id,
);
//
// Destroy Remote Video Source ...
if (!isNullOrUndefined(this.remoteVideoSource)) {
//
this.remoteVideoSource.getTracks().forEach((t) => t.stop());
this.remoteVideoSource = undefined;
}
//
// Close Remote Peer ...
if (
!isNullOrUndefined(this.remotePeer) &&
(this.remotePeer.connectionState === 'connected' ||
this.remotePeer.connectionState === 'connecting')
) {
this.remotePeer.close();
}
this.remotePeer = undefined;
}
} catch {
//
await this.handleConnectionCheck(XWebinarExceptionIDs.CallEndError);
return;
}
//
this.isWebinarConnected = false;
//
const flags: IVVideoFlags = { isInCall: false };
this.sendLocalVideoAction(this.VideoProvidedActions.SetFlags, flags);
this.sendRemoteVideoAction(this.VideoProvidedActions.SetFlags, flags);
this.detectChanges();
//
await this.handleCloseDialog();
});
}
/**
* Handle Request Call ...
*/
private async handleRequestCall() {
//
const isOwner = await this.getValueAsync(this.isOwner());
//
}
/**
* Handle Stop Screen Share ...
*
* @returns
*/
private async handleStopScreenshare() {
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
// Check Connection Exists ...
if (!this.isWebinarConnected || !this.isScreenShared) {
return;
}
//
// Disable Picture in Picture ...
let flags: IVVideoFlags = {
isScreenShared: false,
};
let state: IVVideoState = {
enablePictureInPicture: false,
};
//
if (isOwner) {
//
this.sendLocalVideoAction(this.VideoProvidedActions.SetState, state);
//
// Remove Tracks From Peers ...
for (let key of this.viewerPeers.keys()) {
//
const item = this.viewerPeers.get(key);
const peer = item.peer;
//
this.screenShareStream.getTracks().forEach((t) => {
//
const sender = peer.getSenders().find((s) => s.track === t);
peer.removeTrack(sender);
});
}
//
// Destroy Stream ...
if (!isNullOrUndefined(this.screenShareStream)) {
this.screenShareStream.getTracks().forEach((t) => t.stop());
}
this.screenShareStream = undefined;
//
this.isScreenShared = false;
//
this.sendLocalVideoAction(this.VideoProvidedActions.SetFlags, flags);
}
}
/**
* Handle Start Screen Share ...
*
* @returns
*/
private async handleStartScreenshare() {
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
// Only Owners can Share Screen ...
if (!isOwner) {
return;
}
//
// Check Connection Exists ...
if (!this.isWebinarConnected) {
return;
}
//
try {
//
this.screenShareStream =
await this.sharedService.mediaDevicesService.getDisplayMediaDevice();
} catch {
//
// TODO: Issue Error ...
}
//
if (!isNullOrUndefined(this.screenShareStream)) {
//
this.isScreenShared = true;
//
// Attach Stops Event Listener ...
const track = this.screenShareStream.getVideoTracks()[0];
if (!isNullOrUndefined(track)) {
track.onended = async () => {
//
// Handle Manually Stop Screen Share ...
await this.handleStopScreenshare();
};
}
//
for (var key of this.viewerPeers.keys()) {
//
const item = this.viewerPeers.get(key);
const peer = item.peer;
//
console.log('adding screen shared tracks to: ', item.username);
this.screenShareStream.getTracks().forEach((t) => {
peer.addTrack(t, this.screenShareStream);
});
}
}
//
if (this.isScreenShared) {
//
// Enable Picture in Picture ...
let state: IVVideoState = {
enablePictureInPicture: true,
};
//
this.sendLocalVideoAction(this.VideoProvidedActions.SetState, state);
//
// Set Screen Shared Flag ...
let flags: IVVideoFlags = {
isScreenShared: true,
};
//
this.sendLocalVideoAction(this.VideoProvidedActions.SetFlags, flags);
}
//
this.detectChanges();
}
/**
* Switch Camera Device ...
*
* @param type
*/
private async handleRequestSwitchCamera(type: XCameraFacing) {
//
// Determine Current User State ...
const isOwner = await this.getValueAsync(this.isOwner());
const isSubscriber = await this.getValueAsync(this.isSubscriber());
if (!isOwner && !isSubscriber) {
return;
}
//
// Only Owners can Share Screen ...
if (
!isOwner ||
!this.hasBackCamera ||
!isXCameraFacing(type) ||
type === this.currentMediaDevice
) {
return;
}
//
// Get Device ...
try {
//
const stream =
await this.sharedService.mediaDevicesService.getMediaDevice(type);
//
// Remove all Pears Tracks ...
this.viewerPeers.forEach(async (i) => {
//
this.localStream.getTracks().forEach((t) => {
//
const tSender = i.peer.getSenders().find((s) => s.track === t);
if (!isNullOrUndefined(tSender)) {
i.peer.removeTrack(tSender);
}
});
});
//
// Destroy Local Stream ...
if (!isNullOrUndefined(this.localStream)) {
this.localStream.getTracks().forEach((t) => t.stop());
}
this.localStream = undefined;
//
// Destroy Local Video Source ...
if (!isNullOrUndefined(this.localVideoSource)) {
this.localVideoSource.getTracks().forEach((t) => t.stop());
}
this.localVideoSource = undefined;
//
this.localStream = stream;
this.localVideoSource = this.localStream;
//
this.viewerPeers.forEach((i) => {
//
this.localStream.getTracks().forEach((t) => {
i.peer.addTrack(t, this.localStream);
});
});
//
this.currentMediaDevice = type;
const flags: IVVideoFlags = {
currentUsingMediaDevice: type,
};
this.sendLocalVideoAction(this.VideoProvidedActions.SetFlags, flags);
} catch {}
}
//#endregion
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-connect\v-webinar-connect.typings.ts
import { isValueInEnum } from '../../tools/v-base.tools';
export enum VWebinarModalContext {
Modal = 'modal',
PopOver = 'pop_over',
}
export type VWebinarModalContextIdentifier = VWebinarModalContext | string;
export enum VWebinarCssVar {
DialogToolbarHeight = 'dialog-toolbar-height',
}
export enum VWebinarReturnAction {}
export interface VWebinarSubscriber {
userId: string;
username: string;
webinarId: string;
connectionId: string;
peer: RTCPeerConnection;
}
export type VWebinarReturnActionIdentifier = VWebinarReturnAction | string;
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-subscriber\v-webinar-subscriber.component.html
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-subscriber\v-webinar-subscriber.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-subscriber\v-webinar-subscriber.component.ts
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-view\v-webinar-view.component.html
<v-dto #dtoPresenter *ngIf="showComponent" [dtoContainer]="this">
<!-- Content Injections -->
<!-- Card Actions -->
<section
actions
*ngIf="
(getValue(wrapWithCard) | async) && (getValue(cardShowActions) | async)
"
>
<ng-content select="[actions]"></ng-content>
</section>
<!-- Custom Contnet -->
<div dtoCustomContent *ngIf="canShowCustomContent()">
<!-- Files Presenter -->
<ng-container
*ngIf="
showFiles && canShowFiles() && (notNullOrUndefinedValue(model) | async)
"
>
<ng-container *ngTemplateOutlet="filesPresenterRef"></ng-container>
</ng-container>
<!-- Tags Presenter -->
<ng-container
*ngIf="
showTags && canShowTags() && (notNullOrUndefinedValue(model) | async)
"
>
<ng-container *ngTemplateOutlet="tagsPresenterRef"></ng-container>
</ng-container>
<!-- Others -->
</div>
</v-dto>
<!-- -->
<!-- Owner Template -->
<!-- -->
<!-- Owner Template -->
<ng-template
#ownerRef
let-key="key"
let-index="index"
let-value="value"
let-props="props"
>
<!-- Make off Form Buttons ... -->
<v-person
[model]="value"
*ngIf="canShowOwner(value)"
[showFormResetButton]="false"
[showFormCancelButton]="false"
[showFormActionButton]="false"
[presentType]="ownerPersentType"
></v-person>
</ng-template>
<!-- Tags Presenter Template -->
<ng-template #tagsPresenterRef>
<!-- Tags Presenting -->
<v-tags-presenter
[canAttach]="canAttachTag"
[showLabel]="showTagsLabel"
[presentType]="tagsPresentType"
[tagsReader]="getTagsReaderTask()"
[tagActionsProvider]="tagActionsProvider"
[actionProvider]="tagsPresenterActionProvider"
(modelStateChanged)="handleTagsModelStateChanged($event)"
*ngIf="
showTags && canShowTags() && (notNullOrUndefinedValue(model) | async)
"
></v-tags-presenter>
</ng-template>
<!-- Files Presenter Template -->
<ng-template #filesPresenterRef>
<!-- Files Presenting -->
<v-files-presenter
[showLabel]="showFilesLabel"
[canAttach]="getCanAttachFile()"
[presentType]="filesPresentType"
[filesReader]="getFilesReaderTask()"
[fileProperties]="filesPresenterFileProperties"
[actionProvider]="filesPresenterActionProvider"
(modelStateChanged)="handleFilesModelStateChanged($event)"
*ngIf="
showFiles && canShowFiles() && (notNullOrUndefinedValue(model) | async)
"
></v-files-presenter>
</ng-template>
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-view\v-webinar-view.component.scss
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\app\views\webinar\v-webinar-view\v-webinar-view.component.ts
import {
Input,
Component,
ViewChild,
TemplateRef,
ChangeDetectionStrategy,
Output,
EventEmitter,
} from '@angular/core';
import {
XFileDto,
XPersonDto,
XWebinarType,
isDefaultPersonDto,
} from 'x-saherelm-api-sdk';
import {
getFieldNames,
XExceptionIDs,
nameofInstance,
isNullOrUndefined,
XResourceIdentifier,
isNullOrEmptyString,
} from 'x-framework-core';
import {
getWebinarResourceID,
getWebinarTypeValues,
prepareVWebinarViewDtoFields,
} from '../v-webinar.tools';
import { Subject } from 'rxjs';
import {
XFormDatePickerControlConfig,
XFormDatePickerControlPickerPosition,
} from 'x-framework-components';
import {
VBaseDtoComponent,
isModelStateLocked,
VBaseDtoPresentType,
VBaseDtoModelAction,
VModelFieldDescriptor,
VModelValidationResult,
VBaseDtoModelActionIdentifier,
} from '../../v-dto/v-dto.typings';
import {
PersonPresentType,
PersonPresentTypeIdentifier,
} from '../../v-person/v-person.typings';
import {
XSimpleFormSelectControlConfig,
XSimpleFormSelectControlOption,
} from '../../typings/simple.form.typings';
import { Validators } from '@angular/forms';
import {
VTagsPresenterAction,
VTagsPresenterModelState,
VTagsPresenterPresentType,
VTagsPresenterActionModel,
isVTagsPresenterPeresentType,
VTagsPresenterPresentTypeIdentfier,
} from '../../tags/v-tags-presenter/v-tags-presenter.typings';
import {
VFilesPresenterAction,
VFilesPresenterModelState,
VFilesPresenterActionModel,
VFilesPresenterPresentType,
isVFilesPresenterPeresentType,
VFilesPresenterPresentTypeIdentfier,
} from '../../files/v-files-presenter/v-files-presenter.typings';
import { defaultVWebinarViewDto, VWebinarViewDto } from '../v-webinar.typings';
import { XSimpleListItemSlideOption } from '../../typings/simple.list-item.typings';
import { VFileInputFilePrperties } from '../../files/v-file-input/v-file-input.typings';
@Component({
selector: 'v-webinar-view',
templateUrl: './v-webinar-view.component.html',
styleUrls: ['./v-webinar-view.component.html'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VWebinarViewComponent extends VBaseDtoComponent<VWebinarViewDto> {
//
//#region Props ...
readonly defaultModel: VWebinarViewDto = {
...defaultVWebinarViewDto,
};
readonly FieldNames = getFieldNames(this.defaultModel);
//
//#region Owner ...
private OWNER_REF: TemplateRef<any>;
@ViewChild('ownerRef')
set ownerRef(v: TemplateRef<any>) {
//
this.OWNER_REF = v;
this.handleAttachTemplates();
}
get ownerRef() {
return this.OWNER_REF;
}
//
@Input()
ownerPersentType: PersonPresentTypeIdentifier = PersonPresentType.AsCard;
//#endregion
//
//#region Files ...
//
@Input()
showFiles: boolean = false;
//
@Input()
showFilesLabel: boolean = false;
@Input()
filesPresentType: VFilesPresenterPresentTypeIdentfier =
VFilesPresenterPresentType.AsList;
@Input()
fileActionsProvider: (
t: XFileDto | File,
) => Array<XSimpleListItemSlideOption>;
//
@Output()
filesStateChanged = new EventEmitter<VFilesPresenterModelState>();
//
canAttachFile: boolean = false;
filesState: VFilesPresenterModelState;
filesPresenterFileProperties: VFileInputFilePrperties = {
fileUploadMaxAllowedFiles: 1,
};
filesPresenterActionProvider = new Subject<VFilesPresenterActionModel>();
//#endregion
//
//#region Tags ...
//
@Input()
showTags: boolean = false;
//
@Input()
showTagsLabel: boolean = false;
@Input()
tagsPresentType: VTagsPresenterPresentTypeIdentfier =
VTagsPresenterPresentType.AsList;
@Input()
tagActionsProvider: (t: string) => Array<XSimpleListItemSlideOption>;
@Output()
tagsStateChanged = new EventEmitter<VTagsPresenterModelState>();
//
canAttachTag: boolean = false;
tagsState: VTagsPresenterModelState;
tagsPresenterActionProvider = new Subject<VTagsPresenterActionModel>();
//#endregion
//
defaultFieldDescriptors: VModelFieldDescriptor<any>[] = [
//
// ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.id,
label: this.ResourceIDs.id,
value: this.defaultModel.id,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Language ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.language,
label: this.ResourceIDs.language,
value: this.defaultModel.language,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Owner ID ...
{
hide: true,
hideLabel: true,
allowFieldForm: false,
key: this.FieldNames.ownerId,
label: this.AppResourceIDs.author_id,
value: this.defaultModel.ownerId,
type: this.ModelFieldTypes.String,
} as VModelFieldDescriptor<string>,
//
// Type ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.type,
value: this.defaultModel.type,
label: this.AppResourceIDs.type,
type: this.ModelFieldTypes.Number,
valueProvider: (value) => {
return this.resourceProvider(getWebinarResourceID(value));
},
formControlConfig: {
index: 0,
propName: this.FieldNames.type,
type: {
type: this.FormControlTypes.Select,
config: {
multiple: false,
options: getWebinarTypeValues().map((k) => {
return {
value: k,
viewValue: this.resourceProvider(getWebinarResourceID(k)),
} as XSimpleFormSelectControlOption<XWebinarType>;
}),
} as XSimpleFormSelectControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
label: this.AppResourceIDs.type,
placeholder: this.AppResourceIDs.type,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<XWebinarType>,
//
// Title ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.title,
value: this.defaultModel.title,
label: this.AppResourceIDs.title,
type: this.ModelFieldTypes.String,
formControlConfig: {
index: 1,
propName: this.FieldNames.title,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
focusHandler: new Subject(),
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.title,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Description ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.description,
value: this.defaultModel.description,
label: this.AppResourceIDs.description,
type: this.ModelFieldTypes.String,
formControlConfig: {
index: 2,
propName: this.FieldNames.description,
type: {
type: this.FormControlTypes.Text,
config: {},
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.description,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.label,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<string>,
//
// Enabled ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.enabled,
value: this.defaultModel.enabled,
label: this.AppResourceIDs.state,
type: this.ModelFieldTypes.Boolean,
valueProvider: (value: boolean) => {
//
let resource: XResourceIdentifier = undefined;
if (!!value) {
resource = this.AppResourceIDs.enabled;
} else {
resource = this.AppResourceIDs.disabled;
}
//
return this.resourceProvider(resource);
},
formControlConfig: {
index: 3,
propName: this.FieldNames.enabled,
type: {
type: this.FormControlTypes.CheckBox,
config: {},
},
validators: {},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
label: this.AppResourceIDs.state,
placeholder: this.AppResourceIDs.state,
appearance: this.FormControlAppearances.Outline,
icons: {},
},
},
} as VModelFieldDescriptor<boolean>,
//
// Created On ...
{
hide: false,
hideLabel: false,
allowFieldForm: true,
key: this.FieldNames.createdOn,
value: this.defaultModel.createdOn,
label: this.AppResourceIDs.created_on,
type: this.ModelFieldTypes.Date,
valueProvider: (value: Date) => {
//
if (isNullOrUndefined(value)) {
return '';
}
//
return this.applyDefaultDate(value);
},
formControlConfig: {
index: 2,
propName: this.FieldNames.createdOn,
type: {
type: this.FormControlTypes.Date,
config: {
iconColor: this.ColorNames.Tertiary,
toggleIcon: this.IconNames.calendar,
applyStateColor: true,
datePickerPosition: XFormDatePickerControlPickerPosition.Prefix,
} as XFormDatePickerControlConfig,
},
validators: {
validators: [Validators.required],
},
eventHandlers: {},
appearance: {
hidden: false,
disabled: false,
placeholder: this.AppResourceIDs.created_on,
appearance: this.FormControlAppearances.Outline,
icons: {
prefix: {
applyStateColor: true,
name: this.IconNames.calendar,
color: this.ColorNames.Tertiary,
},
},
},
},
} as VModelFieldDescriptor<Date>,
//
// Owner ...
{
hide: false,
hideLabel: false,
showContentBelowLabel: true,
key: this.FieldNames.owner,
value: this.defaultModel.owner,
label: this.AppResourceIDs.author,
type: this.ModelFieldTypes.Object,
hasCustomTemplate: true,
template: undefined,
} as VModelFieldDescriptor<XPersonDto>,
];
//#endregion
//
//#region LifeCycles ...
afterViewInit(): void {
super.afterViewInit();
//
this.handleAttachTemplates();
this.handleCanAttachTagState();
this.handleCanAttachFileState();
}
onChange(changeKeys: string[]): void {
super.onChange(changeKeys);
//
let isChanged = false;
let applyChanges = false;
//
// Model Changed ...
isChanged = changeKeys.includes(nameofInstance(this, 'model'));
if (isChanged) {
//
if (!isNullOrUndefined(this.model)) {
//
applyChanges = true;
this.handleCanAttachTagState();
this.handleCanAttachFileState();
}
}
//
this.handleAttachTemplates();
//
if (applyChanges) {
this.detectChanges();
}
}
//#endregion
//
//#region UI Provider ...
canShowOwner(owner: any) {
return !isNullOrUndefined(owner) && !isDefaultPersonDto(owner);
}
canShowCustomContent() {
return this.canShowTags() || this.canShowFiles();
}
canShowTags() {
return (
!isNullOrUndefined(this.tagsPresentType) &&
isVTagsPresenterPeresentType(this.tagsPresentType) &&
this.tagsPresentType !== VTagsPresenterPresentType.AsNone
);
}
canShowFiles() {
return (
!isNullOrUndefined(this.filesPresentType) &&
isVFilesPresenterPeresentType(this.filesPresentType) &&
this.filesPresentType !== VFilesPresenterPresentType.AsNone
);
}
/**
* Tags Reader Task ...
*/
getTagsReaderTask() {
//
let result = undefined;
//
if (!isNullOrUndefined(this.model) && !isNullOrEmptyString(this.model.id)) {
result =
this.sharedService.saherElmAPIService.webinars.webinarProviderService.getTags(
this.model.id,
);
}
//
return result;
}
/**
* Files Reader Task ...
*/
getFilesReaderTask() {
//
let result = undefined;
//
if (!isNullOrUndefined(this.model) && !isNullOrEmptyString(this.model.id)) {
result =
this.sharedService.saherElmAPIService.webinars.webinarProviderService.getFiles(
this.model.id,
);
}
//
return result;
}
/**
* in this special case we have to restrict
* attached files to 1 ...
*
* @returns
*/
getCanAttachFile() {
//
let result = this.canAttachFile;
//
if (!isNullOrUndefined(this.filesState)) {
//
let filesCount = 0;
//
if (this.filesState.hasOriginals) {
filesCount += this.filesState.originals!.length;
}
//
if (this.filesState.hasNews) {
filesCount += this.filesState.news!.length;
}
//
if (this.filesState.hasRemoveds) {
filesCount -= this.filesState.removeds!.length;
}
//
result = result && filesCount < 1 && filesCount >= 0;
}
//
return result;
}
//#endregion
//
//#region Abstracts ...
/**
* Validate Webinar Model ...
*
* @param model
* @returns
*/
validate(
model?: Partial<VWebinarViewDto>,
): VModelValidationResult<VWebinarViewDto> {
//
let result: VModelValidationResult<VWebinarViewDto> = undefined;
//
let isValid = false;
const validationErrors: string[] = [];
//
model = {
...this.model,
...model,
};
const normalized = this.normalize(model);
//
// Handle Validation ...
isValid =
!isNullOrUndefined(normalized) && !this.isSameAsDefault(normalized);
if (!isValid) {
//
validationErrors.push(
this.getExceptionMessage(XExceptionIDs.InvalidArgs),
);
} else {
//
// Check Other Validations ...
//
// ID ...
//
// Language ...
isValid = !isNullOrEmptyString(normalized.language);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.language,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Title ...
isValid = !isNullOrEmptyString(normalized.title);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.title,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Description ...
isValid = !isNullOrEmptyString(normalized.description);
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.AppResourceIDs.description,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
//
// Type ...
isValid = normalized.type >= 0;
if (!isValid) {
//
validationErrors.push(
`${this.resourceProvider(
this.ResourceIDs.type,
)}: ${this.getExceptionMessage(XExceptionIDs.InvalidArgs)}`,
);
}
}
//
// Handle Result ...
//
isValid = !this.hasChild(validationErrors);
//
const isValidForAdd = isNullOrEmptyString(normalized.id);
const isValidForUpdate = !isNullOrEmptyString(normalized.id);
//
result = {
isValid,
normalized,
isValidForAdd,
isValidForUpdate,
validationErrors,
};
//
return result;
}
/**
* Prepare Specified Model ...
*
* @param model
* @returns
*/
prepare(model?: VWebinarViewDto): VWebinarViewDto {
return prepareVWebinarViewDtoFields(model);
}
/**
* Handle Register Focus Handler for User Select By ...
*
* @param to
* @param from
*/
onPresentTypeChanged(to: string, from: string): void {
super.onPresentTypeChanged(to, from);
//
this.handleCanAttachTagState();
this.handleCanAttachFileState();
//
if (to !== this.PresentTypes.AsForm) {
this.isFocusedHandlerRegistered = false;
}
//
this.registerFieldFocusHandler(this.FieldNames.title);
}
/**
* Handle Register Field Focus Handler ...
*/
onFieldDescriptorsReady(): void {
super.onFieldDescriptorsReady();
//
this.registerFieldFocusHandler(this.FieldNames.title);
}
/**
* Try to Override Model Actions on Tags Presenter ...
*
* @param action
*/
onDtoModelAction(action: VBaseDtoModelActionIdentifier): void {
//
// Validate ...
if (isNullOrEmptyString(action)) {
return;
}
//
// Cancel ...
if (action === VBaseDtoModelAction.Cancel) {
return;
}
//
// Reset ...
if (action === VBaseDtoModelAction.Reset) {
//
// Reset Files ...
this.filesPresenterActionProvider.next({
action: VFilesPresenterAction.Reset,
});
//
// Reset Tags ...
this.tagsPresenterActionProvider.next({
action: VTagsPresenterAction.Reset,
});
return;
}
}
/**
* Check Can Reset Model or not ...
* Override ...
*
* @returns
*/
canReset() {
//
const modelStateChanged = this.isModelStateChanged();
const filesStateChanged =
!isNullOrUndefined(this.filesState) &&
(this.filesState.hasNews || this.filesState.hasRemoveds);
const tagsStateChanged =
!isNullOrUndefined(this.tagsState) &&
(this.tagsState.hasNews || this.tagsState.hasRemoveds);
//
const result = modelStateChanged || filesStateChanged || tagsStateChanged;
//
return result;
}
/**
* Check Can Add or Update Model or not ...
* Override ...
*
* @returns
*/
canAddOrUpdate() {
//
const state = this.getState();
const modelStateChanged = this.isModelStateHasValidChanges();
const filesStateChanged =
!isNullOrUndefined(this.filesState) &&
(this.filesState.hasNews || this.filesState.hasRemoveds);
const tagsStateChanged =
!isNullOrUndefined(this.tagsState) &&
(this.tagsState.hasNews || this.tagsState.hasRemoveds);
//
let result = false;
if (modelStateChanged) {
result = modelStateChanged;
} else {
//
result =
state.isValid &&
!state.isDefault &&
!this.hasChild(state.errors) &&
((!isNullOrUndefined(this.tagsState) && tagsStateChanged) ||
(!isNullOrUndefined(this.filesState) && filesStateChanged));
}
//
return result;
}
/**
* Handle DTO State Management ...
*/
handleDtoStateManagement(): void {
//
// By Calling this method MODEL_STATE Filled automatically ...
const state = this.getState();
//
// Check DTO State Has Locked or not ...
const filesHasLock =
!isNullOrUndefined(this.filesState) &&
(this.filesState.hasNews || this.filesState.hasRemoveds);
const tagsHasLock =
!isNullOrUndefined(this.tagsState) &&
(this.tagsState.hasNews || this.tagsState.hasRemoveds);
const modelHasLock = isModelStateLocked({ ...this.modelState });
//
const isLocked = filesHasLock || tagsHasLock || modelHasLock;
//
this.lock = isLocked;
this.detectChanges();
//
this.changeNotifier.emit(this.modelState);
}
//#endregion
//
//#region Handlers ...
/**
* Handle Tags Presenter State Changed ...
*
* @param event
* @returns
*/
handleTagsModelStateChanged(event: VTagsPresenterModelState) {
//
if (isNullOrUndefined(event)) {
return;
}
//
this.tagsState = {
...event,
};
//
// Notify Change Happens ...
this.changeSubject.next();
this.tagsStateChanged.emit(this.tagsState);
}
/**
* Handle Files Presenter State Changed ...
*
* @param event
* @returns
*/
handleFilesModelStateChanged(event: VFilesPresenterModelState) {
//
if (isNullOrUndefined(event)) {
return;
}
//
this.filesState = {
...event,
};
//
// Notify Change Happens ...
this.changeSubject.next();
this.filesStateChanged.emit(this.filesState);
}
//#endregion
//
//#region Private ...
/**
* Handle Attach Templates to Field Descriptors ...
*
* @returns
*/
private handleAttachTemplates() {
//
let applyChanges = false;
//
if (isNullOrUndefined(this.ownerRef)) {
//
// setTimeout(() => {
// this.handleAttachTemplates();
// }, 100);
return;
}
//
this.defaultFieldDescriptors = this.fieldDescriptors.map((fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.owner &&
fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.ownerRef
: fd.template,
};
});
//
if (!isNullOrUndefined(this.providedFieldDescriptors)) {
//
this.providedFieldDescriptors = this.providedFieldDescriptors.map(
(fd) => {
//
return {
...fd,
template:
fd.key === this.FieldNames.owner &&
fd.hasCustomTemplate &&
isNullOrUndefined(fd.template)
? this.ownerRef
: fd.template,
};
},
);
}
//
this.resetFieldDescriptors();
}
/**
* Handle Check Can Attach Tags or not ...
*/
private handleCanAttachTagState() {
//
let result = false;
//
if (!isNullOrUndefined(this.model)) {
//
// Check Present Type ...
if (this.presentType === VBaseDtoPresentType.AsForm) {
result = true;
}
}
//
this.canAttachTag = result;
}
/**
* Handle Check Can Attach Files or not ...
*/
private handleCanAttachFileState() {
//
let result = false;
//
if (!isNullOrUndefined(this.model)) {
//
// Check Present Type ...
if (this.presentType === VBaseDtoPresentType.AsForm) {
result = true;
}
}
//
this.canAttachFile = result;
}
//
//#region Focus Handler ...
//
isFocusedHandlerRegistered = false;
/**
* Register Focus Handler ...
*
* @param field
* @returns
*/
private registerFieldFocusHandler(field: string) {
//
if (this.isFocusedHandlerRegistered) {
return;
}
//
if (this.presentType === this.PresentTypes.AsForm) {
//
// Make Sure runs when Model Form Config Prepared ...
if (isNullOrUndefined(this.modelFormConfig)) {
//
setTimeout(() => this.registerFieldFocusHandler(field), 100);
return;
}
//
this.isFocusedHandlerRegistered = true;
this.registerFocusHandler(this.modelFormConfig.name, field);
}
}
//#endregion
//#endregion
}
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\environments\environment.prod.ts
import { XConfig, xSharedConfig } from '../app/config/app.config';
export const environment = {
production: true,
configurations: {
//
// Attach Shared Config ...
...xSharedConfig,
//
// Disable SSL Check ...
rejectUnauthorized: true,
//
// Set Base Api Path ...
baseUrl: 'https://api.mabsut.ir/',
//
hubServerRoute: 'https://api.mabsut.ir/',
//
// Set Production State ...
isProd: true,
//
// Set Logging state ...
enableLogging: false,
baseLogTag: 'xSaherElmClient',
} as XConfig
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\environments\environment.ts
import { XConfig, xSharedConfig } from '../app/config/app.config';
export const environment = {
production: false,
configurations: {
//
// Attach Shared Config ...
...xSharedConfig,
//
// Disable SSL Check ...
rejectUnauthorized: true,
//
// Set Base Api Path ...
baseUrl: 'https://localhost:5001/',
// baseUrl: 'https://192.168.1.110:5001/',
//
hubServerRoute: 'https://localhost:5001/',
// hubServerRoute: 'https://192.168.1.110:5001/',
//
// Set Production State ...
isProd: false,
//
// Set Logging state ...
enableLogging: true,
baseLogTag: 'xSaherElmClient',
} as XConfig,
};
### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xClient\src\theme\theme.scss
//
// XFramework Color Mechanism ...
:root {
/** Primary **/
--x-color-primary: #3880ff;
--x-color-primary-shade: #3171e0;
--x-color-primary-tint: #4c8dff;
--x-color-primary-contrast: #ffffff;
--x-color-primary-shade-contrast: #ffffff;
--x-color-primary-tint-contrast: #ffffff;
/** Secondary **/
--x-color-secondary: #0cd1e8;
--x-color-secondary-shade: #0bb8cc;
--x-color-secondary-tint: #24d6ea;
--x-color-secondary-contrast: #ffffff;
--x-color-secondary-shade-contrast: #ffffff;
--x-color-secondary-tint-contrast: #ffffff;
/** Tertiary **/
--x-color-tertiary: #7044ff;
--x-color-tertiary-shade: #633ce0;
--x-color-tertiary-tint: #7e57ff;
--x-color-tertiary-contrast: #ffffff;
--x-color-tertiary-shade-contrast: #ffffff;
--x-color-tertiary-tint-contrast: #ffffff;
/** Success **/
--x-color-success: #10dc60;
--x-color-success-shade: #0ec254;
--x-color-success-tint: #28e070;
--x-color-success-contrast: #ffffff;
--x-color-success-shade-contrast: #ffffff;
--x-color-success-tint-contrast: #ffffff;
/** Warning **/
--x-color-warning: #ffce00;
--x-color-warning-shade: #e0b500;
--x-color-warning-tint: #ffd31a;
--x-color-warning-contrast: #ffffff;
--x-color-warning-shade-contrast: #ffffff;
--x-color-warning-tint-contrast: #ffffff;
/** Danger **/
--x-color-danger: #f04141;
--x-color-danger-shade: #d33939;
--x-color-danger-tint: #f25454;
--x-color-danger-contrast: #ffffff;
--x-color-danger-shade-contrast: #ffffff;
--x-color-danger-tint-contrast: #ffffff;
/** Dark **/
--x-color-dark: #222428;
--x-color-dark-shade: #1e2023;
--x-color-dark-tint: #383a3e;
--x-color-dark-contrast: #ffffff;
--x-color-dark-shade-contrast: #ffffff;
--x-color-dark-tint-contrast: #ffffff;
/** Medium **/
--x-color-medium: #989aa2;
--x-color-medium-shade: #86888f;
--x-color-medium-tint: #a2a4ab;
--x-color-medium-contrast: #ffffff;
--x-color-medium-shade-contrast: #ffffff;
--x-color-medium-tint-contrast: #ffffff;
/** Light **/
--x-color-light: #f4f5f8;
--x-color-light-shade: #d7d8da;
--x-color-light-tint: #f5f6f9;
--x-color-light-contrast: #000000;
--x-color-light-shade-contrast: #000000;
--x-color-light-tint-contrast: #000000;
}
/* Core CSS required for Ionic components to work properly */
@import "~@ionic/angular/css/core.css";
/* Basic CSS for apps built with Ionic */
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";
@import "~@ionic/angular/css/display.css";
/* Optional CSS utils that can be commented out */
@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";
/* XFramework Theme */
@import "~x-framework-components/theme/theme.scss";
/* Override Default XFramework's Body Background Color */
body {
background-color: #ffffff;
}
.code-block {
direction: ltr;
color: white;
margin: {
top: 6px;
bottom: 8px;
}
// white-space: pre;
word-spacing: normal;
word-break: normal;
background-color: black;
//
overflow: hidden;
//
.x-header {
direction: var(--ui-direction) !important;
}
}
//
//#region Card ..
$border-radius: 8px;
x-card.v-card,
.v-card > x-card {
.x-title {
margin-bottom: 8px;
font-size: 16px !important;
color: var(--x-color-primary-tint-contrast);
}
.x-sub-title {
margin-bottom: 0;
font-size: 12px !important;
color: var(--x-color-primary-tint-contrast);
}
mat-card {
border-top-left-radius: $border-radius;
border-top-right-radius: $border-radius;
}
mat-card.mobile {
.mat-card-header {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
mat-card-header {
@include transition-duration();
//
background-color: var(--x-color-primary-tint);
//
border-top-left-radius: $border-radius;
border-top-right-radius: $border-radius;
}
mat-card-header:hover {
background-color: var(--x-color-primary-shade);
}
}
x-card.v-card.v-no-margin-card,
.v-card.v-no-margin-card > x-card {
mat-card-content {
padding: 0 !important;
margin: 0 !important;
}
}
//#endregion
.unselecteable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
//
* {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
}
.x-auto-complete-margin {
margin-top: -1.34375em;
}
.center-vertical {
position: relative;
top: 50%;
transform: translateY(-50%);
}
.text-truncate {
display: inline-block;
overflow-x: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap;
min-width: 0;
}
.x-dynamic-slot {
flex: 0 auto !important;
}
x-button.x-disabled,
ion-button.button-disabled {
pointer-events: none;
opacity: 0.8;
filter: grayscale(100%);
cursor: not-allowed;
}