1414 lines
57 KiB
C#
1414 lines
57 KiB
C#
using C4IT.FASD.Base;
|
||
using C4IT.FASD.Cockpit.Communication;
|
||
using C4IT.Logging;
|
||
using C4IT.MultiLanguage;
|
||
using FasdDesktopUi.Basics;
|
||
using FasdDesktopUi.Basics.Enums;
|
||
using FasdDesktopUi.Basics.Helper;
|
||
using FasdDesktopUi.Basics.Models;
|
||
using FasdDesktopUi.Basics.Services;
|
||
using FasdDesktopUi.Basics.Services.Models;
|
||
using FasdDesktopUi.Basics.Services.RelationService;
|
||
using FasdDesktopUi.Basics.Services.SupportCaseSearchService;
|
||
using FasdDesktopUi.Basics.UiActions;
|
||
using FasdDesktopUi.Basics.UserControls;
|
||
using Newtonsoft.Json;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Input;
|
||
using System.Windows.Interop;
|
||
using System.Windows.Media;
|
||
using System.Windows.Threading;
|
||
using static C4IT.Logging.cLogManager;
|
||
using static FasdDesktopUi.Basics.UserControls.TicketOverview;
|
||
|
||
namespace FasdDesktopUi.Pages.SearchPage
|
||
{
|
||
public partial class SearchPageView : Window, ISearchUiProvider
|
||
{
|
||
private static SearchPageView _instance = null;
|
||
private const int WM_NCHITTEST = 0x0084;
|
||
private const int HTTRANSPARENT = -1;
|
||
public static SearchPageView Instance
|
||
{
|
||
get
|
||
{
|
||
return _instance ?? (_instance = new SearchPageView());
|
||
}
|
||
}
|
||
|
||
private cF4sdPipeServer _pipeServer;
|
||
private cHotKeyManager _hotKeyManager;
|
||
|
||
private CancellationTokenSource _searchCancellationTokenSource = new CancellationTokenSource();
|
||
|
||
private bool _isActivating = false;
|
||
|
||
private cF4sdApiSearchResultRelation preSelectedRelation = null;
|
||
private bool _renderTicketOverviewUserNames = false;
|
||
private readonly HashSet<cSearchHistorySearchResultEntry> _ticketOverviewHistoryEntries = new HashSet<cSearchHistorySearchResultEntry>();
|
||
private const string DemoTicketHasDetailsInfoKey = "Demo.HasTicketDetails";
|
||
// Event zum auslösen wenn Toggle geändert wird
|
||
public event EventHandler<bool> FilterToggleCheckedChanged;
|
||
|
||
// Aktueller Zustand der Checkbox
|
||
public bool IsFilterChecked => FilterCheckbox.IsChecked == true;
|
||
|
||
public SupportCaseSearchService SearchService { get; } = new SupportCaseSearchService(new RelationService());
|
||
|
||
private SearchPageView()
|
||
{
|
||
try
|
||
{
|
||
InitializeComponent();
|
||
Visibility = Visibility.Visible;
|
||
_instance = this;
|
||
|
||
// FilterToggleCheckBox-Events registrieren
|
||
FilterCheckbox.Checked += (s, e) => FilterToggleCheckedChanged?.Invoke(this, true);
|
||
FilterCheckbox.Unchecked += (s, e) => FilterToggleCheckedChanged?.Invoke(this, false);
|
||
|
||
Loaded += (s, e) =>
|
||
{
|
||
Hide();
|
||
SearchBarUc.ActivateManualSearch();
|
||
};
|
||
|
||
AddCustomEventHandlers();
|
||
|
||
UiSettingsChanged(null, null);
|
||
|
||
if (TicketOverviewUpdateService.Instance != null)
|
||
{
|
||
TicketOverviewUpdateService.Instance.Start();
|
||
TicketOverviewUpdateService.Instance.OverviewCountsChanged += TicketOverviewUpdateService_OverviewCountsChanged;
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}
|
||
|
||
private void SetSearchResultVisibility(bool isVisible)
|
||
{
|
||
SearchResultBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
|
||
BodyStack_SearchResults.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
|
||
}
|
||
|
||
public void SetSearchHistoryVisibility(bool isVisible)
|
||
{
|
||
SearchHistoryBorder.Visibility = isVisible && !SearchHistory.IsEmpty() ? Visibility.Visible : Visibility.Collapsed;
|
||
BodyStack_SearchResults.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
|
||
}
|
||
|
||
private bool CheckTicketOverviewAvailability()
|
||
{
|
||
if (cFasdCockpitCommunicationBase.Instance.IsDemo())
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
private void SetTicketOverviewVisibility(bool isVisible)
|
||
{
|
||
var b = isVisible;
|
||
if (!CheckTicketOverviewAvailability())
|
||
b = false;
|
||
|
||
BodyStack_TicketOverview.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
TicketOverviewBorder.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
FilterCheckbox.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
RoleLabel.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
OwnTicketsLabel.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
TicketOverviewLabel.Visibility = b ? Visibility.Visible : Visibility.Collapsed;
|
||
}
|
||
|
||
public void ShowLoadingTextItem(string itemText)
|
||
{
|
||
SetSearchHistoryVisibility(false);
|
||
ResultMenu.ShowLoadingTextItem(itemText);
|
||
}
|
||
|
||
public void ShowTicketOverviewPane()
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
bool overviewAlreadyVisible = TicketOverviewBorder.Visibility == Visibility.Visible;
|
||
|
||
SetTicketOverviewVisibility(true);
|
||
|
||
if (!overviewAlreadyVisible)
|
||
{
|
||
SetSearchResultVisibility(false);
|
||
SetSearchHistoryVisibility(false);
|
||
TicketOverviewUc?.ResetSelection();
|
||
}
|
||
TicketOverviewUc?.RefreshHighlightState(IsFilterChecked);
|
||
|
||
var app = Application.Current as FasdDesktopUi.App;
|
||
app?.ClearTicketOverviewTrayNotification();
|
||
});
|
||
}
|
||
|
||
internal void CloseTicketOverviewResults()
|
||
{
|
||
_renderTicketOverviewUserNames = false;
|
||
ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
|
||
ResultMenu.SetHeaderText(string.Empty);
|
||
SetSearchResultVisibility(false);
|
||
SetTicketOverviewVisibility(true);
|
||
}
|
||
|
||
public void ShowSearchRelations(cSearchHistorySearchResultEntry searchHistoryEntry, IRelationService relationService, ISearchUiProvider searchUiProvider)
|
||
{
|
||
try
|
||
{
|
||
_renderTicketOverviewUserNames = searchHistoryEntry != null && _ticketOverviewHistoryEntries.Contains(searchHistoryEntry);
|
||
Dispatcher.Invoke(() => ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: _renderTicketOverviewUserNames));
|
||
|
||
cSearchManager.ResolveRelations(searchHistoryEntry.Relations);
|
||
|
||
ILookup<enumFasdInformationClass, cMenuDataBase> relationsLookup = searchHistoryEntry.Relations
|
||
.OrderBy(r => r.UsingLevel)
|
||
.ThenBy(r => r.LastUsed)
|
||
.ToLookup(GetInformationClass, r => GetMenuData(r, relationService));
|
||
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
ResultMenu.UpdateSearchRelations(relationsLookup);
|
||
ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: _renderTicketOverviewUserNames);
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
}
|
||
|
||
enumFasdInformationClass GetInformationClass(cF4sdApiSearchResultRelation relation) => cF4sdIdentityEntry.GetFromSearchResult(relation.Type);
|
||
|
||
cMenuDataBase GetMenuData(cF4sdApiSearchResultRelation relation, IRelationService trelationService)
|
||
{
|
||
try
|
||
{
|
||
var requiredInformationClasses = new List<enumFasdInformationClass>() { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
|
||
bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(requiredInformationClasses);
|
||
string disabledReason = null;
|
||
|
||
if (!isEnabled)
|
||
{
|
||
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
|
||
}
|
||
else if (ShouldDisableTicketRelationForDemo(relation))
|
||
{
|
||
isEnabled = false;
|
||
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.Demo.NoTicketDetails") ?? string.Empty;
|
||
}
|
||
string trailingText = null;
|
||
if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("UserDisplayName", out var relationUserDisplayName))
|
||
{
|
||
trailingText = relationUserDisplayName;
|
||
}
|
||
string summaryText = null;
|
||
if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("Summary", out var relationSummary))
|
||
{
|
||
summaryText = relationSummary;
|
||
}
|
||
return new cMenuDataSearchRelation(relation)
|
||
{
|
||
MenuText = relation.DisplayName,
|
||
SubMenuText = summaryText,
|
||
TrailingText = trailingText,
|
||
UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
|
||
{
|
||
DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
|
||
Description = isEnabled ? string.Empty : disabledReason,
|
||
AlternativeDescription = isEnabled ? string.Empty : disabledReason
|
||
}
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
// >>> Fallback NICHT null: einfacher, aktivierter Eintrag <<<
|
||
string fallbackTrailingText = null;
|
||
if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("UserDisplayName", out var relationUserDisplayNameFallback))
|
||
{
|
||
fallbackTrailingText = relationUserDisplayNameFallback;
|
||
}
|
||
string fallbackSummaryText = null;
|
||
if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("Summary", out var relationSummaryFallback))
|
||
{
|
||
fallbackSummaryText = relationSummaryFallback;
|
||
}
|
||
string fallbackDisabledReason = null;
|
||
bool fallbackIsEnabled = true;
|
||
try
|
||
{
|
||
var required = new List<enumFasdInformationClass> { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
|
||
fallbackIsEnabled = cHealthCardDataHelper.HasAvailableHealthCard(required);
|
||
if (!fallbackIsEnabled)
|
||
{
|
||
fallbackDisabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
|
||
}
|
||
else if (ShouldDisableTicketRelationForDemo(relation))
|
||
{
|
||
fallbackIsEnabled = false;
|
||
fallbackDisabledReason = cMultiLanguageSupport.GetItem("Searchbar.Demo.NoTicketDetails") ?? string.Empty;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
fallbackIsEnabled = true;
|
||
fallbackDisabledReason = string.Empty;
|
||
}
|
||
return new cMenuDataSearchRelation(relation)
|
||
{
|
||
MenuText = relation.DisplayName,
|
||
SubMenuText = fallbackSummaryText,
|
||
TrailingText = fallbackTrailingText,
|
||
UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
|
||
{
|
||
DisplayType = fallbackIsEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
|
||
Description = fallbackIsEnabled ? string.Empty : fallbackDisabledReason,
|
||
AlternativeDescription = fallbackIsEnabled ? string.Empty : fallbackDisabledReason
|
||
}
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
private void TriggeredSearch(string searchValue)
|
||
{
|
||
try
|
||
{
|
||
var visibleWindows = Application.Current.Windows.Cast<Window>().Where(window => window.IsVisible && (window is SearchPageView) == false).ToList();
|
||
visibleWindows.ForEach(window => window.Visibility = Visibility.Hidden);
|
||
SearchBarUc.SetSearchText(searchValue);
|
||
Show();
|
||
Activate();
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}
|
||
|
||
private void ShowExternalSearchInfo(string strInfo, cFasdApiSearchResultCollection resultEntry, enumF4sdSearchResultClass Class)
|
||
{
|
||
var filteredResults = new cFilteredResults(resultEntry) { AutoContinue = true };
|
||
var _t = ShowExternalSearchInfoAsync(strInfo, filteredResults, Class);
|
||
}
|
||
|
||
private async Task ShowExternalSearchInfoAsync(string strInfo, cFilteredResults result, enumF4sdSearchResultClass Class)
|
||
{
|
||
try
|
||
{
|
||
await this.Dispatcher.InvokeAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
await SearchBarUc.SetFixedSearchResultAsync(Class, strInfo, result);
|
||
|
||
if (result.AutoContinue && result.Results?.Count == 1)
|
||
{
|
||
ResultMenu.IndexOfSelectedResultItem = 0;
|
||
ResultMenu.SelectCurrentResultItem();
|
||
}
|
||
|
||
Show();
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}
|
||
|
||
private void ActivateManualSearch()
|
||
{
|
||
CancledSearchAction();
|
||
SearchBarUc.ActivateManualSearch();
|
||
Show();
|
||
Activate();
|
||
|
||
if (cSearchManager.Instance.HistoryList.Count > 0)
|
||
{
|
||
SearchHistory.ShowSearchHistory();
|
||
SetSearchHistoryVisibility(true);
|
||
}
|
||
else
|
||
SetSearchHistoryVisibility(false);
|
||
}
|
||
|
||
public void PhoneCallSearch(cPhoneSearchParameters searchInfo)
|
||
{
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
var _h = Task.Run(async () =>
|
||
{
|
||
|
||
try
|
||
{
|
||
var _result = await cFasdCockpitCommunicationBase.Instance.GetPhoneSearchResults(searchInfo);
|
||
if (_result != null && _result.Count > 0 || cFasdCockpitConfig.Instance?.PhoneSupport?.ShowUnresolvedPhoneNumbers is true)
|
||
{
|
||
string strTxt;
|
||
if (string.IsNullOrEmpty(searchInfo.name))
|
||
strTxt = searchInfo.phone;
|
||
else
|
||
strTxt = $"{searchInfo.name} ({searchInfo.phone})";
|
||
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.Phonecall.Info"), strTxt);
|
||
ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Phone);
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
LogMethodEnd(CM);
|
||
}
|
||
|
||
}
|
||
|
||
private void ComputerDomainSearch(cComputerDomainSearchParameters searchInfo)
|
||
{
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
var _h = Task.Run(async () =>
|
||
{
|
||
|
||
try
|
||
{
|
||
var _result = await cFasdCockpitCommunicationBase.Instance.GetComputerSearchResults(searchInfo.name, searchInfo.domain);
|
||
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.ComputerSearch.Info"), searchInfo.name);
|
||
ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Computer);
|
||
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
LogMethodEnd(CM);
|
||
}
|
||
|
||
}
|
||
|
||
private void UserSidSearch(cUserSidSearchParameters searchInfo)
|
||
{
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
var _h = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
var lstSids = searchInfo.sids.Split(',').Select((v) => v.Trim()).ToList();
|
||
var _result = await cFasdCockpitCommunicationBase.Instance.GetUserSearchResults(searchInfo.name, lstSids);
|
||
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.UserSearch.Info"), searchInfo.name);
|
||
ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.User);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
LogMethodEnd(CM);
|
||
}
|
||
|
||
}
|
||
|
||
private void TicketSearch(cTicketSearchParameters searchInfo)
|
||
{
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
if (!Guid.TryParse(searchInfo.ticketId, out var _ticketId))
|
||
return;
|
||
|
||
var _h = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
var lstSids = searchInfo.sids?.Split(',').Select((v) => v.Trim()).ToList();
|
||
var _result = await cFasdCockpitCommunicationBase.Instance.GetUserSearchResults(searchInfo.userName, lstSids);
|
||
|
||
if (_result is null || _result.Count == 0 || _result.First().Value.Count == 0)
|
||
{
|
||
LogEntry($"No corresponding user could be found for ticket '{searchInfo.ticketName}'", LogLevels.Warning);
|
||
return;
|
||
}
|
||
|
||
var UserId = _result.Values.First().First().id;
|
||
var _ticketRelation = new cF4sdApiSearchResultRelation()
|
||
{
|
||
Type = enumF4sdSearchResultClass.Ticket,
|
||
DisplayName = searchInfo.ticketName,
|
||
id = _ticketId,
|
||
Status = enumF4sdSearchResultStatus.Active,
|
||
Identities = new cF4sdIdentityList
|
||
{
|
||
{ new cF4sdIdentityEntry()
|
||
{
|
||
Class = enumFasdInformationClass.User,
|
||
Id = UserId
|
||
}
|
||
},
|
||
{ new cF4sdIdentityEntry()
|
||
{
|
||
Class = enumFasdInformationClass.Ticket,
|
||
Id = _ticketId
|
||
}
|
||
},
|
||
}
|
||
};
|
||
|
||
var filteredResults = new cFilteredResults(_result) { AutoContinue = true, PreSelectedRelation = _ticketRelation };
|
||
|
||
var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.TicketSearch.Info"), searchInfo.ticketName);
|
||
var _t = ShowExternalSearchInfoAsync(strInfo, filteredResults, enumF4sdSearchResultClass.Ticket);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
});
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
LogMethodEnd(CM);
|
||
}
|
||
|
||
}
|
||
|
||
private void PipeServer_PipeMessage(string reply)
|
||
{
|
||
try
|
||
{
|
||
var type = reply;
|
||
if (type.Contains(':'))
|
||
type = reply.Substring(0, Math.Max(reply.IndexOf(':'), 0));
|
||
type = type.ToLowerInvariant();
|
||
|
||
if (string.IsNullOrWhiteSpace(type))
|
||
{
|
||
LogEntry("No valid pipe reply", LogLevels.Warning);
|
||
return;
|
||
}
|
||
|
||
if (type == "hello")
|
||
return;
|
||
|
||
if (DefaultLogger.IsDebug)
|
||
{
|
||
var _msg = new List<string>() { "pipe message received:", reply };
|
||
DefaultLogger.LogList(LogLevels.Debug, _msg);
|
||
}
|
||
|
||
if (cConnectionStatusHelper.Instance?.ApiConnectionStatus is cConnectionStatusHelper.enumOnlineStatus.offline)
|
||
return;
|
||
|
||
var value = reply.Remove(0, type.Length).TrimStart(':');
|
||
|
||
switch (type)
|
||
{
|
||
case "search":
|
||
try
|
||
{
|
||
Dispatcher.Invoke(() => TriggeredSearch(value));
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
break;
|
||
case "phonesearch":
|
||
try
|
||
{
|
||
var searchInfo = JsonConvert.DeserializeObject<cPhoneSearchParameters>(value);
|
||
PhoneCallSearch(searchInfo);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
break;
|
||
case "computerdomainsearch":
|
||
try
|
||
{
|
||
var searchInfo = JsonConvert.DeserializeObject<cComputerDomainSearchParameters>(value);
|
||
ComputerDomainSearch(searchInfo);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
break;
|
||
case "usersidsearch":
|
||
try
|
||
{
|
||
var searchInfo = JsonConvert.DeserializeObject<cUserSidSearchParameters>(value);
|
||
UserSidSearch(searchInfo);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
break;
|
||
case "ticketsearch":
|
||
try
|
||
{
|
||
var searchInfo = JsonConvert.DeserializeObject<cTicketSearchParameters>(value);
|
||
TicketSearch(searchInfo);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}
|
||
|
||
protected override void OnSourceInitialized(EventArgs e)
|
||
{
|
||
base.OnSourceInitialized(e);
|
||
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
_pipeServer = new cF4sdPipeServer();
|
||
_pipeServer.PipeMessage += PipeServer_PipeMessage;
|
||
_pipeServer.Listen(App.PipeName, MaxSize: 4096, LowerIntegrity: true);
|
||
LogEntry($"Start listening on named pipe '{App.PipeName}'...", LogLevels.Debug);
|
||
|
||
IntPtr handle = new WindowInteropHelper(this).Handle;
|
||
var hwndSource = HwndSource.FromHwnd(handle);
|
||
hwndSource.AddHook(SearchViewWindowProc);
|
||
hwndSource.AddHook(new HwndSourceHook(cUtility.WindowProc));
|
||
|
||
var hotKeyDefinitions = new List<cHotKeyManager.cHotKeyDefinition>
|
||
{
|
||
new cHotKeyManager.cHotKeyDefinition { Id = 1, Modifier = new List<ModifierKeys> { ModifierKeys.Control }, Key = Key.F3, HotKeyAction = HotKeyManager_ActivateSearch },
|
||
new cHotKeyManager.cHotKeyDefinition { Id = 2, Modifier = new List<ModifierKeys> { ModifierKeys.Control, ModifierKeys.Alt }, Key = Key.F3, HotKeyAction = HotKeyManager_CopyAndSearch }
|
||
};
|
||
|
||
#if isDemo
|
||
if (cFasdCockpitCommunicationBase.Instance?.IsDemo() == true)
|
||
{
|
||
hotKeyDefinitions.Add(new cHotKeyManager.cHotKeyDefinition
|
||
{
|
||
Id = 3,
|
||
Modifier = new List<ModifierKeys> { ModifierKeys.Control, ModifierKeys.Alt },
|
||
Key = Key.T,
|
||
HotKeyAction = () => TicketOverviewUpdateService.Instance.SimulateDemoTicket()
|
||
});
|
||
}
|
||
#endif
|
||
|
||
_hotKeyManager = new cHotKeyManager(handle, hotKeyDefinitions);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
LogMethodEnd(CM);
|
||
}
|
||
}
|
||
|
||
public void ActivateSearchView()
|
||
{
|
||
if (_isActivating)
|
||
return;
|
||
|
||
_isActivating = true;
|
||
|
||
var CM = MethodBase.GetCurrentMethod();
|
||
LogMethodBegin(CM);
|
||
try
|
||
{
|
||
|
||
|
||
bool doShow = true;
|
||
|
||
if (doShow)
|
||
{
|
||
ActivateManualSearch();
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
_isActivating = false;
|
||
LogMethodEnd(CM);
|
||
}
|
||
}
|
||
|
||
private void HotKeyManager_ActivateSearch()
|
||
{
|
||
if (!IsVisible)
|
||
ActivateSearchView();
|
||
}
|
||
|
||
public void BringToFrontPreserveState()
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
if (!IsVisible)
|
||
Show();
|
||
|
||
if (WindowState == WindowState.Minimized)
|
||
WindowState = WindowState.Normal;
|
||
|
||
Activate();
|
||
Topmost = true;
|
||
Topmost = false;
|
||
Focus();
|
||
});
|
||
}
|
||
|
||
private async void HotKeyManager_CopyAndSearch()
|
||
{
|
||
try
|
||
{
|
||
bool isHotKeyDown = true;
|
||
while (isHotKeyDown)
|
||
{
|
||
isHotKeyDown = _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_F3) || _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_CONTROL) || _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_MENU);
|
||
await Task.Delay(50);
|
||
}
|
||
|
||
cUtility.SendCtrlCToCurrentWindow();
|
||
|
||
await Task.Delay(150);
|
||
|
||
var copiedText = Clipboard.GetText();
|
||
copiedText = cUtility.CleanPhoneString(copiedText);
|
||
copiedText = copiedText.Trim().Substring(0, Math.Min(copiedText.Trim().Length, 75));
|
||
|
||
ActivateSearchView();
|
||
|
||
if (copiedText != null)
|
||
{
|
||
await Dispatcher.BeginInvoke((Action)delegate
|
||
{
|
||
Activate();
|
||
SearchBarUc.SetSearchText(copiedText);
|
||
}, DispatcherPriority.Render);
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
|
||
}
|
||
|
||
protected override void OnClosed(EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (TicketOverviewUpdateService.Instance != null)
|
||
TicketOverviewUpdateService.Instance.OverviewCountsChanged -= TicketOverviewUpdateService_OverviewCountsChanged;
|
||
_pipeServer?.Dispose();
|
||
}
|
||
catch { }
|
||
_pipeServer = null;
|
||
|
||
_hotKeyManager?.UnRegisterHotKeys();
|
||
RemoveHandler(TicketOverview.SelectionRequestedEvent,
|
||
new TicketOverviewSelectionRequestedEventHandler(TicketOverview_SelectionRequested));
|
||
base.OnClosed(e);
|
||
}
|
||
|
||
private void AddCustomEventHandlers()
|
||
{
|
||
AddHandler(cUiActionBase.UiActionClickedEvent, new cUiActionBase.UiActionEventHandlerDelegate(UiActionWasTriggered));
|
||
SearchBarUc.CancledSearchAction = CancledSearchAction;
|
||
AddHandler(TicketOverview.SelectionRequestedEvent,
|
||
new TicketOverviewSelectionRequestedEventHandler(TicketOverview_SelectionRequested), true);
|
||
cFasdCockpitConfig.Instance.UiSettingsChanged += UiSettingsChanged;
|
||
|
||
SearchBarUc.SearchValueChanged += HandleSearchValueChanged;
|
||
SearchService.RelationsFound += HandleRelationsFound;
|
||
}
|
||
|
||
private async void HandleSearchValueChanged(object sender, string searchValue)
|
||
{
|
||
const int minSearchLength = 2;
|
||
|
||
try
|
||
{
|
||
_searchCancellationTokenSource?.Cancel();
|
||
_searchCancellationTokenSource = new CancellationTokenSource();
|
||
|
||
if (string.IsNullOrWhiteSpace(searchValue))
|
||
{
|
||
SearchBarUc.SetSpinnerVisibility(Visibility.Hidden);
|
||
UpdateSearchResults(new cFilteredResults());
|
||
return;
|
||
}
|
||
|
||
if (searchValue.Length < minSearchLength)
|
||
return;
|
||
|
||
SearchBarUc.SetSpinnerVisibility(Visibility.Visible);
|
||
cFasdApiSearchResultCollection searchResults = await SupportCaseSearchService.GetSearchResultsAsync(searchValue, _searchCancellationTokenSource.Token);
|
||
SearchBarUc.SetSpinnerVisibility(Visibility.Hidden);
|
||
UpdateSearchResults(new cFilteredResults(searchResults));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
}
|
||
}
|
||
|
||
private void HandleRelationsFound(object sender, StagedSearchResultRelationsEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
var first = e.RelatedTo.FirstOrDefault();
|
||
var relationSearchResult = new cSearchHistorySearchResultEntry(first.DisplayName, first.DisplayName, e.RelatedTo.ToList(), e.StagedResultRelations.Relations.ToList(), this);
|
||
ShowSearchRelations(relationSearchResult, e.RelationService, this);
|
||
|
||
UpdatePendingInformationClasses(e.StagedResultRelations.PendingInformationClasses);
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
}
|
||
}
|
||
|
||
private void TicketOverviewUpdateService_OverviewCountsChanged(object sender, TicketOverviewCountsChangedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
ApplyLatestCounts();
|
||
var positiveChanges = e.Changes?.Where(change => change.Delta > 0).ToList();
|
||
TicketOverviewUc?.SetHighlights(positiveChanges, IsFilterChecked);
|
||
|
||
var app = Application.Current as FasdDesktopUi.App;
|
||
|
||
if (positiveChanges == null || positiveChanges.Count == 0)
|
||
{
|
||
app?.ClearTicketOverviewTrayNotification();
|
||
return;
|
||
}
|
||
|
||
var message = BuildNotificationMessage(positiveChanges);
|
||
if (string.IsNullOrWhiteSpace(message))
|
||
{
|
||
app?.ClearTicketOverviewTrayNotification();
|
||
return;
|
||
}
|
||
|
||
app?.ShowTicketOverviewTrayNotification(message);
|
||
ShowTicketOverviewNotification(message);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
}
|
||
}
|
||
|
||
private void ApplyLatestCounts()
|
||
{
|
||
var service = TicketOverviewUpdateService.Instance;
|
||
if (service == null)
|
||
return;
|
||
var counts = service.GetCountsForScope(IsFilterChecked);
|
||
if (counts == null || counts.Count == 0)
|
||
return;
|
||
|
||
TicketOverviewUc?.UpdateCounts(counts, IsFilterChecked);
|
||
TicketOverviewUc?.RefreshHighlightState(IsFilterChecked);
|
||
}
|
||
|
||
private string BuildNotificationMessage(IReadOnlyList<TileCountChange> changes)
|
||
{
|
||
if (changes == null || changes.Count == 0)
|
||
return string.Empty;
|
||
|
||
var aggregates = new List<(string RowKey, string ColumnKey, TileScope Scope, int Delta)>();
|
||
var indexMap = new Dictionary<(string RowKey, string ColumnKey, TileScope Scope), int>();
|
||
|
||
foreach (var change in changes)
|
||
{
|
||
if (!TryResolveTicketOverviewLabels(change.Key, out var rowKey, out var columnKey))
|
||
continue;
|
||
|
||
var delta = change.NewCount - change.OldCount;
|
||
if (delta == 0)
|
||
continue;
|
||
|
||
var aggregateKey = (rowKey, columnKey, change.Scope);
|
||
if (indexMap.TryGetValue(aggregateKey, out var aggregateIndex))
|
||
{
|
||
var existing = aggregates[aggregateIndex];
|
||
aggregates[aggregateIndex] = (existing.RowKey, existing.ColumnKey, existing.Scope, existing.Delta + delta);
|
||
}
|
||
else
|
||
{
|
||
indexMap[aggregateKey] = aggregates.Count;
|
||
aggregates.Add((rowKey, columnKey, change.Scope, delta));
|
||
}
|
||
}
|
||
|
||
if (aggregates.Count == 0)
|
||
return string.Empty;
|
||
|
||
var pieces = new List<string>(aggregates.Count);
|
||
foreach (var aggregate in aggregates)
|
||
{
|
||
if (aggregate.Delta == 0)
|
||
continue;
|
||
|
||
var row = cMultiLanguageSupport.GetItem(aggregate.RowKey) ?? aggregate.RowKey;
|
||
var column = cMultiLanguageSupport.GetItem(aggregate.ColumnKey) ?? aggregate.ColumnKey;
|
||
var scopeRowLabel = GetScopeRowLabel(aggregate.Scope, aggregate.RowKey, row);
|
||
var deltaText = aggregate.Delta >= 0 ? $"+{aggregate.Delta}" : aggregate.Delta.ToString();
|
||
pieces.Add($"{scopeRowLabel} – {column} ({deltaText})");
|
||
}
|
||
|
||
return string.Join(Environment.NewLine, pieces);
|
||
}
|
||
|
||
private static bool TryResolveTicketOverviewLabels(string key, out string rowKey, out string columnKey)
|
||
{
|
||
rowKey = null;
|
||
columnKey = null;
|
||
|
||
if (string.IsNullOrWhiteSpace(key))
|
||
return false;
|
||
|
||
var normalized = key.ToLowerInvariant();
|
||
|
||
if (normalized.StartsWith("tickets"))
|
||
rowKey = "TicketOverview.Row.Heading.Tickets";
|
||
else if (normalized.StartsWith("incident"))
|
||
rowKey = "TicketOverview.Row.Heading.Incidents";
|
||
else if (normalized.StartsWith("unassigned"))
|
||
rowKey = "TicketOverview.Row.Heading.UnassignedTickets";
|
||
else
|
||
return false;
|
||
|
||
columnKey = ResolveColumnTranslationKey(normalized);
|
||
return columnKey != null;
|
||
}
|
||
|
||
private static string ResolveColumnTranslationKey(string normalizedKey)
|
||
{
|
||
if (normalizedKey.EndsWith("newinfo"))
|
||
return "TicketOverview.Column.Heading.NewInfo";
|
||
if (normalizedKey.EndsWith("critical"))
|
||
return "TicketOverview.Column.Heading.Critical";
|
||
if (normalizedKey.EndsWith("active"))
|
||
return "TicketOverview.Column.Heading.Active";
|
||
return "TicketOverview.Column.Heading.New";
|
||
}
|
||
|
||
private static string GetScopeRowLabel(TileScope scope, string rowKey, string rowLabel)
|
||
{
|
||
string suffix = null;
|
||
if (string.Equals(rowKey, "TicketOverview.Row.Heading.Tickets", StringComparison.OrdinalIgnoreCase))
|
||
suffix = "Tickets";
|
||
else if (string.Equals(rowKey, "TicketOverview.Row.Heading.Incidents", StringComparison.OrdinalIgnoreCase))
|
||
suffix = "Incidents";
|
||
else if (string.Equals(rowKey, "TicketOverview.Row.Heading.UnassignedTickets", StringComparison.OrdinalIgnoreCase))
|
||
suffix = "UnassignedTickets";
|
||
|
||
var translationKey = scope == TileScope.Role
|
||
? $"TicketOverview.ScopeRow.Role.{suffix}"
|
||
: $"TicketOverview.ScopeRow.Personal.{suffix}";
|
||
|
||
return cMultiLanguageSupport.GetItem(translationKey) ?? rowLabel;
|
||
}
|
||
|
||
private IntPtr SearchViewWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||
{
|
||
if (msg == WM_NCHITTEST)
|
||
{
|
||
if (!IsPointWithinInteractiveBounds(lParam))
|
||
{
|
||
handled = true;
|
||
return new IntPtr(HTTRANSPARENT);
|
||
}
|
||
}
|
||
|
||
return IntPtr.Zero;
|
||
}
|
||
|
||
private bool IsPointWithinInteractiveBounds(IntPtr lParam)
|
||
{
|
||
if (!IsLoaded || MainBorder == null || !MainBorder.IsLoaded)
|
||
return true;
|
||
|
||
if (MainBorder.ActualWidth <= 0 || MainBorder.ActualHeight <= 0)
|
||
return true;
|
||
|
||
var screenPoint = ExtractScreenPoint(lParam);
|
||
var windowPoint = PointFromScreen(screenPoint);
|
||
|
||
if (!(InputHitTest(windowPoint) is DependencyObject hitElement))
|
||
return false;
|
||
|
||
return IsDescendantOf(hitElement, MainBorder);
|
||
}
|
||
|
||
private static Point ExtractScreenPoint(IntPtr lParam)
|
||
{
|
||
int value = unchecked((int)(long)lParam);
|
||
short x = (short)(value & 0xFFFF);
|
||
short y = (short)((value >> 16) & 0xFFFF);
|
||
return new Point(x, y);
|
||
}
|
||
|
||
private static bool IsDescendantOf(DependencyObject element, DependencyObject ancestor)
|
||
{
|
||
while (element != null)
|
||
{
|
||
if (element == ancestor)
|
||
return true;
|
||
|
||
element = GetParent(element);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private static DependencyObject GetParent(DependencyObject current)
|
||
{
|
||
if (current == null)
|
||
return null;
|
||
|
||
if (current is Visual || current is System.Windows.Media.Media3D.Visual3D)
|
||
return VisualTreeHelper.GetParent(current);
|
||
|
||
if (current is System.Windows.ContentElement contentElement)
|
||
return System.Windows.ContentOperations.GetParent(contentElement);
|
||
|
||
return LogicalTreeHelper.GetParent(current);
|
||
}
|
||
|
||
private void ShowTicketOverviewNotification(string message)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(message))
|
||
return;
|
||
|
||
try
|
||
{
|
||
var notificationTitle = cMultiLanguageSupport.GetItem("TicketOverview.Notification.Title") ?? "Ticketübersicht";
|
||
var app = Application.Current as App;
|
||
app?.notifyIcon?.ShowBalloonTip(3000, notificationTitle, message, System.Windows.Forms.ToolTipIcon.Info);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
}
|
||
}
|
||
private async void TicketOverview_SelectionRequested(object sender, TicketOverviewSelectionRequestedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
var app = Application.Current as FasdDesktopUi.App;
|
||
app?.ClearTicketOverviewTrayNotification();
|
||
|
||
_renderTicketOverviewUserNames = true;
|
||
SetSearchHistoryVisibility(false);
|
||
SetTicketOverviewVisibility(true);
|
||
SetSearchResultVisibility(true);
|
||
var header = BuildHeaderText(e.Key, e.UseRoleScope, e.Count);
|
||
ResultMenu.SetHeaderText(header, hideDetailsCheckbox: true);
|
||
|
||
ShowLoadingTextItem(cMultiLanguageSupport.GetItem("Searchbar.Loading.CaseData"));
|
||
|
||
SetPendingInformationClasses(new HashSet<enumFasdInformationClass> { enumFasdInformationClass.Ticket });
|
||
|
||
var relations = await LoadRelationsForTileAsync(e.Key, e.UseRoleScope, Math.Max(0, e.Count));
|
||
Debug.WriteLine($"[TicketOverview] Relations loaded: {relations?.Count ?? 0}");
|
||
var firstRelation = relations.FirstOrDefault();
|
||
string displayText = header;
|
||
if (firstRelation != null)
|
||
{
|
||
string firstSummary = null;
|
||
if (firstRelation.Infos != null && firstRelation.Infos.TryGetValue("Summary", out var summaryValue))
|
||
{
|
||
firstSummary = summaryValue;
|
||
}
|
||
|
||
displayText = string.IsNullOrWhiteSpace(firstSummary)
|
||
? $"{header} → {firstRelation.DisplayName}"
|
||
: $"{header} → {firstRelation.DisplayName} – {firstSummary}";
|
||
}
|
||
|
||
var entry = new cSearchHistorySearchResultEntry(
|
||
displayText,
|
||
header,
|
||
new List<cFasdApiSearchResultEntry>(),
|
||
relations,
|
||
this
|
||
)
|
||
{ isSeen = true };
|
||
_ticketOverviewHistoryEntries.Add(entry);
|
||
|
||
var lookup = relations.ToLookup(
|
||
r => cF4sdIdentityEntry.GetFromSearchResult(r.Type),
|
||
r =>
|
||
{
|
||
var required = new List<enumFasdInformationClass> { cF4sdIdentityEntry.GetFromSearchResult(r.Type) };
|
||
bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(required);
|
||
string disabledReason = null;
|
||
if (!isEnabled)
|
||
{
|
||
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
|
||
}
|
||
else if (ShouldDisableTicketRelationForDemo(r))
|
||
{
|
||
isEnabled = false;
|
||
disabledReason = cMultiLanguageSupport.GetItem("Searchbar.Demo.NoTicketDetails") ?? string.Empty;
|
||
}
|
||
string trailingUser = null;
|
||
if (_renderTicketOverviewUserNames && r.Infos != null && r.Infos.TryGetValue("UserDisplayName", out var userDisplayName))
|
||
{
|
||
trailingUser = userDisplayName;
|
||
}
|
||
return (cMenuDataBase)new cMenuDataSearchRelation(r)
|
||
{
|
||
MenuText = r.DisplayName,
|
||
TrailingText = trailingUser,
|
||
UiAction = new cUiProcessSearchRelationAction(entry, r, null, this)
|
||
{
|
||
DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
|
||
Description = isEnabled ? string.Empty : disabledReason,
|
||
AlternativeDescription = isEnabled ? string.Empty : disabledReason
|
||
}
|
||
};
|
||
}
|
||
);
|
||
ResultMenu.UpdateSearchRelations(lookup);
|
||
ResultMenu.SetHeaderText(header, hideDetailsCheckbox: true);
|
||
|
||
UpdatePendingInformationClasses(new HashSet<enumFasdInformationClass>());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
CancledSearchAction();
|
||
}
|
||
System.Diagnostics.Debug.WriteLine(
|
||
$"[TicketOverview] Key={e.Key}, UseRoleScope={e.UseRoleScope}, Count={e.Count}");
|
||
}
|
||
private string BuildHeaderText(string key, bool useRoleScope, int? count = null)
|
||
{
|
||
string scopeKey = useRoleScope ? "Searchbar.Header.Scope.Role" : "Searchbar.Header.Scope.Personal";
|
||
string entityKey = DetermineFallbackEntityKey(key);
|
||
string filterKey = DetermineFallbackFilterKey(key);
|
||
|
||
string scope = cMultiLanguageSupport.GetItem(scopeKey) ?? scopeKey;
|
||
string entity = cMultiLanguageSupport.GetItem(entityKey) ?? entityKey;
|
||
string filter = cMultiLanguageSupport.GetItem(filterKey) ?? filterKey;
|
||
string template = cMultiLanguageSupport.GetItem("Searchbar.Header.Template") ?? "{0} - {1} ({2})";
|
||
|
||
bool expectsCount = template.IndexOf("{3}", StringComparison.Ordinal) >= 0;
|
||
try
|
||
{
|
||
if (expectsCount)
|
||
{
|
||
int countValue = count ?? 0;
|
||
return string.Format(template, entity, filter, scope, countValue);
|
||
}
|
||
|
||
return string.Format(template, entity, filter, scope);
|
||
}
|
||
catch (FormatException)
|
||
{
|
||
return string.Format("{0} - {1} ({2})", entity, filter, scope);
|
||
}
|
||
}
|
||
|
||
private string DetermineFallbackEntityKey(string key)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(key))
|
||
return "Searchbar.Header.Entity.Tickets";
|
||
|
||
var keyLower = key.ToLowerInvariant();
|
||
return keyLower.Contains("incident")
|
||
? "Searchbar.Header.Entity.Incidents"
|
||
: "Searchbar.Header.Entity.Tickets";
|
||
}
|
||
|
||
private string DetermineFallbackFilterKey(string key)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(key))
|
||
return "Searchbar.Header.Filter.Overview";
|
||
|
||
var keyLower = key.ToLowerInvariant();
|
||
|
||
if (keyLower.Contains("newinfo"))
|
||
return "Searchbar.Header.Filter.NewInfo";
|
||
if (keyLower.Contains("new"))
|
||
return "Searchbar.Header.Filter.New";
|
||
if (keyLower.Contains("active"))
|
||
return "Searchbar.Header.Filter.Active";
|
||
if (keyLower.Contains("critical"))
|
||
return "Searchbar.Header.Filter.Critical";
|
||
if (keyLower.Contains("unassigned"))
|
||
return "Searchbar.Header.Filter.Unassigned";
|
||
if (keyLower.Contains("hold") || keyLower.Contains("onhold") || keyLower.Contains("wait"))
|
||
return "Searchbar.Header.Filter.OnHold";
|
||
if (keyLower.Contains("closed"))
|
||
return "Searchbar.Header.Filter.Closed";
|
||
|
||
return "Searchbar.Header.Filter.Overview";
|
||
}
|
||
|
||
private bool ShouldDisableTicketRelationForDemo(cF4sdApiSearchResultRelation relation)
|
||
{
|
||
if (relation == null)
|
||
return false;
|
||
|
||
var communication = cFasdCockpitCommunicationBase.Instance;
|
||
if (communication?.IsDemo() != true)
|
||
return false;
|
||
|
||
if (relation.Type != enumF4sdSearchResultClass.Ticket)
|
||
return false;
|
||
|
||
if (relation.Infos == null)
|
||
return false;
|
||
|
||
if (!relation.Infos.TryGetValue(DemoTicketHasDetailsInfoKey, out var hasDetailsValue))
|
||
return false;
|
||
|
||
if (bool.TryParse(hasDetailsValue, out var hasDetails))
|
||
return !hasDetails;
|
||
|
||
return true;
|
||
}
|
||
|
||
private async Task<List<cF4sdApiSearchResultRelation>> LoadRelationsForTileAsync(string key, bool useRoleScope, int count)
|
||
{
|
||
var communication = cFasdCockpitCommunicationBase.Instance;
|
||
if (communication == null)
|
||
return new List<cF4sdApiSearchResultRelation>();
|
||
try
|
||
{
|
||
var relations = await communication.GetTicketOverviewRelations(key, useRoleScope, Math.Max(0, count)).ConfigureAwait(false);
|
||
var list = relations?.ToList() ?? new List<cF4sdApiSearchResultRelation>();
|
||
|
||
if (TicketOverviewUpdateService.Instance != null)
|
||
foreach (var demoRelation in TicketOverviewUpdateService.Instance.GetDemoRelations(key, useRoleScope))
|
||
{
|
||
if (!list.Any(r => r.id == demoRelation.id))
|
||
list.Add(demoRelation);
|
||
}
|
||
|
||
return list;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogException(ex);
|
||
return new List<cF4sdApiSearchResultRelation>();
|
||
}
|
||
}
|
||
|
||
private void UpdateSearchResults(cFilteredResults filteredResults)
|
||
{
|
||
this.Dispatcher.Invoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
_renderTicketOverviewUserNames = false;
|
||
|
||
if (filteredResults?.Results is null)
|
||
{
|
||
SetSearchResultVisibility(false);
|
||
SetTicketOverviewVisibility(true);
|
||
return;
|
||
}
|
||
|
||
string menuHeaderText = filteredResults?.PreSelectedRelation?.DisplayName;
|
||
ResultMenu.ShowSearchResults(filteredResults, menuHeaderText, this);
|
||
preSelectedRelation = filteredResults.PreSelectedRelation;
|
||
|
||
SetSearchResultVisibility(true);
|
||
SetTicketOverviewVisibility(false);
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}));
|
||
}
|
||
|
||
|
||
|
||
private void CancledSearchAction()
|
||
{
|
||
_renderTicketOverviewUserNames = false;
|
||
SearchBarUc.Clear();
|
||
ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
|
||
SetSearchResultVisibility(false);
|
||
SetSearchHistoryVisibility(false);
|
||
SetTicketOverviewVisibility(true);
|
||
Visibility = Visibility.Hidden;
|
||
}
|
||
|
||
private void SlimPageWindowStateBar_ClickedClose(object sender, RoutedEventArgs e)
|
||
{
|
||
CancledSearchAction();
|
||
}
|
||
|
||
private async void UiActionWasTriggered(object sender, UiActionEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (e.UiAction == null)
|
||
return;
|
||
|
||
Mouse.OverrideCursor = Cursors.Wait;
|
||
|
||
switch (e.UiAction)
|
||
{
|
||
case cUiProcessSearchResultAction searchResultAction:
|
||
searchResultAction.PreSelectedSearchRelation = preSelectedRelation;
|
||
break;
|
||
case cUiProcessSearchRelationAction _:
|
||
case cUiProcessSearchHistoryEntry _:
|
||
break;
|
||
default:
|
||
var _t = e.UiAction?.GetType().Name;
|
||
Debug.Assert(true, $"The UI action '{_t}' is not supported in detailed page");
|
||
CancledSearchAction();
|
||
return;
|
||
}
|
||
|
||
SetSearchResultVisibility(true);
|
||
if (!await e.UiAction.RunUiActionAsync(sender, null, false, null))
|
||
CancledSearchAction();
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
finally
|
||
{
|
||
Mouse.OverrideCursor = null;
|
||
}
|
||
}
|
||
|
||
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
|
||
{
|
||
switch (e.Key)
|
||
{
|
||
case Key.Up:
|
||
if (ResultMenu.IndexOfSelectedResultItem == int.MinValue)
|
||
ResultMenu.IndexOfSelectedResultItem = ResultMenu.GetSearchResultCount() - 1;
|
||
else
|
||
ResultMenu.IndexOfSelectedResultItem--;
|
||
break;
|
||
case Key.Down:
|
||
if (ResultMenu.IndexOfSelectedResultItem == int.MinValue)
|
||
ResultMenu.IndexOfSelectedResultItem = 0;
|
||
else
|
||
ResultMenu.IndexOfSelectedResultItem++;
|
||
break;
|
||
case Key.Enter:
|
||
ResultMenu.SelectCurrentResultItem();
|
||
e.Handled = true;
|
||
break;
|
||
case Key.Escape:
|
||
CancledSearchAction();
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void UiSettingsChanged(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
var positionAlignement = cFasdCockpitConfig.Instance.Global.SmallViewAlignment;
|
||
|
||
switch (positionAlignement)
|
||
{
|
||
case enumF4sdHorizontalAlignment.Center:
|
||
Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Center);
|
||
break;
|
||
case enumF4sdHorizontalAlignment.Right:
|
||
Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Right);
|
||
break;
|
||
default:
|
||
Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Left);
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception E)
|
||
{
|
||
LogException(E);
|
||
}
|
||
}
|
||
|
||
public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => Dispatcher.Invoke(() => ResultMenu.SetPendingInformationClasses(informationClasses));
|
||
|
||
public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => Dispatcher.Invoke(() => ResultMenu.UpdatePendingInformationClasses(informationClasses));
|
||
}
|
||
}
|