Compare commits

...
15 Commits
Author SHA1 Message Date
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
243c2c1536 Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2 (#91)
* Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2

Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump version

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandru Macocian <amacocian@yahoo.com>
2022-09-15 14:34:21 +00:00
amacocianandGitHub 930aca4ffe Bump dependencies (#90)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2022-09-12 18:11:36 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1639cabe8e Bump Slim from 1.7.3 to 1.8.1 (#86)
Bumps [Slim](https://github.com/AlexMacocian/Slim) from 1.7.3 to 1.8.1.
- [Release notes](https://github.com/AlexMacocian/Slim/releases)
- [Commits](https://github.com/AlexMacocian/Slim/commits)

---
updated-dependencies:
- dependency-name: Slim
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 08:38:17 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5961a57c5b Bump Microsoft.Web.WebView2 from 1.0.1293.44 to 1.0.1343.22 (#85)
Bumps Microsoft.Web.WebView2 from 1.0.1293.44 to 1.0.1343.22.

---
updated-dependencies:
- dependency-name: Microsoft.Web.WebView2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 10:29:18 +02:00
amacocianandGitHub 75d9bc6dec Add checkmark when builds are synchronized (#84) 2022-08-27 12:54:48 +00:00
amacocianandGitHub 4ccca32881 Build synchronization speedups (#82) 2022-08-27 12:15:21 +00:00
amacocianandGitHub a7f5d9dd96 Fix deletion error when installing (#80)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2022-08-25 20:55:01 +00:00
amacocianandGitHub c8ba125abc Allow individual uploads/downloads for backed up builds (#77) 2022-08-25 20:44:20 +00:00
amacocianandGitHub 7d290031f1 Alexmacocian/fix secret build (#76)
* Fix pre-build secret building action

* Update version
2022-08-22 22:10:18 +00:00
amacocianandGitHub 5220a8c7ac Run fixed CD pipeline (#75) 2022-08-22 21:31:58 +00:00
amacocianandGitHub 33e221f60f Fix secret generation for CD pipeline (#74) 2022-08-22 21:05:13 +00:00
amacocianandGitHub 7f77547c50 Add support for build backups (#73) 2022-08-22 20:30:10 +00:00
amacocianandGitHub c5964e33ac Setup support for secrets (#72) 2022-08-22 12:55:29 +00:00
amacocianandGitHub 1c6f3ec797 Make all browsers use BrowserData folder (#70)
* Make all browsers use BrowserData folder

* Increment version
2022-08-21 14:09:26 +00:00
amacocianandGitHub 8601f61739 Run CD pipeline only on code changes (#67) 2022-08-20 15:24:45 +02:00
42 changed files with 1401 additions and 96 deletions
+9 -1
View File
@@ -9,6 +9,9 @@ on:
push:
branches:
- master
paths:
- "Daybreak/**"
- "Daybreak.Installer/**"
jobs:
@@ -24,7 +27,7 @@ jobs:
Configuration: Release
Solution_Path: Daybreak.sln
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
Wpf_Project_Path: Daybreak\Daybreal.csproj
Wpf_Project_Path: Daybreak\Daybreak.csproj
Actions_Allow_Unsecure_Commands: true
steps:
@@ -57,6 +60,11 @@ jobs:
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
- name: Setup project secrets
run: |
dotnet user-secrets --project Daybreak\Daybreak.csproj set AadApplicationId "${{ secrets.AadApplicationId }}"
dotnet user-secrets --project Daybreak\Daybreak.csproj set AadTenantId "${{ secrets.AadTenantId }}"
- name: Restore project
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
env:
+25 -4
View File
@@ -24,11 +24,32 @@ catch
}
Console.WriteLine("Deleting package");
File.Delete(tempFile);
try
{
File.Delete(tempFile);
}
catch(Exception e)
{
Console.WriteLine($"Failed to delete {tempFile}.\n{e}");
}
Console.WriteLine("Deleting browser caches");
Directory.Delete("BrowserData", true);
Directory.Delete("Daybreak.exe.WebView2", true);
try
{
Directory.Delete("BrowserData", true);
}
catch(Exception e)
{
Console.WriteLine($"Failed to delete BrowserData.\n{e}");
}
try
{
Directory.Delete("Daybreak.exe.WebView2", true);
}
catch(Exception e)
{
Console.WriteLine($"Failed to delete Daybreak.exe.WebView2.\n{e}");
}
Console.WriteLine("Launching application");
var process = new Process
@@ -43,4 +64,4 @@ if (process.Start() is false)
{
Console.WriteLine("Failed to launch application");
Console.ReadKey();
}
}
@@ -37,5 +37,7 @@ namespace Daybreak.Configuration
public bool PlaceShortcut { get; set; }
[JsonProperty("AutoCheckUpdate")]
public bool AutoCheckUpdate { get; set; } = true;
[JsonProperty("ProtectedGraphAccessToken")]
public string ProtectedGraphAccessToken { get; set; }
}
}
+57 -31
View File
@@ -26,6 +26,8 @@ using System.Logging;
using Daybreak.Services.Updater.PostUpdate;
using System.Core.Extensions;
using Daybreak.Services.Updater.PostUpdate.Actions;
using Daybreak.Services.Graph;
using Microsoft.Extensions.DependencyInjection;
namespace Daybreak.Configuration
{
@@ -35,50 +37,72 @@ namespace Daybreak.Configuration
{
serviceManager.ThrowIfNull();
serviceManager.RegisterHttpFactory((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;
});
serviceManager.RegisterOptionsManager<ApplicationConfigurationOptionsManager>();
serviceManager.RegisterResolver(new LoggerResolver());
serviceManager
.RegisterHttpClient<ApplicationUpdater>()
.WithMessageHandler(sp =>
{
var logger = sp.GetService<ILogger<ApplicationUpdater>>();
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
})
.Build()
.RegisterHttpClient<BloogumClient>()
.WithMessageHandler(sp =>
{
var logger = sp.GetService<ILogger<BloogumClient>>();
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
})
.Build()
.RegisterHttpClient<GraphClient>()
.WithMessageHandler(sp =>
{
var logger = sp.GetService<ILogger<GraphClient>>();
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
})
.Build();
}
public static void RegisterServices(IServiceProducer serviceProducer)
public static void RegisterServices(IServiceCollection services)
{
serviceProducer.ThrowIfNull();
services.ThrowIfNull();
serviceProducer.RegisterSingleton<ILogsManager, JsonLogsManager>();
serviceProducer.RegisterSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
serviceProducer.RegisterSingleton<ILoggerFactory, LoggerFactory>(sp =>
services.AddSingleton<ILogsManager, JsonLogsManager>();
services.AddSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
services.AddSingleton<ILoggerFactory, LoggerFactory>(sp =>
{
var factory = new LoggerFactory();
factory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
return factory;
});
serviceProducer.RegisterSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
services.AddSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
sp.GetService<ILogsManager>(),
sp.GetService<IDebugLogsWriter>()));
serviceProducer.RegisterScoped((sp) => new ScopeMetadata(new CorrelationVector()));
serviceProducer.RegisterSingleton<ViewManager>(registerAllInterfaces: true);
serviceProducer.RegisterSingleton<PostUpdateActionManager>(registerAllInterfaces: true);
serviceProducer.RegisterSingleton<IConfigurationManager, ConfigurationManager>();
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
serviceProducer.RegisterSingleton<IIconBrowser, IconBrowser>();
serviceProducer.RegisterSingleton<IIconDownloader, IconDownloader>();
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<IIconCache, IconCache>();
serviceProducer.RegisterScoped<IPrivilegeManager, PrivilegeManager>();
serviceProducer.RegisterScoped<IScreenManager, ScreenManager>();
services.AddScoped((sp) => new ScopeMetadata(new CorrelationVector()));
services.AddSingleton<ViewManager>();
services.AddSingleton<IViewManager, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<IViewProducer, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<PostUpdateActionManager>();
services.AddSingleton<IPostUpdateActionManager>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IPostUpdateActionProducer>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IPostUpdateActionProvider>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IConfigurationManager, ConfigurationManager>();
services.AddSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
services.AddSingleton<IMutexHandler, MutexHandler>();
services.AddSingleton<IShortcutManager, ShortcutManager>();
services.AddSingleton<IIconBrowser, IconBrowser>();
services.AddSingleton<IIconDownloader, IconDownloader>();
services.AddScoped<ICredentialManager, CredentialManager>();
services.AddScoped<IApplicationLauncher, ApplicationLauncher>();
services.AddScoped<IScreenshotProvider, ScreenshotProvider>();
services.AddScoped<IBloogumClient, BloogumClient>();
services.AddScoped<IApplicationUpdater, ApplicationUpdater>();
services.AddScoped<IBuildTemplateManager, BuildTemplateManager>();
services.AddScoped<IIconCache, IconCache>();
services.AddScoped<IPrivilegeManager, PrivilegeManager>();
services.AddScoped<IScreenManager, ScreenManager>();
services.AddScoped<IGraphClient, GraphClient>();
}
public static void RegisterViews(IViewProducer viewProducer)
@@ -100,6 +124,8 @@ namespace Daybreak.Configuration
viewProducer.RegisterView<VersionManagementView>();
viewProducer.RegisterView<LogsView>();
viewProducer.RegisterView<IconDownloadView>();
viewProducer.RegisterView<GraphAuthorizationView>();
viewProducer.RegisterView<BuildsSynchronizationView>();
}
public static void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer)
+12
View File
@@ -0,0 +1,12 @@
namespace Daybreak.Configuration;
public class SecretKeys
{
public static readonly SecretKeys AadApplicationId = new() { Key = "AadApplicationId" };
public static readonly SecretKeys AadTenantId = new() { Key = "AadTenantId" };
public string Key { get; private set; }
private SecretKeys()
{
}
}
+49
View File
@@ -0,0 +1,49 @@
using Newtonsoft.Json.Linq;
using System.Extensions;
using System.Linq;
using System.Reflection;
namespace Daybreak.Configuration;
public static class SecretManager
{
private static JObject SecretsHolder;
public static string GetSecret(SecretKeys secretKey)
{
if (SecretsHolder is null)
{
LoadSecrets();
}
return SecretsHolder.Value<string>(secretKey.Key);
}
public static T GetSecret<T>(SecretKeys secretKey)
{
if (SecretsHolder is null)
{
LoadSecrets();
}
return SecretsHolder.Value<T>(secretKey.Key);
}
private static void LoadSecrets()
{
var serializedSecrets = Assembly.GetExecutingAssembly().GetManifestResourceStream("Daybreak.secrets.json").ReadAllBytes().GetString();
serializedSecrets = TrimUnwantedCharacters(serializedSecrets);
SecretsHolder = JObject.Parse(serializedSecrets);
}
private static string TrimUnwantedCharacters(string s)
{
return new string(s.Where(c =>
char.IsWhiteSpace(c) ||
char.IsLetterOrDigit(c) ||
char.IsSymbol(c) ||
char.IsSeparator(c) ||
char.IsPunctuation(c))
.ToArray());
}
}
@@ -0,0 +1,36 @@
<UserControl x:Class="Daybreak.Controls.SynchronizeButton"
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>
<Grid>
<Ellipse x:Name="BackgroundEllipse" Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Visibility="Visible" Opacity="0.1" Width="35" Height="35" />
<Ellipse Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
StrokeThickness="3"/>
<Grid VerticalAlignment="Center"
HorizontalAlignment="Center">
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
StrokeThickness="1.5"
Data="m19,15.6073c1.4937,-0.5852 3,-1.9184 3,-4.6073c0,-4 -3.3333,-5 -5,-5c0,-2 0,-6 -6,-6c-6,0 -6,4 -6,6c-1.66667,0 -5,1 -5,5c0,2.6889 1.50628,4.0221 3,4.6073" />
<Path StrokeThickness="1.5"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m6,17l1.76777,1.7678c1.56214,1.5621 4.09474,1.5621 5.65684,0l0.3536,-0.3536" />
<Path StrokeThickness="1.5"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m6.35355,19.4749l-0.35355,-2.4749l2.47482,0.3536l-2.12127,2.1213z" />
<Path StrokeThickness="1.5"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m14.77821,14.93927l-1.7678,-1.7677c-1.5621,-1.5621 -4.0948,-1.5621 -5.65685,0l-0.35356,0.3535" />
<Path StrokeThickness="1.5"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m14.1213,12l0.3536,2.4749l-2.4749,-0.3536l2.1213,-2.1213z" />
</Grid>
<Ellipse Fill="Transparent" MouseEnter="Ellipse_MouseEnter" MouseLeave="Ellipse_MouseLeave" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"></Ellipse>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,36 @@
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for SynchronizeButton.xaml
/// </summary>
public partial class SynchronizeButton : UserControl
{
public event EventHandler Clicked;
public SynchronizeButton()
{
this.InitializeComponent();
}
private void Ellipse_MouseEnter(object sender, MouseEventArgs e)
{
this.BackgroundEllipse.Opacity = 0.6;
}
private void Ellipse_MouseLeave(object sender, MouseEventArgs e)
{
this.BackgroundEllipse.Opacity = 0;
}
private void Ellipse_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
this.Clicked?.Invoke(this, e);
}
}
}
@@ -3,6 +3,7 @@ using Daybreak.Models.Browser;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using System;
@@ -81,7 +82,16 @@ namespace Daybreak.Controls
}
}
public async Task InitializeBrowser(
public async Task InitializeDefaultBrowser()
{
var options = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<ApplicationConfiguration>>();
var buildTemplateManager = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBuildTemplateManager>();
var logger = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILogger<ChromiumBrowserWrapper>>();
await this.InitializeDefaultBrowser(options, buildTemplateManager, logger);
}
public async Task InitializeDefaultBrowser(
ILiveOptions<ApplicationConfiguration> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
+1 -14
View File
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace Daybreak.Controls
{
+22
View File
@@ -0,0 +1,22 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.BadGlyph"
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.Glyphs"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
<Ellipse Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
StrokeThickness="1"
Width="32"
Height="32"/>
<Line X1="8" Y1="8" X2="24" Y2="24"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"/>
<Line X1="24" Y1="8" X2="8" Y2="24"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"/>
</Grid>
</Viewbox>
</UserControl>
+14
View File
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for BadGlyph.xaml
/// </summary>
public partial class BadGlyph : UserControl
{
public BadGlyph()
{
this.InitializeComponent();
}
}
+20
View File
@@ -0,0 +1,20 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.GoodGlyph"
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.Glyphs"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
<Ellipse Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
StrokeThickness="1"
Width="32"
Height="32"/>
<Polyline Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Points="26,8 13.625,22.714284896850586 8,16.02597427368164 "></Polyline>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for GoodGlyph.xaml
/// </summary>
public partial class GoodGlyph : UserControl
{
public GoodGlyph()
{
this.InitializeComponent();
}
}
@@ -79,7 +79,7 @@ namespace Daybreak.Controls
ILogger<ChromiumBrowserWrapper> logger)
{
this.iconBrowser = iconBrowser.ThrowIfNull();
await this.SkillBrowser.InitializeBrowser(liveOptions, buildTemplateManager, logger);
await this.SkillBrowser.InitializeDefaultBrowser(liveOptions, buildTemplateManager, logger);
this.SkillTemplate0.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate1.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate2.InitializeSkillTemplate(iconRetriever);
+29 -6
View File
@@ -10,35 +10,54 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.5.2</Version>
<Version>0.9.6.8</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<Target Name="AddUserSecrets" BeforeTargets="PrepareForBuild" Condition=" '$(UserSecretsId)' != '' ">
<PropertyGroup>
<UserSecretsFilePath Condition=" '$(OS)' == 'Windows_NT' ">
$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))\AppData\Roaming\Microsoft\UserSecrets\$(UserSecretsId)\secrets.json
</UserSecretsFilePath>
<UserSecretsFilePath Condition=" '$(OS)' == 'Unix' ">
$([System.Environment]::GetFolderPath(SpecialFolder.UserProfile))/.microsoft/usersecrets/$(UserSecretsId)/secrets.json
</UserSecretsFilePath>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="$(UserSecretsFilePath)" Condition="Exists($(UserSecretsFilePath))" />
</ItemGroup>
</Target>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.12" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1293.44" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1343.22" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NReco.Logging.File" Version="1.1.5" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
<PackageReference Include="Slim" Version="1.7.3" />
<PackageReference Include="Slim" Version="1.9.2" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="SystemExtensions.NetCore" Version="1.0.1" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.5.0" />
<PackageReference Include="WCL" Version="1.0.2" />
<PackageReference Include="WpfExtended" Version="0.6.2" />
<PackageReference Include="WpfExtended" Version="0.7.1" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
<PackageReference Include="WpfScreenHelper" Version="2.0.0" />
<PackageReference Include="WpfScreenHelper" Version="2.1.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Controls\Buttons\SynchronizeButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\Buttons\HelpButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
@@ -57,6 +76,10 @@
</ItemGroup>
<ItemGroup>
<Page Update="Controls\Buttons\SynchronizeButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Buttons\HelpButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
+23 -11
View File
@@ -1,10 +1,17 @@
using Daybreak.Configuration;
using Daybreak.Exceptions;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.Privilege;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Updater;
using Daybreak.Services.Updater.PostUpdate;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Slim;
using Slim.Integration.ServiceCollection;
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -16,8 +23,11 @@ namespace Daybreak.Launch
{
public sealed class Launcher : ExtendedApplication<MainWindow>
{
public readonly static Launcher Instance = new();
private ILogger logger;
private readonly static Launcher launcher = new();
public System.IServiceProvider ApplicationServiceProvider => this.ServiceProvider;
[STAThread]
public static int Main()
@@ -25,16 +35,15 @@ namespace Daybreak.Launch
return LaunchMainWindow();
}
protected override void SetupServiceManager(IServiceManager serviceManager)
protected override System.IServiceProvider SetupServiceProvider(IServiceCollection services)
{
var serviceManager = new ServiceManager();
ProjectConfiguration.RegisterResolvers(serviceManager);
return services.BuildSlimServiceProvider(serviceManager);
}
protected override void RegisterServices(IServiceProducer serviceProducer)
protected override void RegisterServices(IServiceCollection services)
{
ProjectConfiguration.RegisterServices(this.ServiceManager);
this.ServiceManager.BuildSingletons();
ProjectConfiguration.RegisterViews(this.ServiceManager.GetService<IViewManager>());
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceManager.GetService<IPostUpdateActionProducer>());
ProjectConfiguration.RegisterServices(services);
}
protected override bool HandleException(Exception e)
{
@@ -91,7 +100,10 @@ namespace Daybreak.Launch
}
protected override void ApplicationStarting()
{
this.logger = this.ServiceManager.GetService<ILogger<Launcher>>();
ProjectConfiguration.RegisterViews(this.ServiceProvider.GetService<IViewManager>());
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceProvider.GetService<IPostUpdateActionProducer>());
this.logger = this.ServiceProvider.GetRequiredService<ILogger<Launcher>>();
this.RegisterViewContainer();
}
protected override void ApplicationClosing()
@@ -100,13 +112,13 @@ namespace Daybreak.Launch
private void RegisterViewContainer()
{
var viewManager = this.ServiceManager.GetService<IViewManager>();
var mainWindow = this.ServiceManager.GetService<MainWindow>();
var viewManager = this.ServiceProvider.GetRequiredService<IViewManager>();
var mainWindow = this.ServiceProvider.GetRequiredService<MainWindow>();
viewManager.RegisterContainer(mainWindow.Container);
}
private static int LaunchMainWindow()
{
return launcher.Run();
return Instance.Run();
}
}
}
+6 -3
View File
@@ -103,10 +103,13 @@
<Grid x:Name="Container" Grid.Row="1">
</Grid>
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Active="True"></wcl:Border>
<webview:WebView2 x:Name="BackgroundWebView"
<controls:ChromiumBrowserWrapper x:Name="BackgroundWebView"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Visibility="Collapsed"
Grid.Row="1"></webview:WebView2>
Visibility="Visible"
ControlsEnabled="False"
CanNavigate="False"
CanDownloadBuild="False"
Grid.Row="1"></controls:ChromiumBrowserWrapper>
</Grid>
</Window>
+9
View File
@@ -0,0 +1,9 @@
using Daybreak.Models.Builds;
namespace Daybreak.Models;
public sealed class BuildWithTemplateCode
{
public BuildEntry Build { get; set; }
public string TemplateCode { get; set; }
}
@@ -80,6 +80,30 @@ namespace Daybreak.Services.BuildTemplates
}
}
public async Task<Result<BuildEntry, Exception>> GetBuild(string name)
{
if (File.Exists($"{BuildsPath}/{name}.txt") is false)
{
return new InvalidOperationException("Unable to find build file");
}
var content = await File.ReadAllTextAsync($"{BuildsPath}/{name}.txt");
if (this.TryDecodeTemplate(content, out var build) is false)
{
return new InvalidOperationException("Unable to parse build file");
}
return new BuildEntry { Build = build, Name = name, PreviousName = name };
}
public void ClearBuilds()
{
foreach(var file in Directory.GetFiles(BuildsPath))
{
File.Delete(file);
}
}
public async IAsyncEnumerable<BuildEntry> GetBuilds()
{
foreach (var file in Directory.GetFiles(BuildsPath))
@@ -252,7 +276,7 @@ namespace Daybreak.Services.BuildTemplates
buildMetadata.NewTemplate = false;
}
buildMetadata.ProfessionIdLength = stream.Read(2) * 2 + 4;
buildMetadata.ProfessionIdLength = (stream.Read(2) * 2) + 4;
buildMetadata.PrimaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
buildMetadata.SecondaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
buildMetadata.AttributeCount = stream.Read(4);
@@ -289,7 +313,7 @@ namespace Daybreak.Services.BuildTemplates
var desiredProfessionIdLength = GetBitLength(new List<int> { buildMetadata.PrimaryProfessionId, buildMetadata.SecondaryProfessionId }.Max());
var professionIdLength = Math.Max((desiredProfessionIdLength - 4) / 2, 0);
var finalProfessionIdLength = professionIdLength * 2 + 4;
var finalProfessionIdLength = (professionIdLength * 2) + 4;
stream.Write(professionIdLength, 2);
stream.Write(buildMetadata.PrimaryProfessionId, finalProfessionIdLength);
stream.Write(buildMetadata.SecondaryProfessionId, finalProfessionIdLength);
@@ -1,5 +1,8 @@
using Daybreak.Models.Builds;
using System;
using System.Collections.Generic;
using System.Extensions;
using System.Threading.Tasks;
namespace Daybreak.Services.BuildTemplates
{
@@ -8,9 +11,11 @@ namespace Daybreak.Services.BuildTemplates
bool IsTemplate(string template);
BuildEntry CreateBuild();
BuildEntry CreateBuild(string name);
void ClearBuilds();
void SaveBuild(BuildEntry buildEntry);
void RemoveBuild(BuildEntry buildEntry);
IAsyncEnumerable<BuildEntry> GetBuilds();
Task<Result<BuildEntry, Exception>> GetBuild(string name);
Build DecodeTemplate(string template);
bool TryDecodeTemplate(string template, out Build build);
string EncodeTemplate(Build build);
+429
View File
@@ -0,0 +1,429 @@
using Daybreak.Configuration;
using Daybreak.Controls;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Graph.Models;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Controls;
namespace Daybreak.Services.Graph;
public sealed class GraphClient : IGraphClient
{
private const string Scopes = "Files.Read Files.Read.All Files.ReadWrite Files.ReadWrite.All User.Read";
private const string RedirectUri = "http://localhost";
private const string QueryStateKey = "state";
private const string QueryCodeKey = "code";
private const string ClientIdPlaceholder = "[ClientID]";
private const string RedirectUriPlaceholder = "[RedirectUri]";
private const string ScopesPlaceholder = "[Scopes]";
private const string StatePlaceholder = "[State]";
private const string ProfileEndpoint = "me";
private const string GraphBaseUrl = "https://graph.microsoft.com/v1.0/";
private const string TokenUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
private const string AuthorizationUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id={ClientIdPlaceholder}&response_type=code&redirect_uri={RedirectUriPlaceholder}&response_mode=query&scope={ScopesPlaceholder}&state={StatePlaceholder}";
private const string SyncFileUri = $"me/drive/root:/Daybreak/Builds/daybreak.json{ContentSuffix}";
private const string ContentSuffix = ":/content";
private static readonly byte[] Entropy = Convert.FromBase64String("R3VpbGR3YXJz");
private static readonly string ApplicationId = SecretManager.GetSecret(SecretKeys.AadApplicationId);
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IViewManager viewManager;
private readonly ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions;
private readonly IHttpClient<GraphClient> httpClient;
private readonly ILogger<GraphClient> logger;
private List<BuildFile> buildsCache;
public GraphClient(
IBuildTemplateManager buildTemplateManager,
IViewManager viewManager,
ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions,
IHttpClient<GraphClient> httpClient,
ILogger<GraphClient> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull();
this.httpClient = httpClient.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.httpClient.BaseAddress = new Uri(GraphBaseUrl);
this.httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<Result<bool, Exception>> PerformAuthorizationFlow(
ChromiumBrowserWrapper chromiumBrowserWrapper,
CancellationToken cancellationToken = default)
{
var maybeAuthCode = await RetrieveAuthorizationCode(chromiumBrowserWrapper, cancellationToken);
if (maybeAuthCode.TryExtractSuccess(out var authCode) is false)
{
return maybeAuthCode.SwitchAny(onFailure: exception => exception);
}
var maybeAccessToken = await this.RetrieveAccessToken(authCode);
return maybeAccessToken.Switch(
onSuccess: token =>
{
var accessToken = AccessToken.FromTokenResponse(token);
this.SaveAccessToken(accessToken);
return true;
},
onFailure: exception => exception);
}
public async Task<Result<User, Exception>> GetUserProfile<TViewType>()
where TViewType : UserControl
{
var authCode = this.LoadAccessToken();
if (authCode.ExtractValue() is not AccessToken accessToken)
{
this.viewManager.ShowView<GraphAuthorizationView>(new ViewRedirectContext { CallingView = typeof(TViewType) });
return new InvalidOperationException("Client is not authorized");
}
if (DateTime.Now > accessToken.ExpirationDate)
{
this.viewManager.ShowView<GraphAuthorizationView>(new ViewRedirectContext { CallingView = typeof(TViewType) });
return new InvalidOperationException("Client authorization expired");
}
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token);
var response = await this.httpClient.GetAsync(ProfileEndpoint);
if (response.IsSuccessStatusCode is false)
{
this.viewManager.ShowView<GraphAuthorizationView>(new ViewRedirectContext { CallingView = typeof(TViewType) });
return new InvalidOperationException($"Failed to load profile. Response status code [{response.StatusCode}]");
}
try
{
var profile = JsonConvert.DeserializeObject<User>(await response.Content.ReadAsStringAsync());
return profile;
}
catch(Exception e)
{
return e;
}
}
public async Task<Result<bool, Exception>> UploadBuilds()
{
var builds = await this.buildTemplateManager.GetBuilds().ToListAsync();
return await this.PutBuilds(builds);
}
public async Task<Result<bool, Exception>> DownloadBuilds()
{
var retrieveBuildsResponse = await this.RetrieveBuildsList();
if (retrieveBuildsResponse.TryExtractFailure(out var failure))
{
this.logger.LogError(failure, "Unable to download builds");
return false;
}
if (retrieveBuildsResponse.TryExtractSuccess(out var builds) is false)
{
this.logger.LogError("Unexpected error occured");
return false;
}
this.buildTemplateManager.ClearBuilds();
var compiledBuilds = this.buildsCache.Select(buildFile =>
{
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode, out var build) is false)
{
return null;
}
return new BuildEntry
{
Build = build,
Name = buildFile.FileName,
PreviousName = buildFile.FileName
};
}).Where(entry => entry is not null).ToList();
_ = compiledBuilds.Do(this.buildTemplateManager.SaveBuild).ToList();
return true;
}
public async Task<Result<bool, Exception>> DownloadBuild(string buildName)
{
var retrieveBuildsResponse = await this.RetrieveBuildsList();
if (retrieveBuildsResponse.TryExtractFailure(out var failure))
{
this.logger.LogError(failure, "Unable to download builds");
return false;
}
if (retrieveBuildsResponse.TryExtractSuccess(out var builds) is false)
{
this.logger.LogError("Unexpected error occured");
return false;
}
var compiledBuilds = this.buildsCache
.Where(b => b.FileName == buildName)
.Select(buildFile =>
{
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode, out var build) is false)
{
return null;
}
return new BuildEntry
{
Build = build,
Name = buildFile.FileName,
PreviousName = buildFile.FileName
};
}).Where(entry => entry is not null).ToList();
_ = compiledBuilds.Do(this.buildTemplateManager.SaveBuild).ToList();
return true;
}
public async Task<Result<bool, Exception>> UploadBuild(string buildName)
{
var getBuildResult = await this.buildTemplateManager.GetBuild(buildName);
if (getBuildResult.TryExtractSuccess(out var buildEntry) is false)
{
return getBuildResult.SwitchAny(
onFailure: exception => exception);
}
return await this.PutBuild(buildEntry);
}
public async Task<Result<IEnumerable<BuildFile>, Exception>> RetrieveBuildsList()
{
var maybeBuildsBackup = await this.GetBuildsBackup();
var buildsBackup = maybeBuildsBackup.ExtractValue();
this.buildsCache = buildsBackup;
return buildsBackup;
}
public void ResetAuthorization()
{
this.ResetAccessToken();
}
private async Task<bool> PutBuild(BuildEntry buildEntry)
{
if (this.buildsCache is null)
{
_ = await this.RetrieveBuildsList();
}
var buildFile = new BuildFile
{
FileName = buildEntry.Name,
TemplateCode = this.buildTemplateManager.EncodeTemplate(buildEntry.Build)
};
var buildList = this.buildsCache ?? new List<BuildFile>();
// Remove the previous version of the build
buildList = buildList.Where(b => b.FileName != buildEntry.Name).ToList();
// Add new version of the build
buildList.Add(buildFile);
// Order by name
buildList = buildList.OrderBy(b => b.FileName).ToList();
using var stringContent = new StringContent(JsonConvert.SerializeObject(buildList));
var response = await this.httpClient.PutAsync(SyncFileUri, stringContent);
if (response.IsSuccessStatusCode is false)
{
return false;
}
this.buildsCache = buildList;
return true;
}
private async Task<bool> PutBuilds(List<BuildEntry> buildEntries)
{
var buildFiles = buildEntries.Select(buildEntry => new BuildFile
{
FileName = buildEntry.Name,
TemplateCode = this.buildTemplateManager.EncodeTemplate(buildEntry.Build)
});
var buildList = new List<BuildFile>();
buildList.AddRange(buildFiles);
buildList = buildList.OrderBy(b => b.FileName).ToList();
using var stringContent = new StringContent(JsonConvert.SerializeObject(buildList));
var response = await this.httpClient.PutAsync(SyncFileUri, stringContent);
if (response.IsSuccessStatusCode is false)
{
return false;
}
this.buildsCache = buildList;
return true;
}
private async Task<Optional<List<BuildFile>>> GetBuildsBackup()
{
var fileItemResponse = await this.httpClient.GetAsync(SyncFileUri);
if (fileItemResponse.IsSuccessStatusCode is false)
{
return Optional.None<List<BuildFile>>();
}
var driveItemContent = await fileItemResponse.Content.ReadAsStringAsync();
var backup = JsonConvert.DeserializeObject<List<BuildFile>>(driveItemContent);
if (backup is null)
{
return Optional.None<List<BuildFile>>();
}
return backup;
}
private static async Task<Result<string, Exception>> RetrieveAuthorizationCode(
ChromiumBrowserWrapper chromiumBrowserWrapper,
CancellationToken cancellationToken = default)
{
chromiumBrowserWrapper.ThrowIfNull();
await chromiumBrowserWrapper.InitializeDefaultBrowser();
var state = GetNewState();
chromiumBrowserWrapper.Address = AuthorizationUrlPlaceholder
.Replace(ClientIdPlaceholder, ApplicationId)
.Replace(RedirectUriPlaceholder, RedirectUri
.Replace(":", "%3a")
.Replace("/", "%2f"))
.Replace(ScopesPlaceholder, Scopes
.Replace(' ', '+'))
.Replace(StatePlaceholder, state);
while (chromiumBrowserWrapper.Address.StartsWith(RedirectUri) is false)
{
if (cancellationToken.IsCancellationRequested)
{
return new TaskCanceledException();
}
await Task.Delay(1000, cancellationToken).ConfigureAwait(true);
}
var query = HttpUtility.ParseQueryString(chromiumBrowserWrapper.Address.Split('?').Skip(1).FirstOrDefault());
if (query.GetValues(QueryStateKey) is string[] states is false)
{
throw new InvalidOperationException("Response doesn't have state key in response");
}
if (states.Length < 1 || states.Length > 1)
{
throw new InvalidOperationException("Response contains invalid state");
}
if (states.First() != state)
{
throw new InvalidOperationException("Response contains incorrect state");
}
if (query.GetValues(QueryCodeKey) is string[] codes is false)
{
throw new InvalidOperationException("Response doesn't have code key in response");
}
if (codes.Length < 1 || codes.Length > 1)
{
throw new InvalidOperationException("Response contains invalid code");
}
var authCode = codes.First();
return authCode;
}
private async Task<Result<TokenResponse, Exception>> RetrieveAccessToken(string authorizationCode)
{
using var formContent = new FormUrlEncodedContent(
new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "code", authorizationCode },
{ "redirect_uri", RedirectUri },
{ "client_id", ApplicationId },
{ "scope", Scopes }
});
using var httpRequest = new HttpRequestMessage
{
Content = formContent,
Method = HttpMethod.Post,
RequestUri = new Uri(TokenUrlPlaceholder)
};
using var response = await this.httpClient.SendAsync(httpRequest);
if (response.IsSuccessStatusCode is false)
{
return new InvalidOperationException($"Invalid access token response. Status code [{response.StatusCode}]");
}
return JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync());
}
private void ResetAccessToken()
{
this.liveUpdateableOptions.Value.ProtectedGraphAccessToken = null;
this.liveUpdateableOptions.UpdateOption();
}
private void SaveAccessToken(AccessToken token)
{
var codeBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(token));
this.liveUpdateableOptions.Value.ProtectedGraphAccessToken = Convert.ToBase64String(ProtectedData.Protect(codeBytes, Entropy, DataProtectionScope.CurrentUser));
this.liveUpdateableOptions.UpdateOption();
}
private Optional<AccessToken> LoadAccessToken()
{
var protectedCode = this.liveUpdateableOptions.Value.ProtectedGraphAccessToken;
if (protectedCode.IsNullOrWhiteSpace())
{
return Optional.None<AccessToken>();
}
var codeBytes = ProtectedData.Unprotect(Convert.FromBase64String(protectedCode), Entropy, DataProtectionScope.CurrentUser);
try
{
return JsonConvert.DeserializeObject<AccessToken>(Encoding.UTF8.GetString(codeBytes));
}
catch(Exception e)
{
this.logger.LogError(e, "Failed to load access token. Resetting access token");
this.ResetAccessToken();
return Optional.None<AccessToken>();
}
}
private static string GetNewState()
{
return Guid.NewGuid().ToString();
}
}
+23
View File
@@ -0,0 +1,23 @@
using Daybreak.Controls;
using Daybreak.Services.Graph.Models;
using System;
using System.Collections.Generic;
using System.Extensions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Daybreak.Services.Graph;
public interface IGraphClient
{
Task<Result<User, Exception>> GetUserProfile<TViewType>()
where TViewType : UserControl;
Task<Result<bool, Exception>> PerformAuthorizationFlow(ChromiumBrowserWrapper chromiumBrowserWrapper, CancellationToken cancellationToken = default);
Task<Result<bool, Exception>> UploadBuilds();
Task<Result<bool, Exception>> DownloadBuilds();
Task<Result<bool, Exception>> UploadBuild(string buildName);
Task<Result<bool, Exception>> DownloadBuild(string buildName);
Task<Result<IEnumerable<BuildFile>, Exception>> RetrieveBuildsList();
void ResetAuthorization();
}
@@ -0,0 +1,18 @@
using System;
namespace Daybreak.Services.Graph.Models;
public sealed class AccessToken
{
public string Token { get; set; }
public DateTime ExpirationDate { get; set; }
public static AccessToken FromTokenResponse(TokenResponse tokenResponse)
{
return new AccessToken
{
Token = tokenResponse.AccessToken,
ExpirationDate = DateTime.Now + TimeSpan.FromSeconds(tokenResponse.ExpiresIn)
};
}
}
@@ -0,0 +1,7 @@
namespace Daybreak.Services.Graph.Models;
public sealed class BuildFile
{
public string TemplateCode { get; set; }
public string FileName { get; set; }
}
@@ -0,0 +1,6 @@
namespace Daybreak.Services.Graph.Models;
public sealed class BuildFilesPayload
{
}
@@ -0,0 +1,14 @@
using Newtonsoft.Json;
using System;
namespace Daybreak.Services.Graph.Models;
public sealed class FileItem
{
[JsonProperty("@microsoft.graph.downloadUrl")]
public string DownloadUrl { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("lastModifiedDateTime")]
public DateTime LastModifiedDateTime { get; set; }
}
@@ -0,0 +1,10 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Daybreak.Services.Graph.Models;
public sealed class FolderItem
{
[JsonProperty("value")]
public List<FileItem> Files { get; set; }
}
@@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace Daybreak.Services.Graph.Models;
public sealed class TokenResponse
{
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
+12
View File
@@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Daybreak.Services.Graph.Models;
public sealed class User
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
[JsonProperty("mail")]
public string Email { get; set; }
}
@@ -0,0 +1,8 @@
using System;
namespace Daybreak.Services.Graph.Models;
public sealed class ViewRedirectContext
{
public Type CallingView { get; set; }
}
@@ -1,13 +1,12 @@
using Daybreak.Controls;
using Daybreak.Models;
using Microsoft.Web.WebView2.Wpf;
using System.Threading;
namespace Daybreak.Services.IconRetrieve
{
public interface IIconBrowser
{
void InitializeWebView(WebView2 webView2, CancellationToken cancellationToken);
void InitializeWebView(ChromiumBrowserWrapper webView2, CancellationToken cancellationToken);
/// <summary>
/// Queue an icon request. The browser will attempt to download the icon. Monitor the <see cref="IconRequest.Finished"/> to be notified when the request has been served.
/// </summary>
@@ -1,13 +1,12 @@
using Daybreak.Controls;
using Daybreak.Models.Progress;
using Microsoft.Web.WebView2.Wpf;
using System.Threading.Tasks;
namespace Daybreak.Services.IconRetrieve
{
public interface IIconDownloader
{
void SetBrowser(WebView2 chromiumBrowserWrapper);
void SetBrowser(ChromiumBrowserWrapper chromiumBrowserWrapper);
bool DownloadComplete { get; }
Task<IconDownloadStatus> StartIconDownload();
@@ -1,4 +1,5 @@
using Daybreak.Controls;
using Daybreak.Configuration;
using Daybreak.Controls;
using Daybreak.Models;
using Daybreak.Models.Builds;
using Daybreak.Utils;
@@ -8,6 +9,7 @@ using Microsoft.Web.WebView2.Wpf;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.IO;
@@ -31,7 +33,8 @@ namespace Daybreak.Services.IconRetrieve
private readonly ConcurrentQueue<IconRequest> iconRequests = new();
private readonly ILogger<IconBrowser> logger;
private WebView2 browserWrapper;
private ChromiumBrowserWrapper browserWrapper;
private CancellationToken cancellationToken;
public IconBrowser(
@@ -40,7 +43,7 @@ namespace Daybreak.Services.IconRetrieve
this.logger = logger.ThrowIfNull();
}
public void InitializeWebView(WebView2 webView2, CancellationToken cancellationToken)
public void InitializeWebView(ChromiumBrowserWrapper webView2, CancellationToken cancellationToken)
{
this.browserWrapper = webView2.ThrowIfNull();
this.cancellationToken = cancellationToken;
@@ -91,7 +94,7 @@ namespace Daybreak.Services.IconRetrieve
try
{
await this.browserWrapper.EnsureCoreWebView2Async();
await this.browserWrapper.InitializeDefaultBrowser();
}
catch(Exception e)
{
@@ -104,7 +107,7 @@ namespace Daybreak.Services.IconRetrieve
var skillIconUrl = $"{BaseUrl}/{QueryUrl.Replace(NamePlaceholder, curedSkillName)}";
logger.LogInformation($"Looking for icon at {skillIconUrl}");
this.browserWrapper.CoreWebView2.Navigate(skillIconUrl);
this.browserWrapper.Address = skillIconUrl;
for (var i = 0; i < 5; i++)
{
@@ -116,7 +119,7 @@ namespace Daybreak.Services.IconRetrieve
logger.LogInformation("Executing extraction script");
var responseTask = await Application.Current.Dispatcher.InvokeAsync(async () =>
{
return await this.browserWrapper.ExecuteScriptAsync(Scripts.GetHrefFromSkillPage);
return await this.browserWrapper.WebBrowser.ExecuteScriptAsync(Scripts.GetHrefFromSkillPage);
});
var response = await responseTask;
logger.LogInformation("Parsing response");
@@ -28,7 +28,7 @@ namespace Daybreak.Services.IconRetrieve
private readonly ILogger<IconDownloader> logger;
private readonly ILiveOptions<ApplicationConfiguration> liveOptions;
private readonly ILogger<ChromiumBrowserWrapper> browserLogger;
private WebView2 browserWrapper;
private ChromiumBrowserWrapper browserWrapper;
private CancellationTokenSource cancellationTokenSource;
private IconDownloadStatus iconDownloadStatus;
@@ -52,7 +52,7 @@ namespace Daybreak.Services.IconRetrieve
this.HookIntoConfigurationChanges();
}
public void SetBrowser(WebView2 chromiumBrowserWrapper)
public void SetBrowser(ChromiumBrowserWrapper chromiumBrowserWrapper)
{
if (this.browserWrapper is not null)
{
@@ -179,7 +179,7 @@ namespace Daybreak.Services.IconRetrieve
Application.Current.Dispatcher.Invoke(() =>
{
this.browserWrapper.IsEnabled = false;
this.browserWrapper.Dispose();
this.browserWrapper.WebBrowser.Dispose();
});
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Finished;
this.DownloadComplete = true;
+2
View File
@@ -18,6 +18,8 @@
Clicked="BackButton_Clicked"></controls:BackButton>
<controls:AddButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5"
Clicked="AddButton_Clicked"></controls:AddButton>
<controls:SynchronizeButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5, 5, 45, 5"
Clicked="SynchronizeButton_Clicked"></controls:SynchronizeButton>
<controls:SearchTextBox Grid.Row="1"
FontSize="24"
Foreground="White"
+5
View File
@@ -67,5 +67,10 @@ namespace Daybreak.Views
this.BuildEntries.AddRange(
this.buildEntries.Where(b => StringUtils.MatchesSearchString(b.Name, e)));
}
private void SynchronizeButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<BuildsSynchronizationView>();
}
}
}
@@ -0,0 +1,168 @@
<UserControl x:Class="Daybreak.Views.BuildsSynchronizationView"
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"
xmlns:glyps="clr-namespace:Daybreak.Controls.Glyphs"
xmlns:converters="clr-namespace:Daybreak.Converters"
Loaded="UserControl_Loaded"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" TriggerValue="False"></converters:BooleanToVisibilityConverter>
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True"></converters:BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid
Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
Clicked="BackButton_Clicked" VerticalAlignment="Top"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:BackButton>
<WrapPanel HorizontalAlignment="Center">
<TextBlock
Text="Build templates synchronization"
FontSize="24"
Foreground="White" />
<Grid Margin="5"
VerticalAlignment="Center">
<glyps:GoodGlyph Foreground="LimeGreen"
Width="20"
Height="20"
Visibility="{Binding ElementName=_this, Path=Synchronized, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<glyps:BadGlyph Foreground="OrangeRed"
Width="20"
Height="20"
Visibility="{Binding ElementName=_this, Path=Synchronized, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
</Grid>
</WrapPanel>
<WrapPanel HorizontalAlignment="Center"
Grid.Row="1">
<TextBlock Text="Logged in as: "
FontSize="20"
Foreground="White"></TextBlock>
<TextBlock Text="{Binding ElementName=_this, Path=DisplayName, Mode=OneWay}"
FontSize="20"
Foreground="White"></TextBlock>
</WrapPanel>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Local"
FontSize="14"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<controls:OpaqueButton Grid.Column="0"
Text="Upload all"
Foreground="White"
Background="DarkGray"
HorizontalAlignment="Right"
Padding="5"
Margin="5"
Clicked="UploadAllButton_Clicked"
Width="80"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
<ListView
Grid.Column="0"
Grid.Row="1"
Background="Transparent"
ItemsSource="{Binding ElementName=_this, Path=LocalBuildEntries, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=SelectedLocalBuild, Mode=TwoWay}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding Build.Name}"
HorizontalAlignment="Left"></TextBlock>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding TemplateCode}"
HorizontalAlignment="Right"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Grid.Column="2"
Text="Remote"
FontSize="14"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<controls:OpaqueButton Grid.Column="2"
Text="Download all"
Foreground="White"
Background="DarkGray"
HorizontalAlignment="Left"
Padding="5"
Margin="5"
Clicked="DownloadAllButton_Clicked"
Width="80"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
<ListView
Grid.Column="2"
Grid.Row="1"
Background="Transparent"
ItemsSource="{Binding ElementName=_this, Path=RemoteBuildEntries, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=SelectedRemoteBuild, Mode=TwoWay}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding TemplateCode}"
HorizontalAlignment="Left"></TextBlock>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding FileName}"
HorizontalAlignment="Right"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<controls:OpaqueButton Text="Download Build"
Foreground="White"
Background="DarkGray"
Margin="5"
Padding="5"
Clicked="DownloadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
<controls:OpaqueButton Text="Upload Build"
Foreground="White"
Background="DarkGray"
Margin="5"
Padding="5"
Clicked="UploadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
</StackPanel>
</Grid>
<Grid Grid.RowSpan="3"
Background="#A0202020"
Visibility="{Binding ElementName=_this, Path=ShowLoading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<controls:CircularLoadingWidget MaxWidth="200"
MaxHeight="200"/>
</Grid>
</Grid>
</UserControl>
@@ -0,0 +1,169 @@
using Daybreak.Services.Graph;
using Microsoft.Extensions.Logging;
using System.Core.Extensions;
using System.Windows;
using System.Windows.Extensions;
using System.Windows.Controls;
using Daybreak.Services.ViewManagement;
using System.Collections.ObjectModel;
using Daybreak.Services.Graph.Models;
using System.Extensions;
using System.Threading.Tasks;
using System;
using Daybreak.Services.BuildTemplates;
using System.Linq;
using Daybreak.Models;
using System.Collections.Generic;
namespace Daybreak.Views;
/// <summary>
/// Interaction logic for BuildsSynchronizationView.xaml
/// </summary>
public partial class BuildsSynchronizationView : UserControl
{
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IGraphClient graphClient;
private readonly IViewManager viewManager;
private readonly ILogger<BuildsSynchronizationView> logger;
public ObservableCollection<BuildFile> RemoteBuildEntries { get; } = new();
public ObservableCollection<BuildWithTemplateCode> LocalBuildEntries { get; } = new();
[GenerateDependencyProperty(InitialValue = true)]
private bool buttonsEnabled;
[GenerateDependencyProperty]
private string displayName;
[GenerateDependencyProperty]
private string lastUploadDate;
[GenerateDependencyProperty]
private BuildFile selectedRemoteBuild;
[GenerateDependencyProperty]
private BuildWithTemplateCode selectedLocalBuild;
[GenerateDependencyProperty]
private bool showLoading;
[GenerateDependencyProperty]
private bool synchronized;
public BuildsSynchronizationView(
IBuildTemplateManager buildTemplateManager,
IGraphClient graphClient,
IViewManager viewManager,
ILogger<BuildsSynchronizationView> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.graphClient = graphClient.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.InitializeComponent();
}
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var profile = await this.graphClient.GetUserProfile<BuildsSynchronizationView>();
if (profile.TryExtractSuccess(out var user) is false)
{
this.logger.LogError("Failed to get user info");
return;
}
this.DisplayName = user.DisplayName;
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async Task PopulateBuilds()
{
var getBuildsResponse = await this.graphClient.RetrieveBuildsList();
if (getBuildsResponse.TryExtractSuccess(out var builds) is false)
{
builds = new List<BuildFile>();
}
this.RemoteBuildEntries.ClearAnd().AddRange(builds);
var localBuilds = await this.buildTemplateManager.GetBuilds().ToListAsync();
this.LocalBuildEntries.ClearAnd().AddRange(localBuilds.Select(build => new BuildWithTemplateCode { Build = build, TemplateCode = this.buildTemplateManager.EncodeTemplate(build.Build) }));
if (this.LocalBuildEntries.Count == this.RemoteBuildEntries.Count &&
this.LocalBuildEntries.Select(b => b.Build.Name).Except(this.RemoteBuildEntries.Select(b => b.FileName)).None() &&
this.LocalBuildEntries.Select(b => b.TemplateCode).Except(this.RemoteBuildEntries.Select(b => b.TemplateCode)).None())
{
this.Synchronized = true;
}
else
{
this.Synchronized = false;
}
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<BuildsListView>();
}
private async void UploadButton_Clicked(object sender, EventArgs e)
{
// TODO: Handle failures to upload
if (this.SelectedLocalBuild is not BuildWithTemplateCode buildWithTemplateCode)
{
return;
}
this.ButtonsEnabled = false;
this.ShowLoading = true;
await this.graphClient.UploadBuild(buildWithTemplateCode.Build.Name);
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void DownloadButton_Clicked(object sender, EventArgs e)
{
if (this.SelectedRemoteBuild is not BuildFile buildFile)
{
return;
}
this.ButtonsEnabled = false;
this.ShowLoading = true;
await this.graphClient.DownloadBuild(buildFile.FileName);
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void DownloadAllButton_Clicked(object sender, EventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var result = await this.graphClient.DownloadBuilds().ConfigureAwait(true);
result.DoAny(
onFailure: (failure) =>
{
this.logger.LogError(failure, $"Failed to download builds");
});
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void UploadAllButton_Clicked(object sender, EventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var result = await this.graphClient.UploadBuilds().ConfigureAwait(true);
result.DoAny(
onFailure: (failure) =>
{
this.logger.LogError(failure, $"Failed to upload builds");
});
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
}
@@ -0,0 +1,30 @@
<UserControl x:Class="Daybreak.Views.GraphAuthorizationView"
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"
Loaded="UserControl_Loaded"
Unloaded="UserControl_Unloaded"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid
Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock
Text="Please login to access OneDrive files"
HorizontalAlignment="Center"
FontSize="24"
Foreground="White"/>
<controls:ChromiumBrowserWrapper
x:Name="BrowserWrapper"
Grid.Row="1"
ControlsEnabled="False"
CanNavigate="True"
CanDownloadBuild="False"/>
</Grid>
</UserControl>
@@ -0,0 +1,59 @@
using Daybreak.Services.Graph;
using Daybreak.Services.Graph.Models;
using Daybreak.Services.ViewManagement;
using Microsoft.Extensions.Logging;
using System;
using System.Core.Extensions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace Daybreak.Views;
/// <summary>
/// Interaction logic for GraphAuthorizationView.xaml
/// </summary>
public partial class GraphAuthorizationView : UserControl
{
private readonly CancellationTokenSource cancellationTokenSource = new();
private readonly IGraphClient graphClient;
private readonly IViewManager viewManager;
private readonly ILogger<GraphAuthorizationView> logger;
public GraphAuthorizationView(
IGraphClient graphClient,
IViewManager viewManager,
ILogger<GraphAuthorizationView> logger)
{
this.graphClient = graphClient.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.InitializeComponent();
}
private async void UserControl_Loaded(object _, RoutedEventArgs e)
{
var authorizationResult = await this.graphClient.PerformAuthorizationFlow(this.BrowserWrapper, this.cancellationTokenSource.Token);
if (authorizationResult.TryExtractFailure(out var failure))
{
this.logger.LogError(failure, "Authorization failed");
this.viewManager.ShowView<MainView>();
return;
}
if (this.DataContext is not ViewRedirectContext redirectContext)
{
this.logger.LogError("Cannot redirect to proper view. No view set in context");
this.viewManager.ShowView<MainView>();
return;
}
this.viewManager.ShowView(redirectContext.CallingView);
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
this.cancellationTokenSource.Cancel();
}
}
+3 -9
View File
@@ -30,8 +30,6 @@ namespace Daybreak.Views
private readonly IViewManager viewManager;
private readonly ILiveUpdateableOptions<ApplicationConfiguration> liveOptions;
private readonly IScreenManager screenManager;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly ILogger<ChromiumBrowserWrapper> browserLogger;
private readonly CancellationTokenSource cancellationTokenSource = new();
private bool leftBrowserMaximized = false;
@@ -60,12 +58,8 @@ namespace Daybreak.Views
IApplicationLauncher applicationDetector,
IViewManager viewManager,
ILiveUpdateableOptions<ApplicationConfiguration> liveOptions,
IScreenManager screenManager,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> browserLogger)
IScreenManager screenManager)
{
this.browserLogger = browserLogger;
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.screenManager = screenManager.ThrowIfNull(nameof(screenManager));
this.liveOptions = liveOptions.ThrowIfNull(nameof(liveOptions));
this.applicationDetector = applicationDetector.ThrowIfNull(nameof(applicationDetector));
@@ -77,8 +71,8 @@ namespace Daybreak.Views
private async void InitializeBrowsers()
{
await this.LeftWebBrowser.InitializeBrowser(this.liveOptions, this.buildTemplateManager, this.browserLogger);
await this.RightWebBrowser.InitializeBrowser(this.liveOptions, this.buildTemplateManager, this.browserLogger);
await this.LeftWebBrowser.InitializeDefaultBrowser();
await this.RightWebBrowser.InitializeDefaultBrowser();
this.NavigateToDefaults();
}