57059 lines
1.4 MiB
Plaintext
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 = `<x-icon name='${item.data}'></x-icon>`;
|
|
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;
|
|
}
|