Compare commits

...
9 Commits
Author SHA1 Message Date
amacocianandGitHub 15a67ea28c Minor fixes
Stop showing browser when browser is disabled
Prevent setting address of browser when browser is disabled (should prevent browser from instantiating when disabled)
Ignore errors from browser caused by switching view before the browser could be initialized
Provide expression to filter logs
Button to export logs to json file
2021-06-07 14:33:54 +02:00
amacocianandGitHub c6b62fd5fd Logging improvements
Log uncaught exceptions
Logview expander
2021-06-07 00:34:43 +02:00
amacocianandGitHub 3c0877c1a0 Ability to keep a local cache of icons (#16) 2021-06-03 21:12:31 +02:00
967b57b9a8 Fix build code encoding/decoding view (#15)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-06-03 20:40:56 +02:00
amacocianandGitHub f47cc319b5 Build template code field 2021-06-03 19:57:29 +02:00
amacocianandGitHub 26da84c4e7 Update to new version of wpfextended
Fix a bug where version parsing would ignore all 0s before the first digit
Remove LoggerFactory and LoggerProviders duplicated in WpfExtended
2021-06-03 14:48:35 +02:00
a39e8dc5c6 Separate CV from message (#12)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-06-01 21:57:27 +02:00
1bb4669135 Introduce correlated logs (#11)
Co-authored-by: Alexandru Macocian <almacoci@microsoft.com>
2021-06-01 20:43:55 +02:00
amacocianandGitHub 0203eef006 Rework logging (#10)
* Rework log storing into a litedb.
LogView implementation to view logs.
Create crash.log on crash.

* Rename Logs namespace to Logging.
2021-05-18 19:49:51 +02:00
35 changed files with 793 additions and 137 deletions
+1
View File
@@ -13,6 +13,7 @@
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
+2
View File
@@ -10,6 +10,8 @@ namespace Daybreak.Tests.Models
[TestClass]
public class VersionTests
{
[DataRow("v0.1.0.0.1", "v0.1.0.0.1")]
[DataRow("v0.1.0.0.1.0.0.0.0", "v0.1.0.0.1")]
[DataRow("v0.1.0", "v0.1")]
[DataRow("0.1.0", "0.1")]
[DataRow("v0.1.0.0.0", "v0.1")]
@@ -0,0 +1,75 @@
using Daybreak.Services.Logging;
using FluentAssertions;
using LiteDB;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Linq;
using WpfExtended.Logging;
namespace Daybreak.Tests.Services
{
[TestClass]
public class JsonLoggerProviderTests
{
private ILogsManager logsManager;
private ILoggerProvider loggerProvider;
private ILiteDatabase liteDatabase;
[TestInitialize]
public void InitializeProvider()
{
File.Delete("Daybreak.db");
this.liteDatabase = new LiteDatabase("Daybreak.db");
this.logsManager = new JsonLogsManager(this.liteDatabase);
this.loggerProvider = new CVLoggerProvider(this.logsManager);
}
[TestCleanup]
public void TestCleanup()
{
this.liteDatabase.Dispose();
}
[TestMethod]
public void CreateLoggerReturnsLogger()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.Should().NotBeNull();
}
[TestMethod]
public void LoggerLogsAndReaderReadsFiltered()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogTrace("Logging some trace");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
this.logsManager.GetLogs(l => l.LogLevel < LogLevel.Information).Should().HaveCount(1);
var log = this.logsManager.GetLogs(l => l.LogLevel < LogLevel.Information).First();
log.LogLevel.Should().Be(LogLevel.Error);
}
[TestMethod]
public void LoggerLogsAndReaderReads()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
this.logsManager.GetLogs().Should().HaveCount(2);
}
[TestMethod]
public void DeletingLogsShouldDeleteLogs()
{
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
this.logsManager.GetLogs().Should().HaveCount(2);
this.logsManager.DeleteLogs();
this.logsManager.GetLogs().Should().HaveCount(0);
}
}
}
+5 -5
View File
@@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak", "Daybreak\Daybreak.csproj", "{AA45C2B1-8BD0-466C-9271-699F168905AF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Daybreak.Tests", "Daybreak.Tests\Daybreak.Tests.csproj", "{C911C1C2-6566-419C-89C7-F802EB3FE709}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak.Tests", "Daybreak.Tests\Daybreak.Tests.csproj", "{C911C1C2-6566-419C-89C7-F802EB3FE709}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,12 +17,12 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.ActiveCfg = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.Build.0 = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.Build.0 = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.Build.0 = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.ActiveCfg = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.Build.0 = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.ActiveCfg = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.Build.0 = Release|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -37,5 +37,7 @@ namespace Daybreak.Configuration
public bool PlaceShortcut { get; set; }
[JsonProperty("AutoCheckUpdate")]
public bool AutoCheckUpdate { get; set; } = true;
[JsonProperty("KeepLocalIconCache")]
public bool KeepLocalIconCache { get; set; } = true;
}
}
+35 -10
View File
@@ -5,6 +5,7 @@ using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.Credentials;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.Logging;
using Daybreak.Services.Mutex;
using Daybreak.Services.Privilege;
using Daybreak.Services.Runtime;
@@ -14,32 +15,55 @@ using Daybreak.Services.Shortcuts;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
using Slim;
using System.Extensions;
using System.Net.Http;
using System.Windows.Extensions.Http;
using LiteDB;
using System.Windows.Extensions;
namespace Daybreak.Configuration
{
public static class ProjectConfiguration
{
public static void RegisterResolvers(IServiceManager serviceManager)
{
serviceManager.ThrowIfNull(nameof(serviceManager));
serviceManager.RegisterResolver(
new HttpClientResolver()
.WithHttpMessageHandlerFactory((serviceProvider, categoryType) =>
{
var loggerType = typeof(ILogger<>).MakeGenericType(categoryType);
var logger = serviceProvider.GetService(loggerType).As<ILogger>();
var handler = new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
return handler;
}));
}
public static void RegisterServices(IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterSingleton<ICredentialManager, CredentialManager>();
serviceProducer.RegisterSingleton<ApplicationLifetimeManager>();
serviceProducer.RegisterSingleton<ViewManager>();
serviceProducer.RegisterSingleton<IApplicationLauncher, ApplicationLauncher>();
serviceProducer.RegisterSingleton<IScreenshotProvider, ScreenshotProvider>();
serviceProducer.RegisterSingleton<IConfigurationManager, ConfigurationManager>();
serviceProducer.RegisterSingleton<IBloogumClient, BloogumClient>();
serviceProducer.RegisterSingleton<IApplicationUpdater, ApplicationUpdater>();
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
serviceProducer.RegisterSingleton<IRuntimeStore, RuntimeStore>();
serviceProducer.RegisterSingleton<IBuildTemplateManager, BuildTemplateManager>();
serviceProducer.RegisterSingleton<IIconRetriever, IconRetriever>();
serviceProducer.RegisterSingleton<IPrivilegeManager, PrivilegeManager>();
serviceProducer.RegisterSingleton<IScreenManager, ScreenManager>();
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
serviceProducer.RegisterScoped<ICredentialManager, CredentialManager>();
serviceProducer.RegisterScoped<IApplicationLauncher, ApplicationLauncher>();
serviceProducer.RegisterScoped<IScreenshotProvider, ScreenshotProvider>();
serviceProducer.RegisterScoped<IBloogumClient, BloogumClient>();
serviceProducer.RegisterScoped<IApplicationUpdater, ApplicationUpdater>();
serviceProducer.RegisterScoped<IBuildTemplateManager, BuildTemplateManager>();
serviceProducer.RegisterScoped<IIconRetriever, IconRetriever>();
serviceProducer.RegisterScoped<IPrivilegeManager, PrivilegeManager>();
serviceProducer.RegisterScoped<IScreenManager, ScreenManager>();
serviceProducer.RegisterLogWriter<ILogsManager, JsonLogsManager>();
}
public static void RegisterLifetimeServices(IApplicationLifetimeProducer applicationLifetimeProducer)
{
@@ -66,6 +90,7 @@ namespace Daybreak.Configuration
viewProducer.RegisterView<RequestElevationView>();
viewProducer.RegisterView<ScreenChoiceView>();
viewProducer.RegisterView<VersionManagementView>();
viewProducer.RegisterView<LogsView>();
}
}
}
@@ -1,5 +1,4 @@
using Daybreak.Launch;
using Daybreak.Models.Browser;
using Daybreak.Models.Browser;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
@@ -23,15 +22,18 @@ namespace Daybreak.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class ChromiumBrowserWrapper : UserControl
{
public static readonly DependencyProperty AddressProperty =
DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(Address));
private const string BrowserDownloadLink = "https://developer.microsoft.com/en-us/microsoft-edge/webview2/";
public event EventHandler<string> FavoriteUriChanged;
public event EventHandler MaximizeClicked;
public event EventHandler<Build> BuildDecoded;
private readonly IConfigurationManager configurationManager;
private readonly ILogger<ChromiumBrowserWrapper> logger;
private readonly IBuildTemplateManager buildTemplateManager;
private IConfigurationManager configurationManager;
private ILogger<ChromiumBrowserWrapper> logger;
private IBuildTemplateManager buildTemplateManager;
private CoreWebView2Environment coreWebView2Environment;
[GenerateDependencyProperty(InitialValue = true)]
@@ -40,7 +42,7 @@ namespace Daybreak.Controls
private bool canNavigate;
[GenerateDependencyProperty(InitialValue = true)]
private bool controlsEnabled;
[GenerateDependencyProperty(InitialValue = true)]
[GenerateDependencyProperty]
private bool browserSupported;
[GenerateDependencyProperty]
private bool addressBarReadonly;
@@ -50,17 +52,22 @@ namespace Daybreak.Controls
private bool navigating;
[GenerateDependencyProperty]
private string favoriteAddress;
[GenerateDependencyProperty]
private string address;
public string Address
{
get => this.GetTypedValue<string>(AddressProperty);
set
{
if (this.BrowserSupported is true)
{
this.SetValue(AddressProperty, value);
}
}
}
public ChromiumBrowserWrapper()
{
this.configurationManager = Launcher.ApplicationServiceManager.GetService<IConfigurationManager>();
this.logger = Launcher.ApplicationServiceManager.GetService<ILogger<ChromiumBrowserWrapper>>();
this.buildTemplateManager = Launcher.ApplicationServiceManager.GetService<IBuildTemplateManager>();
this.InitializeComponent();
this.InitializeEnvironment();
this.InitializeBrowser();
this.WebBrowser.IsEnabled = false;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
@@ -72,6 +79,18 @@ namespace Daybreak.Controls
}
}
public async void InitializeBrowser(
IConfigurationManager configurationManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.configurationManager = configurationManager;
this.buildTemplateManager = buildTemplateManager;
this.logger = logger;
this.InitializeEnvironment();
await this.InitializeBrowser();
}
public async void ReinitializeBrowser()
{
await this.InitializeBrowser();
@@ -101,6 +120,7 @@ namespace Daybreak.Controls
{
if (this.BrowserSupported is true)
{
this.WebBrowser.IsEnabled = true;
await this.WebBrowser.EnsureCoreWebView2Async(this.coreWebView2Environment);
this.AddressBarReadonly = this.configurationManager.GetConfiguration().AddressBarReadonly;
this.CanDownloadBuild = this.configurationManager.GetConfiguration().ExperimentalFeatures.DynamicBuildLoading;
+14
View File
@@ -0,0 +1,14 @@
<UserControl x:Class="Daybreak.Controls.LogsGlyph"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m7,3l0,-1.00209c0,-1.09965 0.89762,-1.99791 2.00488,-1.99791l0.99024,0c1.11098,0 2.00488,0.8945 2.00488,1.99791l0,1.00209l2.00442,0c0.55074,0 0.99558,0.44725 0.99558,0.99896l0,1.00208c0,0.5563 -0.44574,0.99896 -0.99558,0.99896l-9.00884,0c-0.55074,0 -0.99558,-0.44725 -0.99558,-0.99896l0,-1.00208c0,-0.5563 0.44574,-0.99896 0.99558,-0.99896l2.00442,0l0,0zm8.99999,1l1.00259,0c1.10649,0 1.99742,0.89704 1.99742,2.00359l0,20.99282c0,1.11383 -0.89428,2.00359 -1.99742,2.00359l-15.00516,0c-1.10649,0 -1.99742,-0.89704 -1.99742,-2.00359l0,-20.99282c0,-1.11383 0.89428,-2.00359 1.99742,-2.00359l1.00259,0c-0.00001,0.00163 -0.00001,0.00325 -0.00001,0.00488l0,0.99024c0,1.10726 0.89354,2.00488 2.00276,2.00488l8.99448,0c1.10609,0 2.00276,-0.8939 2.00276,-2.00488l0,-0.99024c0,-0.00163 0,-0.00325 -0.00001,-0.00488l0,0l0,0zm-6.49999,-1c0.27614,0 0.5,-0.22386 0.5,-0.5c0,-0.27614 -0.22386,-0.5 -0.5,-0.5c-0.27614,0 -0.5,0.22386 -0.5,0.5c0,0.27614 0.22386,0.5 0.5,0.5l0,0zm-6.5,8l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0zm0,3l0,1l13,0l0,-1l-13,0l0,0z"></Path>
</Viewbox>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace Daybreak.Controls
{
/// <summary>
/// Interaction logic for LogsGlyph.xaml
/// </summary>
public partial class LogsGlyph : UserControl
{
public LogsGlyph()
{
InitializeComponent();
}
}
}
@@ -15,6 +15,7 @@ namespace Daybreak.Controls
public partial class AttributeTemplate : UserControl
{
public event EventHandler<AttributeEntry> HelpClicked;
public event EventHandler<AttributeEntry> AttributeChanged;
[GenerateDependencyProperty(InitialValue = false)]
private bool canAdd;
@@ -50,6 +51,7 @@ namespace Daybreak.Controls
this.DataContext.As<AttributeEntry>().Points--;
this.CanSubtract = this.DataContext.As<AttributeEntry>().Points > 0;
this.CanAdd = true;
this.AttributeChanged?.Invoke(this, this.DataContext.As<AttributeEntry>());
}
}
@@ -60,6 +62,7 @@ namespace Daybreak.Controls
this.DataContext.As<AttributeEntry>().Points++;
this.CanAdd = this.DataContext.As<AttributeEntry>().Points < 12;
this.CanSubtract = true;
this.AttributeChanged?.Invoke(this, this.DataContext.As<AttributeEntry>());
}
}
@@ -75,7 +75,7 @@
HorizontalContentAlignment="Stretch" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<local:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked"></local:AttributeTemplate>
<local:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked" AttributeChanged="AttributeTemplate_AttributeChanged"></local:AttributeTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -95,42 +95,42 @@
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<local:SkillTemplate Grid.Column="0"
<local:SkillTemplate Grid.Column="0" x:Name="SkillTemplate0"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill0, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="1"
<local:SkillTemplate Grid.Column="1" x:Name="SkillTemplate1"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill1, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="2"
<local:SkillTemplate Grid.Column="2" x:Name="SkillTemplate2"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill2, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="3"
<local:SkillTemplate Grid.Column="3" x:Name="SkillTemplate3"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill3, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="4"
<local:SkillTemplate Grid.Column="4" x:Name="SkillTemplate4"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill4, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="5"
<local:SkillTemplate Grid.Column="5" x:Name="SkillTemplate5"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill5, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="6"
<local:SkillTemplate Grid.Column="6" x:Name="SkillTemplate6"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill6, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="7"
<local:SkillTemplate Grid.Column="7" x:Name="SkillTemplate7"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill7, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
@@ -1,4 +1,9 @@
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.IconRetrieve;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Extensions;
@@ -19,10 +24,13 @@ namespace Daybreak.Controls
private const string InfoNamePlaceholder = "[NAME]";
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
private bool suppressBuildChanged = false;
private bool loadedProperties = false;
private BuildEntry loadedBuild;
private SkillTemplate selectingSkillTemplate;
public event EventHandler BuildChanged;
[GenerateDependencyProperty]
private Profession primaryProfession;
[GenerateDependencyProperty]
@@ -54,6 +62,23 @@ namespace Daybreak.Controls
this.DataContextChanged += BuildTemplate_DataContextChanged;
}
public void InitializeTemplate(
IIconRetriever iconRetriever,
IConfigurationManager configurationManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.SkillBrowser.InitializeBrowser(configurationManager, buildTemplateManager, logger);
this.SkillTemplate0.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate1.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate2.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate3.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate4.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate5.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate6.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate7.InitializeSkillTemplate(iconRetriever);
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
@@ -74,6 +99,33 @@ namespace Daybreak.Controls
}
this.LoadSkills();
this.LoadAttributes();
if (this.suppressBuildChanged is false)
{
this.BuildChanged?.Invoke(this, new EventArgs());
}
}
if (e.Property == Skill0Property ||
e.Property == Skill1Property ||
e.Property == Skill2Property ||
e.Property == Skill3Property ||
e.Property == Skill4Property ||
e.Property == Skill5Property ||
e.Property == Skill6Property ||
e.Property == Skill7Property)
{
if (this.suppressBuildChanged is false)
{
this.loadedBuild.Build.Skills[0] = this.Skill0;
this.loadedBuild.Build.Skills[1] = this.Skill1;
this.loadedBuild.Build.Skills[2] = this.Skill2;
this.loadedBuild.Build.Skills[3] = this.Skill3;
this.loadedBuild.Build.Skills[4] = this.Skill4;
this.loadedBuild.Build.Skills[5] = this.Skill5;
this.loadedBuild.Build.Skills[6] = this.Skill6;
this.loadedBuild.Build.Skills[7] = this.Skill7;
this.BuildChanged?.Invoke(this, new EventArgs());
}
}
}
@@ -197,6 +249,7 @@ namespace Daybreak.Controls
private void LoadBuild()
{
this.suppressBuildChanged = true;
var build = this.DataContext.As<BuildEntry>();
this.loadedBuild = build;
this.PrimaryProfession = build.Build.Primary;
@@ -209,6 +262,7 @@ namespace Daybreak.Controls
this.Skill5 = build.Build.Skills[5];
this.Skill6 = build.Build.Skills[6];
this.Skill7 = build.Build.Skills[7];
this.suppressBuildChanged = false;
}
private void BrowseToInfo(string infoName)
@@ -220,13 +274,19 @@ namespace Daybreak.Controls
private void ShowInfoBrowser()
{
this.SkillBrowser.Width = 400;
this.SkillsListView.Width = 0;
if (this.SkillBrowser.BrowserSupported is true)
{
this.SkillBrowser.Width = 400;
this.SkillsListView.Width = 0;
}
}
private void HideInfoBrowser()
{
this.SkillBrowser.Width = 0;
if (this.SkillBrowser.BrowserSupported is true)
{
this.SkillBrowser.Width = 0;
}
}
private void ShowSkillListView()
@@ -273,6 +333,11 @@ namespace Daybreak.Controls
this.BrowseToInfo(e.Attribute.Name);
}
private void AttributeTemplate_AttributeChanged(object sender, AttributeEntry e)
{
this.BuildChanged?.Invoke(this, new EventArgs());
}
private void SkillTemplate_Clicked(object sender, RoutedEventArgs e)
{
var skill = sender.As<SkillTemplate>().DataContext.As<Skill>();
@@ -0,0 +1,18 @@
<UserControl x:Class="Daybreak.Controls.LogMessageTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
x:Name="_this"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch">
<TextBlock Text="{Binding ElementName=_this, Path=Message, Mode=OneWay}" Background="Transparent"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
MouseLeftButtonDown="TextBox_MouseLeftButtonDown" MaxHeight="18" TextWrapping="Wrap"></TextBlock>
</Grid>
</UserControl>
@@ -0,0 +1,36 @@
using System.Windows.Extensions;
using System.Windows.Controls;
using System.Windows.Input;
using System.Extensions;
namespace Daybreak.Controls
{
/// <summary>
/// Interaction logic for LogTemplate.xaml
/// </summary>
public partial class LogMessageTemplate : UserControl
{
private bool expanded;
[GenerateDependencyProperty]
private string message;
public LogMessageTemplate()
{
this.InitializeComponent();
}
private void TextBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs eventArgs)
{
this.expanded = !this.expanded;
if (this.expanded)
{
sender.As<TextBlock>().MaxHeight = double.MaxValue;
}
else
{
sender.As<TextBlock>().MaxHeight = 18;
}
}
}
}
@@ -23,7 +23,7 @@ namespace Daybreak.Controls
public event EventHandler<RoutedEventArgs> Clicked;
public event EventHandler RemoveClicked;
private readonly IIconRetriever iconRetriever;
private IIconRetriever iconRetriever;
[GenerateDependencyProperty]
private ImageSource imageSource;
@@ -32,22 +32,26 @@ namespace Daybreak.Controls
public SkillTemplate()
{
this.iconRetriever = Launcher.ApplicationServiceManager.GetService<IIconRetriever>();
this.InitializeComponent();
this.DataContextChanged += SkillTemplate_DataContextChanged;
}
public void InitializeSkillTemplate(IIconRetriever iconRetriever)
{
this.iconRetriever = iconRetriever;
}
private void SkillTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is Skill skill)
{
if (skill != Skill.NoSkill)
{
Task.Run(() => GetImageStream(skill)).ContinueWith((previousTask) =>
Task.Run(() => this.GetImageStream(skill)).ContinueWith((previousTask) =>
{
this.Dispatcher.Invoke(() =>
{
this.ImageSource = GetImageSource(previousTask.Result);
this.ImageSource = this.GetImageSource(previousTask.Result);
});
});
}
@@ -88,6 +92,11 @@ namespace Daybreak.Controls
}
private async Task<Stream> GetImageStream(Skill skill)
{
if (this.iconRetriever is null)
{
return null;
}
var maybeStream = await this.iconRetriever.GetIcon(skill);
return maybeStream.ExtractValue();
}
+8 -6
View File
@@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
@@ -11,21 +10,24 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.8.9.2</Version>
<Version>0.9.2.2</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
<PackageReference Include="LiteDB" Version="5.0.10" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.818.41" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.864.35" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
<PackageReference Include="Slim" Version="1.3.3" />
<PackageReference Include="Slim" Version="1.4.3" />
<PackageReference Include="System.Windows.Interactivity.WPF" Version="2.0.20525" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.2.0" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.3.0" />
<PackageReference Include="WCL" Version="1.0.2" />
<PackageReference Include="WpfExtended" Version="0.3.2" />
<PackageReference Include="WpfExtended" Version="0.4.2" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
<PackageReference Include="WpfScreenHelper" Version="1.0.0" />
</ItemGroup>
+27 -24
View File
@@ -2,24 +2,21 @@
using Daybreak.Exceptions;
using Daybreak.Services.ApplicationLifetime;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using NReco.Logging.File;
using Slim;
using System;
using System.Extensions;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Extensions;
using System.Windows.Extensions.Http;
namespace Daybreak.Launch
{
public sealed class Launcher : ExtendedApplication<MainWindow>
{
public static IServiceManager ApplicationServiceManager { get; private set; }
private ILogger logger;
private readonly static Launcher launcher = new();
[STAThread]
@@ -28,26 +25,14 @@ namespace Daybreak.Launch
return LaunchMainWindow();
}
protected override ILoggerFactory SetupLoggerFactory()
{
var factory = new LoggerFactory();
factory.AddProvider(new FileLoggerProvider("Daybreak.db"));
return factory;
}
protected override void SetupServiceManager(IServiceManager serviceManager)
{
serviceManager.RegisterResolver(
new HttpClientResolver()
.WithHttpMessageHandlerFactory((serviceProvider, categoryType) =>
{
var loggerType = typeof(ILogger<>).MakeGenericType(categoryType);
var logger = serviceProvider.GetService(loggerType).As<ILogger>();
return new LoggingHttpHandler(logger, new HttpClientHandler());
}));
ProjectConfiguration.RegisterResolvers(serviceManager);
}
protected override void RegisterServices(IServiceProducer serviceProducer)
{
ProjectConfiguration.RegisterServices(this.ServiceManager);
ServiceManager.BuildSingletons();
ProjectConfiguration.RegisterLifetimeServices(this.ServiceManager.GetService<IApplicationLifetimeManager>());
ProjectConfiguration.RegisterViews(this.ServiceManager.GetService<IViewManager>());
}
@@ -58,11 +43,18 @@ namespace Daybreak.Launch
return false;
}
this.ServiceManager.GetService<ILogger>().LogCritical(e, $"Unhandled exception");
if (e is FatalException fatalException)
{
this.ServiceManager.GetService<ILogger>().LogCritical(e, $"{nameof(FatalException)} encountered. Closing application.");
this.logger.LogCritical(e, $"{nameof(FatalException)} encountered. Closing application");
MessageBox.Show(fatalException.ToString());
File.WriteAllText("crash.log", e.ToString());
return false;
}
else if (e is TargetInvocationException targetInvocationException && e.InnerException is FatalException innerFatalException)
{
this.logger.LogCritical(e, $"{nameof(FatalException)} encountered. Closing application");
MessageBox.Show(innerFatalException.ToString());
File.WriteAllText("crash.log", e.ToString());
return false;
}
else if (e is AggregateException aggregateException)
@@ -72,19 +64,30 @@ namespace Daybreak.Launch
{
/*
* Ignore exception caused by browser failing to initialize due to missing window.
* Likely caused by switching windows before browser was initialized.
* Likely caused by switching views before browser was initialized.
*/
this.logger.LogError(e, "Failed to initialize browser");
return true;
}
}
else if (e.Message.Contains("Invalid window handle.") && e.StackTrace.Contains("CoreWebView2Environment.CreateCoreWebView2ControllerAsync"))
{
/*
* Ignore exception caused by browser failing to initialize due to missing window.
* Likely caused by switching views before the browser was initialized.
*/
this.logger.LogError(e, "Failed to initialize browser");
return true;
}
this.logger.LogError(e, $"Unhandled exception caught {e.GetType()}");
MessageBox.Show(e.ToString());
return true;
}
protected override void ApplicationStarting()
{
ApplicationServiceManager = this.ServiceManager;
this.ServiceManager.GetService<IApplicationLifetimeManager>().OnStartup();
this.logger = this.ServiceManager.GetService<ILogger<Launcher>>();
this.RegisterViewContainer();
}
protected override void ApplicationClosing()
+15
View File
@@ -0,0 +1,15 @@
using Microsoft.Extensions.Logging;
using System;
namespace Daybreak.Models
{
public sealed class Log
{
public string Message { get; set; }
public string Category { get; set; }
public LogLevel LogLevel { get; set; }
public string CorrelationVector { get; set; }
public string EventId { get; set; }
public DateTime LogTime { get; set; }
}
}
+14
View File
@@ -0,0 +1,14 @@
using Microsoft.CorrelationVector;
namespace Daybreak.Models
{
public class ScopeMetadata
{
public CorrelationVector CorrelationVector { get; set; }
public ScopeMetadata(CorrelationVector correlationVector)
{
this.CorrelationVector = correlationVector;
}
}
}
+4
View File
@@ -68,6 +68,10 @@ namespace Daybreak.Models.Versioning
{
parts.RemoveAt(i);
}
else
{
break;
}
}
parsedVersion = new Version
@@ -1,4 +1,5 @@
using Daybreak.Models.Builds;
using Daybreak.Services.Configuration;
using HtmlAgilityPack;
using Microsoft.Extensions.Logging;
using System;
@@ -15,20 +16,48 @@ namespace Daybreak.Services.IconRetrieve
private const string NamePlaceholder = "[SKILLNAME]";
private const string BaseUrl = "https://wiki.guildwars.com";
private const string QueryUrl = $"wiki/File:{NamePlaceholder}.jpg";
private const string IconsDirectoryName = "Icons";
private const string IconsLocation = $"{IconsDirectoryName}/{NamePlaceholder}.jpg";
private readonly IHttpClient<IconRetriever> httpClient;
private readonly ILogger<IconRetriever> logger;
private readonly IConfigurationManager configurationManager;
public IconRetriever(
ILogger<IconRetriever> logger,
IHttpClient<IconRetriever> httpClient)
IHttpClient<IconRetriever> httpClient,
IConfigurationManager configurationManager)
{
this.logger = logger.ThrowIfNull(nameof(logger));
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
this.httpClient.BaseAddress = new Uri(BaseUrl);
if (Directory.Exists(IconsDirectoryName) is false)
{
Directory.CreateDirectory(IconsDirectoryName);
}
}
public async Task<Optional<Stream>> GetIcon(Skill skill)
{
if (this.configurationManager.GetConfiguration().KeepLocalIconCache)
{
this.logger.LogInformation($"{nameof(IconRetriever)} configured to look first in cache before downloading icons");
var maybeIcon = await this.GetLocalIcon(skill);
if (maybeIcon.ExtractValue() is Stream stream)
{
return stream;
}
}
else
{
this.logger.LogInformation($"{nameof(IconRetriever)} configured to skip local cache. Downloading icon");
}
return await this.DownloadIcon(skill);
}
private async Task<Optional<Stream>> DownloadIcon(Skill skill)
{
var curedSkillName = skill.Name
.Replace(" ", "_");
@@ -56,13 +85,42 @@ namespace Daybreak.Services.IconRetrieve
if (response.IsSuccessStatusCode)
{
this.logger.LogInformation("Retrieved latest icon stream");
return new MemoryStream(await iconResponse.Content.ReadAsByteArrayAsync());
var iconData = await iconResponse.Content.ReadAsByteArrayAsync();
if (this.configurationManager.GetConfiguration().KeepLocalIconCache)
{
await this.SaveIconLocally(skill, iconData);
}
return new MemoryStream(iconData);
}
this.logger.LogError($"Failed to retrieve icon from {BaseUrl + "/" + url}");
return Optional.None<Stream>();
}
private async Task<Optional<Stream>> GetLocalIcon(Skill skill)
{
var curedSkillName = skill.Name
.Replace(" ", "_")
.Replace("\"", "");
this.logger.LogInformation("Checking local icon cache");
if (File.Exists(IconsLocation.Replace(NamePlaceholder, curedSkillName)))
{
this.logger.LogInformation("Local icon cache found. Retrieving icon");
return new MemoryStream(await File.ReadAllBytesAsync(IconsLocation.Replace(NamePlaceholder, curedSkillName)));
}
this.logger.LogWarning("No local icon cache found");
return Optional.None<Stream>();
}
private async Task SaveIconLocally(Skill skill, byte[] data)
{
var curedSkillName = skill.Name
.Replace(" ", "_")
.Replace("\"", "");
await File.WriteAllBytesAsync(IconsLocation.Replace(NamePlaceholder, curedSkillName), data);
}
private static string GetHref(HtmlDocument doc)
{
foreach (var child in doc.DocumentNode.Descendants("a"))
+15
View File
@@ -0,0 +1,15 @@
using Daybreak.Models;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using WpfExtended.Logging;
namespace Daybreak.Services.Logging
{
public interface ILogsManager : ILogsWriter
{
IEnumerable<Log> GetLogs(Expression<Func<Log, bool>> filter);
IEnumerable<Log> GetLogs();
int DeleteLogs();
}
}
@@ -0,0 +1,46 @@
using LiteDB;
using System;
using System.Collections.Generic;
using System.Extensions;
using System.Linq.Expressions;
using WpfExtended.Models;
namespace Daybreak.Services.Logging
{
public sealed class JsonLogsManager : ILogsManager
{
private readonly ILiteDatabase liteDatabase;
public JsonLogsManager(ILiteDatabase liteDatabase)
{
this.liteDatabase = liteDatabase.ThrowIfNull(nameof(liteDatabase));
}
public IEnumerable<Models.Log> GetLogs(Expression<Func<Models.Log, bool>> filter)
{
return this.liteDatabase.GetCollection<Models.Log>().Find(filter);
}
public IEnumerable<Models.Log> GetLogs()
{
return this.liteDatabase.GetCollection<Models.Log>().FindAll();
}
public void WriteLog(Log log)
{
var dbLog = new Models.Log
{
EventId = log.EventId,
Message = log.Exception is null ? log.Message : $"{log.Message}{Environment.NewLine}{log.Exception}",
Category = log.Category,
LogLevel = log.LogLevel,
LogTime = log.LogTime,
CorrelationVector = log.CorrelationVector
};
this.liteDatabase.GetCollection<Models.Log>().Insert(dbLog);
}
public int DeleteLogs()
{
return this.liteDatabase.GetCollection<Models.Log>().DeleteAll();
}
}
}
+6 -2
View File
@@ -1,4 +1,6 @@
using Slim;
using Daybreak.Models;
using Microsoft.CorrelationVector;
using Slim;
using System;
using System.Extensions;
using System.Windows;
@@ -53,9 +55,11 @@ namespace Daybreak.Services.ViewManagement
private void ShowViewInner(Type viewType, object dataContext)
{
var scopedManager = this.serviceManager.CreateScope();
scopedManager.As<IServiceManager>().RegisterScoped<ScopeMetadata, ScopeMetadata>((sp) => new ScopeMetadata(new CorrelationVector()));
Application.Current.Dispatcher.Invoke(() =>
{
var view = this.serviceManager.GetService(viewType).As<UserControl>();
var view = scopedManager.GetService(viewType).As<UserControl>();
this.container.Children.Clear();
this.container.Children.Add(view);
view.DataContext = dataContext;
-35
View File
@@ -1,35 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Daybreak.Utils
{
public sealed class LoggingHttpHandler : DelegatingHandler
{
private readonly ILogger logger;
public LoggingHttpHandler(ILogger logger, HttpMessageHandler innerHandler) : base(innerHandler)
{
this.logger = logger.ThrowIfNull(nameof(logger));
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
this.logger.LogInformation($"{request.Method} - {request.RequestUri}");
try
{
var response = await base.SendAsync(request, cancellationToken);
this.logger.LogInformation($"{request.RequestUri} - {response.StatusCode}");
return response;
}
catch(Exception e)
{
this.logger.LogError($"Exception encountered while processing {request.Method} - {request.RequestUri}", e);
throw;
}
}
}
}
+12 -1
View File
@@ -11,6 +11,7 @@
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
@@ -35,7 +36,17 @@
<controls:SaveButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5"
Clicked="SaveButton_Clicked" Grid.Column="2" IsEnabled="{Binding ElementName=_this, Path=SaveButtonEnabled, Mode=OneWay}"></controls:SaveButton>
</Grid>
<controls:BuildTemplate Grid.Row="1" DataContext="{Binding ElementName=_this, Path=CurrentBuild, Mode=OneWay}">
<Grid Grid.Row="1" Margin="10, 0, 10, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Code: " Foreground="White" Background="Transparent" FontSize="16"></TextBlock>
<TextBox Grid.Column="1" Foreground="White" Background="Transparent" FontSize="16"
Text="{Binding ElementName=_this, Path=CurrentBuildCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</Grid>
<controls:BuildTemplate x:Name="BuildTemplate" Grid.Row="2" DataContext="{Binding ElementName=_this, Path=CurrentBuild, Mode=OneWay}"
BuildChanged="BuildTemplate_BuildChanged">
</controls:BuildTemplate>
</Grid>
</UserControl>
+65 -19
View File
@@ -1,12 +1,17 @@
using Daybreak.Models.Builds;
using Daybreak.Controls;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Input;
namespace Daybreak.Views
{
@@ -17,47 +22,89 @@ namespace Daybreak.Views
{
private const string DisallowedChars = "\r\n\\/.";
public readonly static DependencyProperty CurrentBuildProperty =
DependencyPropertyExtensions.Register<BuildTemplateView, BuildEntry>(nameof(CurrentBuild));
public readonly static DependencyProperty SaveButtonEnabledProperty =
DependencyPropertyExtensions.Register<BuildTemplateView, bool>(nameof(SaveButtonEnabled), new PropertyMetadata(false));
private bool supressDecode = false;
private readonly IViewManager viewManager;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly ILogger<BuildTemplateView> logger;
public bool SaveButtonEnabled
{
get => this.GetTypedValue<bool>(SaveButtonEnabledProperty);
set => this.SetValue(SaveButtonEnabledProperty, value);
}
public BuildEntry CurrentBuild
{
get => this.GetTypedValue<BuildEntry>(CurrentBuildProperty);
set => this.SetValue(CurrentBuildProperty, value);
}
[GenerateDependencyProperty(InitialValue = false)]
private bool saveButtonEnabled;
[GenerateDependencyProperty]
private BuildEntry currentBuild;
[GenerateDependencyProperty]
private string currentBuildCode;
public BuildTemplateView(
IViewManager viewManager,
IBuildTemplateManager buildTemplateManager)
IBuildTemplateManager buildTemplateManager,
IIconRetriever iconRetriever,
IConfigurationManager configurationManager,
ILogger<ChromiumBrowserWrapper> chromiumLogger,
ILogger<BuildTemplateView> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.logger = logger.ThrowIfNull(nameof(logger));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
this.BuildTemplate.InitializeTemplate(iconRetriever, configurationManager, buildTemplateManager, chromiumLogger);
this.DataContextChanged += (sender, contextArgs) =>
{
if (contextArgs.NewValue is BuildEntry)
{
this.logger.LogInformation("Received data context. Setting current build");
this.CurrentBuild = contextArgs.NewValue.As<BuildEntry>();
this.CurrentBuildCode = this.buildTemplateManager.EncodeTemplate(this.CurrentBuild.Build);
}
};
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == CurrentBuildCodeProperty && this.supressDecode is false)
{
this.logger.LogInformation($"Attempting to decode provided template {this.CurrentBuildCode}");
try
{
this.CurrentBuild = new BuildEntry
{
Name = this.CurrentBuild.Name,
PreviousName = this.CurrentBuild.PreviousName,
Build = this.buildTemplateManager.DecodeTemplate(this.CurrentBuildCode)
};
this.logger.LogInformation($"Template {CurrentBuildCode} decoded");
}
catch
{
this.logger.LogWarning($"Failed to decode {this.CurrentBuildCode}. Reverting to default build");
this.CurrentBuild = new BuildEntry
{
Name = this.CurrentBuild.Name,
PreviousName = this.CurrentBuild.PreviousName,
Build = new Build()
};
}
}
}
private void BuildTemplate_BuildChanged(object sender, EventArgs e)
{
try
{
this.supressDecode = true;
this.CurrentBuildCode = this.buildTemplateManager.EncodeTemplate(this.CurrentBuild.Build);
}
finally
{
this.supressDecode = false;
}
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<BuildsListView>();
}
private void SaveButton_Clicked(object sender, EventArgs e)
{
this.buildTemplateManager.SaveBuild(this.CurrentBuild);
@@ -71,7 +118,6 @@ namespace Daybreak.Views
e.Handled = true;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(sender.As<TextBox>().Text))
+73
View File
@@ -0,0 +1,73 @@
<UserControl x:Class="Daybreak.Views.LogsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Views"
xmlns:controls="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}" x:Key="WrapText">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</UserControl.Resources>
<Grid Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Left" Margin="5"
Clicked="BackButton_Clicked"></controls:BackButton>
<controls:BinButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="5"
Clicked="BinButton_Clicked"></controls:BinButton>
<controls:RefreshGlyph Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 45, 5"
Clicked="RefreshGlyph_Clicked"></controls:RefreshGlyph>
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 85, 5"
Clicked="ExportButton_Clicked">
<controls:BackButton.RenderTransform>
<RotateTransform Angle="270" CenterX="15" CenterY="15"></RotateTransform>
</controls:BackButton.RenderTransform>
</controls:BackButton>
<DataGrid IsReadOnly="True" Background="Transparent" Foreground="White" Grid.Row="1"
ItemsSource="{Binding ElementName=_this, Path=Logs, Mode=OneWay}" HorizontalScrollBarVisibility="Disabled"
AutoGenerateColumns="False" HeadersVisibility="Column" EnableColumnVirtualization="True"
EnableRowVirtualization="True">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderBrush" Value="#80808080"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderBrush" Value="#80808080"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True" Header="DateTime" Binding="{Binding LogTime}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="CV" Binding="{Binding CorrelationVector}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="Category" Binding="{Binding Category}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="LogLevel" Binding="{Binding LogLevel}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTextColumn IsReadOnly="True" Header="EventId" Binding="{Binding EventId}" ElementStyle="{StaticResource WrapText}" Width="auto"/>
<DataGridTemplateColumn IsReadOnly="True" Header="Message" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<controls:LogMessageTemplate Message="{Binding Message}" Foreground="White"></controls:LogMessageTemplate>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
+79
View File
@@ -0,0 +1,79 @@
using Daybreak.Models;
using Daybreak.Services.Logging;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.Extensions;
using System.IO;
using System.Linq;
using System.Windows.Controls;
namespace Daybreak.Views
{
/// <summary>
/// Interaction logic for LogsView.xaml
/// </summary>
public partial class LogsView : UserControl
{
private readonly IViewManager viewManager;
private readonly ILogsManager logManager;
private readonly ILogger<LogsView> logger;
public ObservableCollection<Log> Logs { get; } = new ObservableCollection<Log>();
public LogsView(
IViewManager viewManager,
ILogsManager logManager,
ILogger<LogsView> logger)
{
this.logManager = logManager.ThrowIfNull(nameof(logManager));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.logger = logger.ThrowIfNull(nameof(logger));
this.InitializeComponent();
this.UpdateLogs();
}
private void UpdateLogs()
{
this.Logs.ClearAnd().AddRange(this.logManager.GetLogs(l => l.LogLevel < Microsoft.Extensions.Logging.LogLevel.Trace));
}
private async void ExportButton_Clicked(object sender, EventArgs e)
{
this.logger.LogInformation("Exporting logs");
var saveFileDialog = new SaveFileDialog
{
DefaultExt = "json",
Filter = "Json files (*.json)|*.json",
Title = "Export logs",
ValidateNames = true,
CreatePrompt = true
};
if (saveFileDialog.ShowDialog() is true)
{
var fileName = saveFileDialog.FileName;
this.logger.LogInformation($"Exporting to {fileName}");
await File.WriteAllTextAsync(fileName, this.logManager.GetLogs().ToList().Serialize());
}
else
{
this.logger.LogInformation("Exporting canceled");
}
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<SettingsCategoryView>();
}
private void BinButton_Clicked(object sender, EventArgs e)
{
this.logManager.DeleteLogs();
this.UpdateLogs();
}
private void RefreshGlyph_Clicked(object sender, EventArgs e)
{
this.UpdateLogs();
}
}
}
+4 -2
View File
@@ -40,7 +40,8 @@
Clicked="LaunchTexmodButton_Clicked" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="1"
Visibility="{Binding ElementName=_this, Path=ButtonsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"></controls:OpaqueButton>
<Grid Grid.Column="2" Margin="10" Visibility="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}">
<controls:ChromiumBrowserWrapper Address="{Binding ElementName=_this, Path=RightBrowserAddress, Mode=OneWay}"
<controls:ChromiumBrowserWrapper x:Name="RightWebBrowser"
Address="{Binding ElementName=_this, Path=RightBrowserAddress, Mode=OneWay}"
Foreground="White" FavoriteUriChanged="RightBrowser_FavoriteUriChanged"
FavoriteAddress="{Binding ElementName=_this, Path=RightBrowserFavoriteAddress, Mode=OneWay}"
MaximizeClicked="RightChromiumBrowserWrapper_MaximizeClicked"
@@ -48,7 +49,8 @@
CanDownloadBuild="True"/>
</Grid>
<Grid Grid.Column="0" Margin="10" Visibility="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}">
<controls:ChromiumBrowserWrapper Address="{Binding ElementName=_this, Path=LeftBrowserAddress, Mode=OneWay}"
<controls:ChromiumBrowserWrapper x:Name="LeftWebBrowser"
Address="{Binding ElementName=_this, Path=LeftBrowserAddress, Mode=OneWay}"
Foreground="White" FavoriteUriChanged="LeftBrowser_FavoriteUriChanged"
FavoriteAddress="{Binding ElementName=_this, Path=LeftBrowserFavoriteAddress, Mode=OneWay}"
MaximizeClicked="LeftChromiumBrowserWrapper_MaximizeClicked"
+18 -2
View File
@@ -1,9 +1,12 @@
using Daybreak.Exceptions;
using Daybreak.Controls;
using Daybreak.Exceptions;
using Daybreak.Models.Builds;
using Daybreak.Services.ApplicationLauncher;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.Screens;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Extensions;
using System.Linq;
@@ -26,6 +29,8 @@ namespace Daybreak.Views
private readonly IViewManager viewManager;
private readonly IConfigurationManager configurationManager;
private readonly IScreenManager screenManager;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly ILogger<ChromiumBrowserWrapper> browserLogger;
private readonly CancellationTokenSource cancellationTokenSource = new();
private bool leftBrowserMaximized = false;
@@ -54,17 +59,28 @@ namespace Daybreak.Views
IApplicationLauncher applicationDetector,
IViewManager viewManager,
IConfigurationManager configurationManager,
IScreenManager screenManager)
IScreenManager screenManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> browserLogger)
{
this.browserLogger = browserLogger;
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
this.applicationDetector = applicationDetector.ThrowIfNull(nameof(applicationDetector));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.InitializeComponent();
this.PeriodicallyCheckGameState();
this.InitializeBrowsers();
this.NavigateToDefaults();
}
private void InitializeBrowsers()
{
this.LeftWebBrowser.InitializeBrowser(this.configurationManager, this.buildTemplateManager, this.browserLogger);
this.RightWebBrowser.InitializeBrowser(this.configurationManager, this.buildTemplateManager, this.browserLogger);
}
private void NavigateToDefaults()
{
var applicationConfiguration = this.configurationManager.GetConfiguration();
+6
View File
@@ -48,6 +48,12 @@
<controls:StaticGlyph></controls:StaticGlyph>
</controls:TileButton.InnerContent>
</controls:TileButton>
<controls:TileButton Title="Logs" Foreground="White" BorderBrush="White" BorderThickness="2"
HighlightColor="White" Clicked="LogsButton_Clicked" Height="150" Width="150">
<controls:TileButton.InnerContent>
<controls:LogsGlyph></controls:LogsGlyph>
</controls:TileButton.InnerContent>
</controls:TileButton>
</WrapPanel>
</WrapPanel>
</Grid>
@@ -43,6 +43,11 @@ namespace Daybreak.Views
this.viewManager.ShowView<VersionManagementView>(this);
}
private void LogsButton_Clicked(object sender, System.EventArgs e)
{
this.viewManager.ShowView<LogsView>();
}
private void FileButton_Clicked(object sender, System.EventArgs e)
{
this.viewManager.ShowView<ExecutablesView>();
+3
View File
@@ -94,6 +94,7 @@
<TextBlock Text="Desired screen id: " FontSize="22" Foreground="White" Height="30"></TextBlock>
<TextBlock Text="Place Shortcut: " FontSize="22" Foreground="White" Height="30"></TextBlock>
<TextBlock Text="Shortcut folder: " FontSize="22" Foreground="White" Height="30"></TextBlock>
<TextBlock Text="Keep local cache of icons: " FontSize="22" Foreground="White" Height="30"></TextBlock>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1">
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
@@ -148,6 +149,8 @@
Foreground="White" BorderBrush="Gray" BorderThickness="0"
Clicked="ShortcutFolderPickerGlyph_Clicked"></controls:FilePickerGlyph>
</Grid>
<ToggleButton HorizontalAlignment="Center" HorizontalContentAlignment="Stretch" Style="{StaticResource AnimatedSwitch}" Height="30" Width="40"
FontSize="22" Foreground="White" IsChecked="{Binding ElementName=_this, Path=KeepLocalIconCache, Mode=TwoWay}"></ToggleButton>
</StackPanel>
</Grid>
</ScrollViewer>
+4
View File
@@ -45,6 +45,8 @@ namespace Daybreak.Views
private bool shortcutPlaced;
[GenerateDependencyProperty]
private bool autoCheckUpdate;
[GenerateDependencyProperty]
private bool keepLocalIconCache;
public SettingsView(
IConfigurationManager configurationManager,
@@ -71,6 +73,7 @@ namespace Daybreak.Views
this.ShortcutFolder = config.ShortcutLocation;
this.ShortcutPlaced = config.PlaceShortcut;
this.AutoCheckUpdate = config.AutoCheckUpdate;
this.KeepLocalIconCache = config.KeepLocalIconCache;
}
private void SaveButton_Clicked(object sender, EventArgs e)
@@ -88,6 +91,7 @@ namespace Daybreak.Views
currentConfig.ShortcutLocation = this.ShortcutFolder;
currentConfig.PlaceShortcut = this.ShortcutPlaced;
currentConfig.AutoCheckUpdate = this.AutoCheckUpdate;
currentConfig.KeepLocalIconCache = this.KeepLocalIconCache;
this.configurationManager.SaveConfiguration(currentConfig);
this.viewManager.ShowView<SettingsCategoryView>();
}