using InABox.Clients; using InABox.Core; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.IO; using System.Windows.Media; using System.Windows.Media.Imaging; using InABox.WPF; namespace InABox.Wpf; public enum MessageWindowButtonPosition { Left, Right } public enum MessageWindowResult { None, OK, Cancel, Yes, No, Other } public class MessageWindowButtonDelegateArgs : EventArgs { public MessageWindowButton Button { get; private set; } public bool Close { get; set; } = false; public MessageWindowButtonDelegateArgs(MessageWindowButton button, bool close) { Button = button; Close = close; } } public delegate void MessageWindowButtonDelegate(MessageWindow window, MessageWindowButtonDelegateArgs args); public class MessageWindowButton : INotifyPropertyChanged { public MessageWindowButtonPosition Position { get; set; } private string _content; public string Content { get => _content; [MemberNotNull(nameof(_content))] set { _content = value; OnPropertyChanged(); } } public MessageWindowButtonDelegate Action { get; set; } public MessageWindowButton(string content, MessageWindowButtonDelegate action, MessageWindowButtonPosition position) { Content = content; Action = action; Position = position; } public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } /// /// Interaction logic for MessageWindow.xaml /// public partial class MessageWindow : Window, INotifyPropertyChanged { public ObservableCollection Buttons { get; private set; } = new(); public IEnumerable LeftButtons => Buttons.Where(x => x.Position == MessageWindowButtonPosition.Left); public IEnumerable RightButtons => Buttons.Where(x => x.Position == MessageWindowButtonPosition.Right); private string _message = ""; public string Message { get => _message; set { _message = value; OnPropertyChanged(); } } public ImageSource? _image = null; public ImageSource? Image { get => _image; set { _image = value; OnPropertyChanged(); } } private string _details = ""; public string Details { get => _details; set { _details = value; OnPropertyChanged(); } } public static readonly DependencyProperty ShowDetailsProperty = DependencyProperty.Register(nameof(ShowDetails), typeof(bool), typeof(MessageWindow)); public bool ShowDetails { get => (bool)GetValue(ShowDetailsProperty); set => SetValue(ShowDetailsProperty, value); } public MessageWindowResult Result { get; set; } = MessageWindowResult.None; public object? OtherResult { get; set; } public MessageWindow() { InitializeComponent(); Buttons.CollectionChanged += Buttons_CollectionChanged; } private void Button_Click(object sender, RoutedEventArgs e) { if (sender is not Button button || button.Tag is not MessageWindowButton winButton) return; var args = new MessageWindowButtonDelegateArgs(winButton, false); winButton.Action(this, args); if (args.Close) Close(); } private void Buttons_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { OnPropertyChanged(nameof(LeftButtons)); OnPropertyChanged(nameof(RightButtons)); } public MessageWindow AddButton(MessageWindowButton button) { Buttons.Add(button); return this; } public MessageWindow AddOKButton(string content = "OK") { Buttons.Add(new MessageWindowButton(content, OKButton_Click, MessageWindowButtonPosition.Right)); return this; } public MessageWindow AddCancelButton(string content = "Cancel") { Buttons.Add(new MessageWindowButton(content, CancelButton_Click, MessageWindowButtonPosition.Right)); return this; } public MessageWindow AddYesButton(string content = "Yes") { Buttons.Add(new MessageWindowButton(content, YesButton_Click, MessageWindowButtonPosition.Right)); return this; } public MessageWindow AddNoButton(string content = "No") { Buttons.Add(new MessageWindowButton(content, NoButton_Click, MessageWindowButtonPosition.Right)); return this; } private void YesButton_Click(MessageWindow window, MessageWindowButtonDelegateArgs args) { Result = MessageWindowResult.Yes; Close(); } private void NoButton_Click(MessageWindow window, MessageWindowButtonDelegateArgs args) { Result = MessageWindowResult.No; Close(); } private void CancelButton_Click(MessageWindow window, MessageWindowButtonDelegateArgs args) { Result = MessageWindowResult.Cancel; Close(); } private void OKButton_Click(MessageWindow window, MessageWindowButtonDelegateArgs args) { Result = MessageWindowResult.OK; Close(); } public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #region Static Constructors private static readonly BitmapImage _warning = InABox.Wpf.Resources.warning.AsBitmapImage(); public static BitmapImage WarningImage => _warning; private static readonly BitmapImage _question = InABox.Wpf.Resources.help.AsBitmapImage(); public static BitmapImage QuestionImage => _question; public static MessageWindow New() { return new MessageWindow(); } public static MessageWindow NewMessage(string message, string title, ImageSource? image = null) { return new MessageWindow() .Title(title) .Message(message) .Image(image) .AddOKButton(); } public static void ShowMessage(string message, string title, ImageSource? image = null) { NewMessage(message, title, image).Display(); } public static MessageWindow NewWarn(string message, string title = "Warning", ImageSource? image = null) { return NewMessage(message, title, image); } public static void Warn(string message, string title = "Warning", ImageSource? image = null) { NewMessage(message, title, image).Display(); } public static MessageWindow NewOKCancel(string message, string title, ImageSource? image = null) { return new MessageWindow() .Title(title) .Message(message) .Image(image) .AddOKButton() .AddCancelButton(); } public static bool ShowOKCancel(string message, string title, ImageSource? image = null) { return NewOKCancel(message, title, image) .Display() .Result == MessageWindowResult.OK; } public static MessageWindow NewYesNo(string message, string title, ImageSource? image = null) { return new MessageWindow() .Title(title) .Message(message) .Image(image) .AddYesButton() .AddNoButton(); } public static bool ShowYesNo(string message, string title, ImageSource? image = null) { return NewYesNo(message, title, image) .Display() .Result == MessageWindowResult.Yes; } public static MessageWindow NewYesNoCancel(string message, string title, ImageSource? image = null) { return new MessageWindow() .Title(title) .Message(message) .Image(image) .AddYesButton() .AddNoButton() .AddCancelButton(); } public static MessageWindowResult ShowYesNoCancel(string message, string title, ImageSource? image = null) { return NewYesNoCancel(message, title, image) .Display().Result; } /// /// Display a message box for an exception, giving options to view the logs. /// /// The message to display. Set to to default to the exception message. /// /// /// If , also logs the exception. public static MessageWindow NewError(string? message, Exception exception, string title = "Error", bool shouldLog = true, ImageSource? image = null) { if (shouldLog) { CoreUtils.LogException(ClientFactory.UserID, exception); } var window = new MessageWindow() .Message(message ?? exception.Message) .Title(title) .Details(CoreUtils.FormatException(exception)) .Image(image ?? _warning) .AddButton(new MessageWindowButton("Email Logs", (window, args) => { EmailLogs_Click(exception); }, MessageWindowButtonPosition.Left)); var showDetailsButton = new MessageWindowButton("Show Details", (win, args) => { win.ShowDetails = !win.ShowDetails; args.Button.Content = win.ShowDetails ? "Hide Details" : "Show Details"; }, MessageWindowButtonPosition.Left); return window.AddButton(showDetailsButton) .AddOKButton(); } private static void EmailLogs_Click(Exception e) { var logFile = Path.Combine(CoreUtils.GetPath(), $"{DateTime.Today:yyyy-MM-dd}.log"); const int nRead = 1024 * 1024; byte[] data; using (var stream = File.OpenRead(logFile)) { if (stream.Length > nRead) { stream.Seek(-nRead, SeekOrigin.End); } data = new BinaryReader(stream).ReadBytes(Math.Min(nRead, (int)stream.Length)); } var message = EmailUtils.CreateMessage( subject: "Error logs", to: "support@prsdigital.com.au", body: $"Error logs for PRS:\n\nException: {CoreUtils.FormatException(e)}"); message.AddAttachment("Error Logs.txt", data); EmailUtils.OpenEmail(message); } /// /// Display a message box for a non-exception error, giving options to view the logs. /// /// The message to display. Set to to default to the exception message. /// /// /// If , also logs the exception. public static MessageWindow NewError(string message, string? details = null, string title = "Error", bool shouldLog = true, ImageSource? image = null) { if (shouldLog) { Logger.Send(LogType.Error, ClientFactory.UserID, details ?? message); } var window = new MessageWindow() .Message(message) .Title(title); if(details is not null) { window.Details(details); } window.Image(image ?? _warning) .AddButton(new MessageWindowButton( "Email Logs", (window, button) => EmailLogs_Click(new Exception(details ?? message)), MessageWindowButtonPosition.Left)); if(details is not null) { var showDetailsButton = new MessageWindowButton("Show Details", (win, args) => { win.ShowDetails = !win.ShowDetails; args.Button.Content = win.ShowDetails ? "Hide Details" : "Show Details"; }, MessageWindowButtonPosition.Left); window.AddButton(showDetailsButton); } return window.AddOKButton(); } public static void ShowError(string? message, Exception exception, string title = "Error", bool shouldLog = true, ImageSource? image = null) { NewError(message, exception, title, shouldLog, image).Display(); } public static void ShowError(string message, string details, string title = "Error", bool shouldLog = true, ImageSource? image = null) { NewError(message, details, title, shouldLog, image).Display(); } private static void ShowLogs_Click(MessageWindow window, MessageWindowButton button) { var console = new MessageWindowConsole("Logs", Path.Combine(CoreUtils.GetPath(), string.Format("{0:yyyy-MM-dd}.log", DateTime.Today))); console.ShowDialog(); } #endregion } public static class MessageWindowBuilder { public static MessageWindow Title(this MessageWindow window, string title) { window.Title = title; return window; } public static MessageWindow Message(this MessageWindow window, string message) { window.Message = message; return window; } public static MessageWindow Image(this MessageWindow window, ImageSource? image) { window.Image = image; return window; } public static MessageWindow Icon(this MessageWindow window, ImageSource image) { window.Icon = image; return window; } public static MessageWindow Details(this MessageWindow window, string details) { window.Details = details; return window; } public static MessageWindow Display(this MessageWindow window) { window.ShowDialog(); return window; } } public class MessageWindowConsole : Console.Console { public string FileName { get; set; } public MessageWindowConsole(string description, string file) : base(description) { FileName = file; ConsoleControl.AllowLoadLogButton = false; } protected override void OnLoaded() { base.OnLoaded(); if (File.Exists(FileName)) { var lines = File.ReadLines(FileName); ConsoleControl.LoadLogEntries(lines); } } protected override string GetLogDirectory() { return CoreUtils.GetPath(); } }