From 2f2e185feab73cf56f1f10df6e3af59085c6510e Mon Sep 17 00:00:00 2001 From: Martijn Hoekstra Date: Fri, 7 Feb 2020 15:55:12 +0100 Subject: [PATCH] show ID for uploads reworks status to an ADT rather than an enum --- Hotsapi.Uploader.Common.Test/MockUploader.cs | 4 +- Hotsapi.Uploader.Common/Analyzer.cs | 14 +- .../Hotsapi.Uploader.Common.csproj | 1 + Hotsapi.Uploader.Common/IUploader.cs | 2 +- Hotsapi.Uploader.Common/Manager.cs | 64 ++- Hotsapi.Uploader.Common/ReplayFile.cs | 11 +- Hotsapi.Uploader.Common/UploadStatus.cs | 82 +++- Hotsapi.Uploader.Common/Uploader.cs | 53 +- .../Hotsapi.Uploader.Windows.csproj | 456 +++++++++--------- Hotsapi.Uploader.Windows/MainWindow.xaml | 20 +- .../UIHelpers/IntToVisibilityConverter.cs | 4 +- .../UIHelpers/UploadColorConverter.cs | 38 +- .../UIHelpers/UploadStatusConverter.cs | 20 +- 13 files changed, 439 insertions(+), 330 deletions(-) diff --git a/Hotsapi.Uploader.Common.Test/MockUploader.cs b/Hotsapi.Uploader.Common.Test/MockUploader.cs index 332e063..82598be 100644 --- a/Hotsapi.Uploader.Common.Test/MockUploader.cs +++ b/Hotsapi.Uploader.Common.Test/MockUploader.cs @@ -28,10 +28,10 @@ public Task Upload(ReplayFile file) UploadCallback(file); return Task.CompletedTask; } - public async Task Upload(string file) + public async Task Upload(string file) { await Task.Delay(100); - return UploadStatus.Success; + return UploadStatus.Successful(-1); } } } diff --git a/Hotsapi.Uploader.Common/Analyzer.cs b/Hotsapi.Uploader.Common/Analyzer.cs index 9dd5eb3..69d6681 100644 --- a/Hotsapi.Uploader.Common/Analyzer.cs +++ b/Hotsapi.Uploader.Common/Analyzer.cs @@ -28,7 +28,7 @@ public Replay Analyze(ReplayFile file) var status = GetPreStatus(replay, parseResult); if (status != null) { - file.UploadStatus = status.Value; + file.UploadStatus = status; } if (parseResult != DataParser.ReplayParseResult.Success) { @@ -44,18 +44,18 @@ public Replay Analyze(ReplayFile file) } } - public UploadStatus? GetPreStatus(Replay replay, DataParser.ReplayParseResult parseResult) + public IUploadStatus GetPreStatus(Replay replay, DataParser.ReplayParseResult parseResult) { switch (parseResult) { case DataParser.ReplayParseResult.ComputerPlayerFound: case DataParser.ReplayParseResult.TryMeMode: - return UploadStatus.AiDetected; + return Rejected.AiDetected; case DataParser.ReplayParseResult.PTRRegion: - return UploadStatus.PtrRegion; + return Rejected.PtrRegion; case DataParser.ReplayParseResult.PreAlphaWipe: - return UploadStatus.TooOld; + return Rejected.TooOld; } if (parseResult != DataParser.ReplayParseResult.Success) { @@ -63,11 +63,11 @@ public Replay Analyze(ReplayFile file) } if (replay.GameMode == GameMode.Custom) { - return UploadStatus.CustomGame; + return Rejected.CustomGame; } if (replay.ReplayBuild < MinimumBuild) { - return UploadStatus.TooOld; + return Rejected.TooOld; } return null; diff --git a/Hotsapi.Uploader.Common/Hotsapi.Uploader.Common.csproj b/Hotsapi.Uploader.Common/Hotsapi.Uploader.Common.csproj index d412fbc..cdbf82c 100644 --- a/Hotsapi.Uploader.Common/Hotsapi.Uploader.Common.csproj +++ b/Hotsapi.Uploader.Common/Hotsapi.Uploader.Common.csproj @@ -3,6 +3,7 @@ netstandard2.0 Debug;Release + 8.0 diff --git a/Hotsapi.Uploader.Common/IUploader.cs b/Hotsapi.Uploader.Common/IUploader.cs index b731770..1e80730 100644 --- a/Hotsapi.Uploader.Common/IUploader.cs +++ b/Hotsapi.Uploader.Common/IUploader.cs @@ -9,6 +9,6 @@ public interface IUploader Task CheckDuplicate(IEnumerable replays); Task GetMinimumBuild(); Task Upload(ReplayFile file); - Task Upload(string file); + Task Upload(string file); } } \ No newline at end of file diff --git a/Hotsapi.Uploader.Common/Manager.cs b/Hotsapi.Uploader.Common/Manager.cs index d994bd2..6b56347 100644 --- a/Hotsapi.Uploader.Common/Manager.cs +++ b/Hotsapi.Uploader.Common/Manager.cs @@ -16,6 +16,12 @@ namespace Hotsapi.Uploader.Common { + public class StatusCount + { + public StatusCount(string status, int count) => (Status, Count) = (status, count); + public string Status { get; } + public int Count { get; } + } public class Manager : INotifyPropertyChanged { /// @@ -49,16 +55,11 @@ public string Status } } - private Dictionary _aggregates = new Dictionary(); + /// /// List of aggregate upload stats /// - public Dictionary Aggregates - { - get { - return _aggregates; - } - } + public IEnumerable Aggregates { get; private set; } = new List(); /// /// Whether to mark replays for upload to hotslogs @@ -103,7 +104,7 @@ public async void Start(IMonitor monitor, IAnalyzer analyzer, IUploader uploader var replays = ScanReplays(); Files.AddRange(replays); - replays.Where(x => x.UploadStatus == UploadStatus.None).Reverse().Map(x => processingQueue.Add(x)); + replays.Where(x => x.UploadStatus == null).Reverse().Map(x => processingQueue.Add(x)); _monitor.ReplayAdded += async (_, e) => { await EnsureFileAvailable(e.Data, 3000); @@ -154,17 +155,50 @@ private async Task UploadLoop() private void RefreshStatusAndAggregates() { _status = Files.Any(x => x.UploadStatus == UploadStatus.InProgress) ? "Uploading..." : "Idle"; - _aggregates = Files.GroupBy(x => x.UploadStatus).ToDictionary(x => x.Key, x => x.Count()); + Aggregates = Aggregate(Files).Select(pair => new StatusCount(pair.Item1, pair.Item2)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Aggregates))); } + //I hate it + private IEnumerable<(String, int)> Aggregate(IEnumerable files) + { + var success = 0; + var unhandled = 0; + var rejected = new List(); + var failed = 0; + var inProgress = 0; + var errors = 0; + foreach(var file in files) { + var status = file.UploadStatus; + if(status == null) { + unhandled++; + } else { + switch (status) { + case UploadSuccess _: success++; break; + case Rejected r: rejected.Add(r); break; + case InternalError _: errors++; break; + case IFailed _: failed++; break; + case InProgress _: inProgress++; break; + default: errors++; break; + } + } + } + if (unhandled > 0) yield return ("Unhandled", unhandled); + if (inProgress > 0) yield return ("In progress", inProgress); + if (success > 0) yield return ("Success", success); + foreach( var (reason, count) in rejected.GroupBy(r => r.Reason).Select(gr => (gr.Key.ToString(), gr.Count()))) { + yield return ($"Rejected: {reason}", count); + } + if (failed > 0) yield return ("Failed", failed); + } + private void SaveReplayList() { try { + // save only replays with fixed status. Will retry failed ones on next launch. - var ignored = new[] { UploadStatus.None, UploadStatus.UploadError, UploadStatus.InProgress }; - _storage.Save(Files.Where(x => !ignored.Contains(x.UploadStatus))); + _storage.Save(Files.Where(x => x.UploadStatus != null && (x.UploadStatus.IsSuccess || x.UploadStatus.IsRejected))); } catch (Exception ex) { _log.Error(ex, "Error saving replay list"); @@ -235,10 +269,10 @@ public async Task EnsureFileAvailable(string filename, int timeout, bool testWri private bool ShouldDelete(ReplayFile file, Replay replay) { return - DeleteAfterUpload.HasFlag(DeleteFiles.PTR) && file.UploadStatus == UploadStatus.PtrRegion || - DeleteAfterUpload.HasFlag(DeleteFiles.Ai) && file.UploadStatus == UploadStatus.AiDetected || - DeleteAfterUpload.HasFlag(DeleteFiles.Custom) && file.UploadStatus == UploadStatus.CustomGame || - file.UploadStatus == UploadStatus.Success && ( + DeleteAfterUpload.HasFlag(DeleteFiles.PTR) && file.UploadStatus == Rejected.PtrRegion || + DeleteAfterUpload.HasFlag(DeleteFiles.Ai) && file.UploadStatus == Rejected.AiDetected || + DeleteAfterUpload.HasFlag(DeleteFiles.Custom) && file.UploadStatus == Rejected.CustomGame || + file.UploadStatus.IsSuccess && ( DeleteAfterUpload.HasFlag(DeleteFiles.Brawl) && replay.GameMode == GameMode.Brawl || DeleteAfterUpload.HasFlag(DeleteFiles.QuickMatch) && replay.GameMode == GameMode.QuickMatch || DeleteAfterUpload.HasFlag(DeleteFiles.UnrankedDraft) && replay.GameMode == GameMode.UnrankedDraft || diff --git a/Hotsapi.Uploader.Common/ReplayFile.cs b/Hotsapi.Uploader.Common/ReplayFile.cs index 6e1ff31..4dc77e3 100644 --- a/Hotsapi.Uploader.Common/ReplayFile.cs +++ b/Hotsapi.Uploader.Common/ReplayFile.cs @@ -14,6 +14,13 @@ public class ReplayFile : INotifyPropertyChanged public string Fingerprint { get; set; } public string Filename { get; set; } public DateTime Created { get; set; } + public int? RemoteID + { + get { + if (UploadStatus is UploadSuccess succ) return succ.UploadID; + else return null; + } + } private bool _deleted; public bool Deleted @@ -31,8 +38,8 @@ public bool Deleted } } - UploadStatus _uploadStatus = UploadStatus.None; - public UploadStatus UploadStatus + IUploadStatus _uploadStatus = null; + public IUploadStatus UploadStatus { get { return _uploadStatus; diff --git a/Hotsapi.Uploader.Common/UploadStatus.cs b/Hotsapi.Uploader.Common/UploadStatus.cs index d2bb9ef..d4a0fa7 100644 --- a/Hotsapi.Uploader.Common/UploadStatus.cs +++ b/Hotsapi.Uploader.Common/UploadStatus.cs @@ -1,20 +1,88 @@ using System; -using System.Collections.Generic; -using System.Linq; namespace Hotsapi.Uploader.Common { - public enum UploadStatus + public interface IUploadStatus + { + bool IsSuccess { get; } + bool IsRejected { get; } + } + public static class UploadStatus + { + public static IUploadStatus InProgress { get; } = new InProgress(); + public static IUploadStatus Successful(int id) => new UploadSuccess(id); + } + public readonly struct UploadSuccess : IUploadStatus + { + public bool IsRejected => false; + public bool IsSuccess => true; + public UploadSuccess(int id) => UploadID = id; + public int UploadID { get; } + } + public readonly struct Rejected : IUploadStatus + { + public bool IsRejected => true; + public bool IsSuccess => false; + public RejectionReason Reason { get; } + public string Details { get; } + private Rejected(RejectionReason reason) : this(reason, reason.ToString()) { } + private Rejected(RejectionReason reason, string details) => (Reason, Details) = (reason, details); + public static IUploadStatus AiDetected { get; } = new Rejected(RejectionReason.AiDetected); + public static IUploadStatus PtrRegion { get; } = new Rejected(RejectionReason.PtrRegion); + public static IUploadStatus TooOld { get; } = new Rejected(RejectionReason.TooOld); + public static IUploadStatus CustomGame { get; } = new Rejected(RejectionReason.CustomGame); + public static IUploadStatus Duplicate { get; } = new Rejected(RejectionReason.Duplicate); + public static IUploadStatus ForReason(RejectionReason reason) + { + switch (reason) { + case RejectionReason.AiDetected: return AiDetected; + case RejectionReason.PtrRegion: return PtrRegion; + case RejectionReason.TooOld: return TooOld; + case RejectionReason.CustomGame: return CustomGame; + case RejectionReason.Duplicate: return Duplicate; + default: return new Rejected(reason); + } + } + + public static IUploadStatus ForUnknownReason(string reason) => new Rejected(RejectionReason.UnknownReason, reason); + } + + /// + /// Represents a status that shouldn't occur, but allows the application + /// to plod on regardless. Each occurrance of an instance of this class represents + /// a bug in the uploader + /// + public readonly struct InternalError : IUploadStatus { + public InternalError(string description) => ErrorDescription = description; + public bool IsSuccess => false; + public bool IsRejected => false; + public string ErrorDescription { get; } + } + //for matching + public interface IFailed : IUploadStatus { + public Exception RawException { get; } + } + public readonly struct Failed : IFailed where E : Exception { + public Failed(E cause) => Cause = cause; + public E Cause { get; } + public Exception RawException => Cause; + public bool IsSuccess => false; + public bool IsRejected => false; + } + + public readonly struct InProgress : IUploadStatus { + public bool IsSuccess => false; + public bool IsRejected => false; + } + + public enum RejectionReason { - None, - Success, - InProgress, - UploadError, Duplicate, AiDetected, CustomGame, PtrRegion, Incomplete, TooOld, + UnknownReason } } diff --git a/Hotsapi.Uploader.Common/Uploader.cs b/Hotsapi.Uploader.Common/Uploader.cs index 130f947..69da64c 100644 --- a/Hotsapi.Uploader.Common/Uploader.cs +++ b/Hotsapi.Uploader.Common/Uploader.cs @@ -14,7 +14,7 @@ public class Uploader : IUploader { private static readonly Logger _log = LogManager.GetCurrentClassLogger(); #if DEBUG - const string ApiEndpoint = "http://hotsapi.local/api/v1"; + const string ApiEndpoint = "https://hotsapi.net/api/v1"; #else const string ApiEndpoint = "https://hotsapi.net/api/v1"; #endif @@ -38,9 +38,10 @@ public async Task Upload(ReplayFile file) file.UploadStatus = UploadStatus.InProgress; if (file.Fingerprint != null && await CheckDuplicate(file.Fingerprint)) { _log.Debug($"File {file} marked as duplicate"); - file.UploadStatus = UploadStatus.Duplicate; + file.UploadStatus = Rejected.Duplicate; } else { - file.UploadStatus = await Upload(file.Filename); + var status = await Upload(file.Filename); + file.UploadStatus = status; } } @@ -49,7 +50,7 @@ public async Task Upload(ReplayFile file) /// /// Path to file /// Upload result - public async Task Upload(string file) + public async Task Upload(string file) { try { string response; @@ -57,29 +58,39 @@ public async Task Upload(string file) var bytes = await client.UploadFileTaskAsync($"{ApiEndpoint}/upload?uploadToHotslogs={UploadToHotslogs}", file); response = Encoding.UTF8.GetString(bytes); } - dynamic json = JObject.Parse(response); - if ((bool)json.success) { - if (Enum.TryParse((string)json.status, out UploadStatus status)) { - _log.Debug($"Uploaded file '{file}': {status}"); - return status; - } else { - _log.Error($"Unknown upload status '{file}': {json.status}"); - return UploadStatus.UploadError; - } - } else { - _log.Warn($"Error uploading file '{file}': {response}"); - return UploadStatus.UploadError; - } + dynamic json = JObject.Parse(response); + return GetResponseStatus(file, response, json); } catch (WebException ex) { if (await CheckApiThrottling(ex.Response)) { return await Upload(file); } _log.Warn(ex, $"Error uploading file '{file}'"); - return UploadStatus.UploadError; + return new Failed(ex); } - } - + } + + private static IUploadStatus GetResponseStatus(string file, string response, dynamic json) + { + if ((bool)json.success) { + if (json.status == "successful") { + _log.Debug($"Uploaded file '{file}': Success"); + int? id = (int?)json.id; + return UploadStatus.Successful(id ?? -1); + } else if (Enum.TryParse((string)json.status, out var reason)) { + _log.Warn($"Uploaded file '{file}' rejected: {reason}"); + return Rejected.ForReason(reason); + } else { + _log.Error($"Unknown upload status '{file}': {json.status}"); + return Rejected.ForUnknownReason(json.status); + } + } else { + _log.Warn($"Error uploading file '{file}': {response}"); + var ex = new Exception(response); + return new Failed(ex); + } + } + /// /// Check replay fingerprint against database to detect duplicate /// @@ -132,7 +143,7 @@ private async Task CheckDuplicate(IEnumerable fingerprints) public async Task CheckDuplicate(IEnumerable replays) { var exists = new HashSet(await CheckDuplicate(replays.Select(x => x.Fingerprint))); - replays.Where(x => exists.Contains(x.Fingerprint)).Map(x => x.UploadStatus = UploadStatus.Duplicate); + replays.Where(x => exists.Contains(x.Fingerprint)).Map(x => x.UploadStatus = Rejected.Duplicate); } /// diff --git a/Hotsapi.Uploader.Windows/Hotsapi.Uploader.Windows.csproj b/Hotsapi.Uploader.Windows/Hotsapi.Uploader.Windows.csproj index 23e0e21..0248812 100644 --- a/Hotsapi.Uploader.Windows/Hotsapi.Uploader.Windows.csproj +++ b/Hotsapi.Uploader.Windows/Hotsapi.Uploader.Windows.csproj @@ -1,229 +1,229 @@ - - - - - Debug - AnyCPU - {F774F86B-410F-410D-9719-ADF892D315D5} - WinExe - Hotsapi.Uploader.Windows - Hotsapi.Uploader - v4.6.2 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - true - - - - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG;NOSQUIRREL - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;NOSQUIRREL - prompt - 4 - - - Resources\uploader_icon_light.ico - - - bin\Zip\ - TRACE;NOSQUIRREL - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Installer\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - - - - 1.5.0.235 - - - - - - - - - - - - - - - - - - - - - - - - - 4.0 - - - - - - - - MSBuild:Compile - Designer - - - - SettingsWindow.xaml - - - - - - - - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - MainWindow.xaml - Code - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - - - Code - - - True - True - Resources.resx - - - True - Settings.settings - True - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - Always - Designer - - - Designer - - - Designer - - - PublicSettingsSingleFileGenerator - Settings.Designer.cs - - - - - Designer - - - - - {DC695BCC-4403-4B20-B4F5-EB80683E5967} - Hotsapi.Uploader.Common - - - - - - - False - - - False - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + AnyCPU + {F774F86B-410F-410D-9719-ADF892D315D5} + WinExe + Hotsapi.Uploader.Windows + Hotsapi.Uploader + v4.6.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG;NOSQUIRREL + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;NOSQUIRREL + prompt + 4 + + + Resources\uploader_icon_light.ico + + + bin\Zip\ + TRACE;NOSQUIRREL + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + true + + + bin\Installer\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + true + + + + + + 1.5.0.235 + + + + + + + + + + + + + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + SettingsWindow.xaml + + + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + Always + Designer + + + Designer + + + Designer + + + PublicSettingsSingleFileGenerator + Settings.Designer.cs + + + + + Designer + + + + + {DC695BCC-4403-4B20-B4F5-EB80683E5967} + Hotsapi.Uploader.Common + + + + + + + False + + + False + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Hotsapi.Uploader.Windows/MainWindow.xaml b/Hotsapi.Uploader.Windows/MainWindow.xaml index ea56f68..ff52f32 100644 --- a/Hotsapi.Uploader.Windows/MainWindow.xaml +++ b/Hotsapi.Uploader.Windows/MainWindow.xaml @@ -47,15 +47,17 @@ - - - - - - - - - + + + + + + + + + + + diff --git a/Hotsapi.Uploader.Windows/UIHelpers/IntToVisibilityConverter.cs b/Hotsapi.Uploader.Windows/UIHelpers/IntToVisibilityConverter.cs index a6d00bb..3806daf 100644 --- a/Hotsapi.Uploader.Windows/UIHelpers/IntToVisibilityConverter.cs +++ b/Hotsapi.Uploader.Windows/UIHelpers/IntToVisibilityConverter.cs @@ -6,9 +6,9 @@ namespace Hotsapi.Uploader.Windows.UIHelpers { - public class IntToVisibilityConverter : GenericValueConverter, Visibility, UploadStatus> + public class IntToVisibilityConverter : GenericValueConverter, Visibility, IUploadStatus> { - protected override Visibility Convert(Dictionary value, UploadStatus parameter) + protected override Visibility Convert(Dictionary value, IUploadStatus parameter) { return value.ContainsKey(parameter) ? Visibility.Visible : Visibility.Collapsed; } diff --git a/Hotsapi.Uploader.Windows/UIHelpers/UploadColorConverter.cs b/Hotsapi.Uploader.Windows/UIHelpers/UploadColorConverter.cs index 0736c34..d080aac 100644 --- a/Hotsapi.Uploader.Windows/UIHelpers/UploadColorConverter.cs +++ b/Hotsapi.Uploader.Windows/UIHelpers/UploadColorConverter.cs @@ -1,39 +1,17 @@ using Hotsapi.Uploader.Common; -using System; -using System.Linq; using System.Windows.Media; namespace Hotsapi.Uploader.Windows.UIHelpers { - public class UploadColorConverter : GenericValueConverter + public class UploadColorConverter : GenericValueConverter { - protected override Brush Convert(UploadStatus value) - { - switch (value) { - case UploadStatus.Success: - return GetBrush("StatusUploadSuccessBrush"); + protected override Brush Convert(IUploadStatus value) => + value == null ? GetBrush("StatusUploadNeutralBrush") : + value.IsSuccess ? GetBrush("StatusUploadSuccessBrush") : + value == UploadStatus.InProgress ? GetBrush("StatusUploadInProgressBrush") : + value is Rejected rej && rej.Reason != RejectionReason.Incomplete ? GetBrush("StatusUploadNeutralBrush") : + GetBrush("StatusUploadFailedBrush"); - case UploadStatus.InProgress: - return GetBrush("StatusUploadInProgressBrush"); - - case UploadStatus.Duplicate: - case UploadStatus.AiDetected: - case UploadStatus.CustomGame: - case UploadStatus.PtrRegion: - case UploadStatus.TooOld: - return GetBrush("StatusUploadNeutralBrush"); - - case UploadStatus.None: - case UploadStatus.UploadError: - case UploadStatus.Incomplete: - default: - return GetBrush("StatusUploadFailedBrush"); - } - } - - private Brush GetBrush(string key) - { - return App.Current.Resources[key] as Brush; - } + private Brush GetBrush(string key) => App.Current.Resources[key] as Brush; } } diff --git a/Hotsapi.Uploader.Windows/UIHelpers/UploadStatusConverter.cs b/Hotsapi.Uploader.Windows/UIHelpers/UploadStatusConverter.cs index 2cac63d..5f1542c 100644 --- a/Hotsapi.Uploader.Windows/UIHelpers/UploadStatusConverter.cs +++ b/Hotsapi.Uploader.Windows/UIHelpers/UploadStatusConverter.cs @@ -6,15 +6,23 @@ namespace Hotsapi.Uploader.Windows.UIHelpers { - public class UploadStatusConverter : GenericValueConverter + public class UploadStatusConverter : GenericValueConverter { - protected override string Convert(UploadStatus value) + protected override string Convert(IUploadStatus value) { - if (value == UploadStatus.None) { - return ""; + if (value == null) { + return "Unhandled"; + } + string unPascal(string pascalCased) => Regex.Replace(pascalCased, "([a-z])([A-Z])", m => $"{m.Groups[1].Value} {m.Groups[2].Value.ToLower()}"); + + switch (value) { + case UploadSuccess s: return (s.UploadID > 0) ? $"Uploaded({s.UploadID})" : "Uploaded"; + case Rejected r: return unPascal(r.Reason.ToString()); + case InternalError err: return err.ErrorDescription; + case IFailed f: return f.RawException.Message; + case InProgress _: return "In Progress"; + default: return "Unknown"; } - // Convert "EnumItems" to "Enum items" - return Regex.Replace(value.ToString(), "([a-z])([A-Z])", m => $"{m.Groups[1].Value} {m.Groups[2].Value.ToLower()}"); } } }