feat: expand cockpit integrations and support workflows
Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
@@ -1,38 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
using FasdDesktopUi.Basics.Models;
|
||||
|
||||
using C4IT.FASD.Cockpit.Communication;
|
||||
using C4IT.Logging;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
using C4IT.MultiLanguage;
|
||||
using C4IT.FASD.Base;
|
||||
|
||||
namespace FasdDesktopUi.Basics.UserControls
|
||||
{
|
||||
public partial class SearchBar : UserControl, IBlurInvoker
|
||||
public partial class SearchBar : UserControl
|
||||
{
|
||||
private const int constTimeBetweenKeystrokes = 250;
|
||||
|
||||
#region Propeties
|
||||
public enum eSearchStatus { message, active, fixedResult };
|
||||
public eSearchStatus SearchStatus { get; private set; } = eSearchStatus.message;
|
||||
|
||||
#region Search Value Property
|
||||
|
||||
private readonly DispatcherTimer _searchTimer = new DispatcherTimer();
|
||||
|
||||
private static CancellationTokenSource _currentSearchTaskToken = null;
|
||||
private static Guid? _currentSearchTaskId = null;
|
||||
private static readonly object _currentSearchTaskLock = new object();
|
||||
#region Dependency Properties
|
||||
|
||||
public static readonly DependencyProperty SearchButtonSizeProperty =
|
||||
DependencyProperty.Register(nameof(SearchButtonSize), typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));
|
||||
|
||||
public double SearchButtonSize
|
||||
{
|
||||
get => (double)GetValue(SearchButtonSizeProperty);
|
||||
set => SetValue(SearchButtonSizeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CloseButtonSizeProperty =
|
||||
DependencyProperty.Register(nameof(CloseButtonSize), typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));
|
||||
|
||||
public double CloseButtonSize
|
||||
{
|
||||
get => (double)GetValue(CloseButtonSizeProperty);
|
||||
set => SetValue(CloseButtonSizeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShowCloseButtonProperty =
|
||||
DependencyProperty.Register(nameof(ShowCloseButton), typeof(bool), typeof(SearchBar), new PropertyMetadata(true));
|
||||
|
||||
public bool ShowCloseButton
|
||||
{
|
||||
get => (bool)GetValue(ShowCloseButtonProperty);
|
||||
set => SetValue(ShowCloseButtonProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShowSearchButtonProperty =
|
||||
DependencyProperty.Register(nameof(ShowSearchButton), typeof(bool), typeof(SearchBar), new PropertyMetadata(true));
|
||||
|
||||
public bool ShowSearchButton
|
||||
{
|
||||
get => (bool)GetValue(ShowSearchButtonProperty);
|
||||
set => SetValue(ShowSearchButtonProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlaceholderTextProperty =
|
||||
DependencyProperty.Register(nameof(PlaceholderText), typeof(string), typeof(SearchBar), new PropertyMetadata(string.Empty));
|
||||
|
||||
public string PlaceholderText
|
||||
{
|
||||
get => (string)GetValue(PlaceholderTextProperty);
|
||||
set => SetValue(PlaceholderTextProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DebounceIntervalProperty =
|
||||
DependencyProperty.Register(nameof(DebounceInterval), typeof(TimeSpan), typeof(SearchBar),
|
||||
new PropertyMetadata(TimeSpan.FromMilliseconds(250), OnDebounceIntervalChanged));
|
||||
|
||||
public TimeSpan DebounceInterval
|
||||
{
|
||||
get => (TimeSpan)GetValue(DebounceIntervalProperty);
|
||||
set => SetValue(DebounceIntervalProperty, value);
|
||||
}
|
||||
|
||||
private static void OnDebounceIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((SearchBar)d)._searchTimer.Interval = (TimeSpan)e.NewValue;
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsInputEnabledProperty =
|
||||
DependencyProperty.Register(nameof(IsInputEnabled), typeof(bool), typeof(SearchBar), new PropertyMetadata(true));
|
||||
|
||||
public bool IsInputEnabled
|
||||
{
|
||||
get => (bool)GetValue(IsInputEnabledProperty);
|
||||
set => SetValue(IsInputEnabledProperty, value);
|
||||
}
|
||||
|
||||
public SolidColorBrush SearchBackground
|
||||
{
|
||||
get { return (SolidColorBrush)GetValue(SearchBackgroundProperty); }
|
||||
set { SetValue(SearchBackgroundProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SearchBackgroundProperty =
|
||||
DependencyProperty.Register(nameof(SearchBackground), typeof(SolidColorBrush), typeof(SearchBar), new PropertyMetadata(null));
|
||||
|
||||
#endregion
|
||||
|
||||
#region SearchValue
|
||||
|
||||
private string _searchValue = null;
|
||||
public string SearchValue
|
||||
@@ -43,111 +104,49 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
if (value != _searchValue)
|
||||
{
|
||||
_searchValue = value;
|
||||
if (SearchStatus == eSearchStatus.active)
|
||||
if (IsInputEnabled)
|
||||
HandleSearchValueChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<enumF4sdSearchResultClass, UIElement> _searchIcons = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<string> SearchValueChanged;
|
||||
|
||||
public delegate Task ChangedSearchValueDelegate(cFilteredResults results);
|
||||
public static readonly RoutedEvent CancelledSearchEvent =
|
||||
EventManager.RegisterRoutedEvent(nameof(CancelledSearch), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchBar));
|
||||
|
||||
public ChangedSearchValueDelegate ChangedSearchValue { get; set; } = null;
|
||||
|
||||
public Action CancledSearchAction { get; set; }
|
||||
|
||||
public bool BlurInvoker_IsActive => IsVisible;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dependency Propertys
|
||||
|
||||
public double SearchButtonSize
|
||||
public event RoutedEventHandler CancelledSearch
|
||||
{
|
||||
get { return (double)GetValue(SearchButtonSizeProperty); }
|
||||
set { SetValue(SearchButtonSizeProperty, value); }
|
||||
add => AddHandler(CancelledSearchEvent, value);
|
||||
remove => RemoveHandler(CancelledSearchEvent, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SearchButtonSizeProperty =
|
||||
DependencyProperty.Register("SearchButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));
|
||||
|
||||
public double CloseButtonSize
|
||||
{
|
||||
get { return (double)GetValue(CloseButtonSizeProperty); }
|
||||
set { SetValue(CloseButtonSizeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CloseButtonSizeProperty =
|
||||
DependencyProperty.Register("CloseButtonSize", typeof(double), typeof(SearchBar), new PropertyMetadata(30.0));
|
||||
#endregion
|
||||
|
||||
public SearchBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_searchIcons = new Dictionary<enumF4sdSearchResultClass, UIElement>()
|
||||
{
|
||||
{ enumF4sdSearchResultClass.Phone, PhoneCallIndicator },
|
||||
{ enumF4sdSearchResultClass.User, UserCallIndicator },
|
||||
{ enumF4sdSearchResultClass.Computer, ComputerCallIndicator }
|
||||
};
|
||||
}
|
||||
|
||||
public void BlurInvoker_IsActiveChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
BlurInvoker.InvokeVisibilityChanged(this, new EventArgs());
|
||||
|
||||
if (e.NewValue is bool newVisible)
|
||||
{
|
||||
if (newVisible == false)
|
||||
{
|
||||
SearchTextBox.IsEnabled = true;
|
||||
SearchButton.Visibility = Visibility.Visible;
|
||||
WarningIndicator.Visibility = Visibility.Collapsed;
|
||||
TicketOverview.Instance?.ResetSelection();
|
||||
foreach (var Entry in _searchIcons.Values)
|
||||
Entry.Visibility = Visibility.Collapsed;
|
||||
|
||||
}
|
||||
SetApiStatus();
|
||||
}
|
||||
|
||||
if (Visibility == Visibility.Visible)
|
||||
{
|
||||
var _h = Dispatcher.BeginInvoke((Action)delegate { Keyboard.Focus(SearchTextBox); }, DispatcherPriority.Render);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
SearchStatus = eSearchStatus.message;
|
||||
SearchTextBox.Text = null;
|
||||
}
|
||||
|
||||
public void ActivateManualSearch()
|
||||
{
|
||||
SearchStatus = eSearchStatus.active;
|
||||
SearchButton.Visibility = Visibility.Visible;
|
||||
WarningIndicator.Visibility = Visibility.Collapsed;
|
||||
foreach (var Entry in _searchIcons.Values)
|
||||
Entry.Visibility = Visibility.Collapsed;
|
||||
|
||||
SearchTextBox.Text = null;
|
||||
SearchTextBox.IsEnabled = true;
|
||||
SearchTextBox.Focus();
|
||||
Keyboard.Focus(SearchTextBox);
|
||||
}
|
||||
|
||||
public void SetSearchText(string search)
|
||||
{
|
||||
SearchStatus = eSearchStatus.active;
|
||||
SearchTextBox.Text = search;
|
||||
SearchTextBox.CaretIndex = search.Length;
|
||||
SearchTextBox.CaretIndex = search?.Length ?? 0;
|
||||
SearchTextBox.Focus();
|
||||
Keyboard.Focus(SearchTextBox);
|
||||
}
|
||||
|
||||
public void FocusInput()
|
||||
{
|
||||
SearchTextBox.Focus();
|
||||
Keyboard.Focus(SearchTextBox);
|
||||
}
|
||||
@@ -157,165 +156,18 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
Dispatcher.Invoke(() => SearchSpinner.Visibility = visibility);
|
||||
}
|
||||
|
||||
public async Task SetFixedSearchResultAsync(enumF4sdSearchResultClass Class, string search, cFilteredResults result)
|
||||
{
|
||||
SearchStatus = eSearchStatus.fixedResult;
|
||||
SearchTextBox.IsEnabled = false;
|
||||
SearchButton.Visibility = Visibility.Collapsed;
|
||||
WarningIndicator.Visibility = Visibility.Collapsed;
|
||||
|
||||
foreach (var entryIcon in _searchIcons)
|
||||
{
|
||||
if (Class == entryIcon.Key)
|
||||
entryIcon.Value.Visibility = Visibility.Visible;
|
||||
else
|
||||
entryIcon.Value.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
SearchTextBox.Text = search;
|
||||
|
||||
if (ChangedSearchValue != null)
|
||||
await ChangedSearchValue.Invoke(result);
|
||||
}
|
||||
|
||||
public void SetApiStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var Status = cConnectionStatusHelper.Instance?.ApiConnectionStatus;
|
||||
if (Status == null)
|
||||
return;
|
||||
|
||||
bool IsEnabled = false;
|
||||
string txt = null;
|
||||
switch (Status)
|
||||
{
|
||||
case cConnectionStatusHelper.enumOnlineStatus.online:
|
||||
IsEnabled = true;
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.connectionError:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ConnectError");
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.incompatibleServerVersion:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IncompatibleVersion");
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.serverStarting:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerStarting");
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.serverNotConfigured:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.ServerNotConfigured");
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.illegalConfig:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.IllegalConfig");
|
||||
break;
|
||||
case cConnectionStatusHelper.enumOnlineStatus.unauthorized:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Unauthorized");
|
||||
break;
|
||||
default:
|
||||
txt = cMultiLanguageSupport.GetItem("Searchbar.Status.Offline");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
SearchStatus = eSearchStatus.message;
|
||||
SearchTextBox.Text = txt;
|
||||
SearchTextBox.IsEnabled = false;
|
||||
WarningIndicator.Visibility = Visibility.Visible;
|
||||
SearchButton.Visibility = Visibility.Collapsed;
|
||||
foreach (var Entry in _searchIcons.Values)
|
||||
Entry.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CancelRunningSearchTaskAsync()
|
||||
{
|
||||
// cancel the search task, in case of a running search task
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
if (_currentSearchTaskToken != null)
|
||||
{
|
||||
LogEntry("Canceling current running search...");
|
||||
_currentSearchTaskToken.Cancel(true);
|
||||
_currentSearchTaskToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
// send a stop to the server, in case of a running search task
|
||||
Guid? currentTaskId = null;
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
if (_currentSearchTaskId != null)
|
||||
{
|
||||
currentTaskId = _currentSearchTaskId;
|
||||
_currentSearchTaskId = null;
|
||||
}
|
||||
}
|
||||
if (currentTaskId != null)
|
||||
{
|
||||
LogEntry("Sending a search stop...");
|
||||
await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)currentTaskId, CancellationToken.None);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async void UpdateSearchResultsTick(object sender, EventArgs e)
|
||||
{
|
||||
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
||||
|
||||
try
|
||||
{
|
||||
_searchTimer.Stop();
|
||||
|
||||
await CancelRunningSearchTaskAsync();
|
||||
|
||||
lock (_currentSearchTaskLock)
|
||||
{
|
||||
LogEntry("Start running new search...");
|
||||
SearchValueChanged?.Invoke(this, SearchValue);
|
||||
}
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (CM != null) LogMethodEnd(CM);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSearchValueChanged()
|
||||
{
|
||||
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
||||
if (_searchTimer.IsEnabled)
|
||||
_searchTimer.Stop();
|
||||
|
||||
try
|
||||
{
|
||||
if (!SearchTextBox.IsEnabled)
|
||||
return;
|
||||
_searchTimer.Start();
|
||||
}
|
||||
|
||||
if (_searchTimer.IsEnabled)
|
||||
_searchTimer.Stop();
|
||||
|
||||
_searchTimer.Start();
|
||||
|
||||
_ = Task.Run(async () => { await CancelRunningSearchTaskAsync(); });
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (CM != null) LogMethodEnd(CM);
|
||||
}
|
||||
private void SearchTimerTick(object sender, EventArgs e)
|
||||
{
|
||||
_searchTimer.Stop();
|
||||
SearchValueChanged?.Invoke(this, SearchValue);
|
||||
}
|
||||
|
||||
private void SearchButton_Click(object sender, InputEventArgs e)
|
||||
@@ -323,40 +175,28 @@ namespace FasdDesktopUi.Basics.UserControls
|
||||
SearchTextBox.Focus();
|
||||
}
|
||||
|
||||
#region CancledSearch Event
|
||||
|
||||
public static readonly RoutedEvent CancledSearchEvent = EventManager.RegisterRoutedEvent("CancledSearch", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SearchBar));
|
||||
|
||||
public event RoutedEventHandler CancledSearch
|
||||
{
|
||||
add { AddHandler(CancledSearchEvent, value); }
|
||||
remove { RemoveHandler(CancledSearchEvent, value); }
|
||||
}
|
||||
|
||||
private void RaiseCancledSearchEvent()
|
||||
{
|
||||
RoutedEventArgs newEventArgs = new RoutedEventArgs(CancledSearchEvent);
|
||||
RaiseEvent(newEventArgs);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void CloseButton_Click(object sender, InputEventArgs e)
|
||||
{
|
||||
RaiseCancledSearchEvent();
|
||||
|
||||
if (cConnectionStatusHelper.Instance?.ApiConnectionStatus == cConnectionStatusHelper.enumOnlineStatus.online)
|
||||
SearchTextBox.Text = null;
|
||||
|
||||
CancledSearchAction?.Invoke();
|
||||
RaiseEvent(new RoutedEventArgs(CancelledSearchEvent));
|
||||
}
|
||||
|
||||
private void SearchBar_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_searchTimer.Stop();
|
||||
_searchTimer.Interval = TimeSpan.FromMilliseconds(constTimeBetweenKeystrokes);
|
||||
_searchTimer.Tick += UpdateSearchResultsTick;
|
||||
_searchTimer.Interval = DebounceInterval;
|
||||
_searchTimer.Tick += SearchTimerTick;
|
||||
BackgroundBorder.CornerRadius = new CornerRadius(BackgroundBorder.ActualHeight / 2.0);
|
||||
}
|
||||
|
||||
internal void ActivateManualSearch()
|
||||
{
|
||||
Search.Clear();
|
||||
Search.IsInputEnabled = true;
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
Search.FocusInput();
|
||||
}), DispatcherPriority.Input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user