Compare commits

...
22 Commits
Author SHA1 Message Date
amacocianandGitHub f54ab42a92 #129 - Change hashing algorithm to SHA512 (#130) 2023-02-02 18:31:07 +00:00
amacocianandGitHub 78c8ae3e8f #127 - Introduce Nullable Support (#128) 2023-02-02 18:05:10 +00:00
amacocianandGitHub c64830c5b6 #123 #113 #105 Build Templates Improvements (#124) 2023-01-21 21:35:16 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
561c7cf7b1 Bump microsoft/setup-msbuild from 1.0.1 to 1.1.3 (#111)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.0.1 to 1.1.3.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/master/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.0.1...v1.1.3)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  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-11-09 15:22:44 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2c6862de41 Bump coverlet.collector from 3.1.2 to 3.2.0 (#112)
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/commits/v3.2.0)

---
updated-dependencies:
- dependency-name: coverlet.collector
  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-11-09 15:10:18 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
69842cbadf Bump Microsoft.NET.Test.Sdk from 17.4.0-preview-20221003-03 to 17.4.0 (#116)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0-preview-20221003-03 to 17.4.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0-preview-20221003-03...v17.4.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  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-11-09 14:24:26 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c7327e7bb2 Bump Microsoft.NET.Test.Sdk (#108)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0-preview-20220726-02 to 17.4.0-preview-20221003-03.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0-preview-20220726-02...v17.4.0-preview-20221003-03)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  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-10-11 10:18:44 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
de33ed7fc4 Bump actions/setup-dotnet from 2 to 3 (#107)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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-10-11 10:09:29 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
c99bb8d074 Bump Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28 (#109)
* Bump Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28

Bumps Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28.

---
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>

* Update daybreak 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-10-11 10:02:33 +00:00
amacocianandGitHub a98e54ebc6 Sync namespaces (#104) 2022-09-23 09:56:47 +00:00
amacocianandGitHub a832aa1925 Minor fixes (#102) 2022-09-21 13:55:37 +00:00
amacocianandGitHub c7eb5debcb Refresh access token (#100) 2022-09-20 11:46:36 +00:00
amacocianandGitHub 91f0cb4e31 Implement expandable menu view (#95) 2022-09-19 19:08:34 +00:00
amacocianandGitHub 5e8407a6ab Make main view maintain its state (#94)
* Make main view maintain its state

* Stop checking game state when mainview is not loaded
2022-09-15 17:05:30 +02:00
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
138 changed files with 1712 additions and 1219 deletions
+2 -2
View File
@@ -53,12 +53,12 @@ jobs:
echo "${{ env.Changelog }}"
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
uses: microsoft/setup-msbuild@v1.1.3
- name: Setup project secrets
run: |
+2 -2
View File
@@ -36,12 +36,12 @@ jobs:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
uses: microsoft/setup-msbuild@v1.1.3
- name: Execute Unit Tests
run: dotnet test $env:Test_Project_Path
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
# If this step fails, then you should remove it and run the build manually (see below)
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.x'
+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();
}
}
+7 -7
View File
@@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<TargetFramework>net7.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0-preview-20220726-02" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>
@@ -14,15 +14,15 @@ namespace Daybreak.Configuration
[JsonProperty("BrowsersEnabled")]
public bool BrowsersEnabled { get; set; } = true;
[JsonProperty("ToolboxPath")]
public string ToolboxPath { get; set; }
public string? ToolboxPath { get; set; }
[JsonProperty("TexmodPath")]
public string TexmodPath { get; set; }
public string? TexmodPath { get; set; }
[JsonProperty("ToolboxAutoLaunch")]
public bool ToolboxAutoLaunch { get; set; }
[JsonProperty("LeftBrowserDefault")]
public string LeftBrowserDefault { get; set; } = "https://gwpvx.fandom.com/wiki/PvX_wiki";
public string? LeftBrowserDefault { get; set; } = "https://gwpvx.fandom.com/wiki/PvX_wiki";
[JsonProperty("RightBrowserDefault")]
public string RightBrowserDefault { get; set; } = "https://wiki.guildwars.com/wiki/Quick_access_links";
public string? RightBrowserDefault { get; set; } = "https://wiki.guildwars.com/wiki/Quick_access_links";
[JsonProperty("GuildwarsPaths")]
public List<GuildwarsPath> GuildwarsPaths { get; set; } = new();
[JsonProperty("ProtectedLoginCredentials")]
@@ -32,12 +32,14 @@ namespace Daybreak.Configuration
[JsonProperty("ExperimentalFeatures")]
public ExperimentalFeatures ExperimentalFeatures { get; set; } = new();
[JsonProperty("ShortcutLocation")]
public string ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
public string? ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
[JsonProperty("PlaceShortcut")]
public bool PlaceShortcut { get; set; }
[JsonProperty("AutoCheckUpdate")]
public bool AutoCheckUpdate { get; set; } = true;
[JsonProperty("ProtectedGraphAccessToken")]
public string ProtectedGraphAccessToken { get; set; }
public string? ProtectedGraphAccessToken { get; set; }
[JsonProperty("ProtectedGraphRefreshToken")]
public string? ProtectedGraphRefreshToken { get; set; }
}
}
+97 -77
View File
@@ -11,7 +11,6 @@ using Daybreak.Services.Screens;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Shortcuts;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Views;
using Microsoft.Extensions.Http.Logging;
using Microsoft.Extensions.Logging;
@@ -27,90 +26,111 @@ using Daybreak.Services.Updater.PostUpdate;
using System.Core.Extensions;
using Daybreak.Services.Updater.PostUpdate.Actions;
using Daybreak.Services.Graph;
using Microsoft.Extensions.DependencyInjection;
using Daybreak.Services.Navigation;
namespace Daybreak.Configuration
namespace Daybreak.Configuration;
public static class ProjectConfiguration
{
public static class ProjectConfiguration
public static void RegisterResolvers(IServiceManager serviceManager)
{
public static void RegisterResolvers(IServiceManager serviceManager)
serviceManager.ThrowIfNull();
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(IServiceCollection services)
{
services.ThrowIfNull();
services.AddSingleton<ILogsManager, JsonLogsManager>();
services.AddSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
services.AddSingleton<ILoggerFactory, LoggerFactory>(sp =>
{
serviceManager.ThrowIfNull();
var factory = new LoggerFactory();
factory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
return factory;
});
services.AddSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
sp.GetService<ILogsManager>()!,
sp.GetService<IDebugLogsWriter>()!));
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>();
}
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 RegisterServices(IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull();
public static void RegisterViews(IViewProducer viewProducer)
{
viewProducer.ThrowIfNull();
serviceProducer.RegisterSingleton<ILogsManager, JsonLogsManager>();
serviceProducer.RegisterSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
serviceProducer.RegisterSingleton<ILoggerFactory, LoggerFactory>(sp =>
{
var factory = new LoggerFactory();
factory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
return factory;
});
serviceProducer.RegisterSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
sp.GetService<ILogsManager>(),
sp.GetService<IDebugLogsWriter>()));
viewProducer.RegisterPermanentView<CompanionView>();
viewProducer.RegisterView<SettingsView>();
viewProducer.RegisterView<AskUpdateView>();
viewProducer.RegisterView<UpdateView>();
viewProducer.RegisterView<AccountsView>();
viewProducer.RegisterView<ExperimentalSettingsView>();
viewProducer.RegisterView<ExecutablesView>();
viewProducer.RegisterView<BuildTemplateView>();
viewProducer.RegisterView<BuildsListView>();
viewProducer.RegisterView<RequestElevationView>();
viewProducer.RegisterView<ScreenChoiceView>();
viewProducer.RegisterView<VersionManagementView>();
viewProducer.RegisterView<LogsView>();
viewProducer.RegisterView<IconDownloadView>();
viewProducer.RegisterView<GraphAuthorizationView>();
viewProducer.RegisterView<BuildsSynchronizationView>();
}
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>();
serviceProducer.RegisterScoped<IGraphClient, GraphClient>();
}
public static void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer)
{
postUpdateActionProducer.ThrowIfNull();
public static void RegisterViews(IViewProducer viewProducer)
{
viewProducer.ThrowIfNull();
viewProducer.RegisterView<MainView>();
viewProducer.RegisterView<SettingsView>();
viewProducer.RegisterView<AskUpdateView>();
viewProducer.RegisterView<UpdateView>();
viewProducer.RegisterView<SettingsCategoryView>();
viewProducer.RegisterView<AccountsView>();
viewProducer.RegisterView<ExperimentalSettingsView>();
viewProducer.RegisterView<ExecutablesView>();
viewProducer.RegisterView<BuildTemplateView>();
viewProducer.RegisterView<BuildsListView>();
viewProducer.RegisterView<RequestElevationView>();
viewProducer.RegisterView<ScreenChoiceView>();
viewProducer.RegisterView<VersionManagementView>();
viewProducer.RegisterView<LogsView>();
viewProducer.RegisterView<IconDownloadView>();
viewProducer.RegisterView<GraphAuthorizationView>();
viewProducer.RegisterView<BuildsSynchronizationView>();
}
public static void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer)
{
postUpdateActionProducer.ThrowIfNull();
postUpdateActionProducer.AddPostUpdateAction<RenameInstallerAction>();
}
postUpdateActionProducer.AddPostUpdateAction<RenameInstallerAction>();
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ 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; }
public string Key { get; private set; } = string.Empty;
private SecretKeys()
{
}
+3 -3
View File
@@ -7,7 +7,7 @@ namespace Daybreak.Configuration;
public static class SecretManager
{
private static JObject SecretsHolder;
private static JObject? SecretsHolder;
public static string GetSecret(SecretKeys secretKey)
{
@@ -16,7 +16,7 @@ public static class SecretManager
LoadSecrets();
}
return SecretsHolder.Value<string>(secretKey.Key);
return SecretsHolder!.Value<string>(secretKey.Key)!;
}
public static T GetSecret<T>(SecretKeys secretKey)
@@ -26,7 +26,7 @@ public static class SecretManager
LoadSecrets();
}
return SecretsHolder.Value<T>(secretKey.Key);
return SecretsHolder!.Value<T>(secretKey.Key)!;
}
private static void LoadSecrets()
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class AddButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public AddButton()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class BackButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public BackButton()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class BinButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public BinButton()
{
this.InitializeComponent();
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class CancelButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public CancelButton()
{
@@ -12,11 +12,7 @@
<Viewbox Stretch="Fill">
<Grid>
<Ellipse Height="4" Width="4" StrokeThickness="0.2" Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Ellipse>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
</StackPanel>
<local:ThreeDotsGlyph Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Padding="0.5"></local:ThreeDotsGlyph>
</Grid>
</Viewbox>
<Ellipse Fill="Transparent" MouseEnter="Ellipse_MouseEnter" MouseLeave="Ellipse_MouseLeave" MouseLeftButtonDown="Ellipse_MouseLeftButtonDown"></Ellipse>
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class FilePickerGlyph : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public FilePickerGlyph()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class HelpButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public HelpButton()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class HomeButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public HomeButton()
{
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class MaximizeButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public MaximizeButton()
{
+41
View File
@@ -0,0 +1,41 @@
<UserControl x:Class="Daybreak.Controls.Buttons.MenuButton"
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.Buttons"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behaviors="clr-namespace:Daybreak.Behaviors"
xmlns:converters="clr-namespace:Daybreak.Converters"
x:Name="_this"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:MenuButtonHighlightConverter x:Key="HighlightConverter"></converters:MenuButtonHighlightConverter>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Rectangle
Fill="{Binding ElementName=_this, Path=HighlightColor, Mode=OneWay}"
Opacity="{Binding ElementName=_this, Path=Highlighted, Converter={StaticResource HighlightConverter}}"
Grid.ColumnSpan="2">
</Rectangle>
<ContentPresenter Margin="5"
Content="{Binding ElementName=_this, Path=InnerContent, Mode=OneWay}" />
<TextBlock Grid.Column="1"
Text="{Binding ElementName=_this, Path=Title, Mode=OneWay}"
FontSize="{Binding ElementName=_this, Path=FontSize, Mode=OneWay}"
FontFamily="{Binding ElementName=_this, Path=FontFamily, Mode=OneWay}"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap"
TextAlignment="Left" />
<Rectangle Fill="Transparent"
MouseEnter="Grid_MouseEnter"
MouseLeave="Grid_MouseLeave"
MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"
Grid.ColumnSpan="2" />
</Grid>
</UserControl>
@@ -0,0 +1,44 @@
using System;
using System.Windows.Extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Daybreak.Controls.Buttons;
/// <summary>
/// Interaction logic for MenuButton.xaml
/// </summary>
public partial class MenuButton : UserControl
{
public event EventHandler? Clicked;
[GenerateDependencyProperty]
private bool highlighted;
[GenerateDependencyProperty]
private Brush highlightColor = default!;
[GenerateDependencyProperty]
private FrameworkElement innerContent = default!;
[GenerateDependencyProperty(InitialValue = "")]
private string title = string.Empty;
public MenuButton()
{
this.InitializeComponent();
}
private void Grid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = true;
}
private void Grid_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = false;
}
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.Clicked?.Invoke(this, e);
}
}
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class MinusButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public MinusButton()
{
+13 -54
View File
@@ -12,61 +12,20 @@ namespace Daybreak.Controls
/// </summary>
public partial class OpaqueButton : UserControl
{
public static readonly DependencyProperty TextProperty =
DependencyPropertyExtensions.Register<OpaqueButton, string>(nameof(Text));
public event EventHandler? Clicked;
public static readonly DependencyProperty TransparentBackgroundProperty =
DependencyPropertyExtensions.Register<OpaqueButton, Brush>(nameof(TransparentBackground));
public static readonly DependencyProperty HighlightProperty =
DependencyPropertyExtensions.Register<OpaqueButton, Brush>(nameof(Highlight));
public static readonly DependencyProperty BackgroundOpacityProperty =
DependencyPropertyExtensions.Register<OpaqueButton, double>(nameof(BackgroundOpacity));
public static readonly DependencyProperty HighlightOpacityProperty =
DependencyPropertyExtensions.Register<OpaqueButton, double>(nameof(HighlightOpacity));
public static readonly DependencyProperty HighlightVisibleProperty =
DependencyPropertyExtensions.Register<OpaqueButton, bool>(nameof(HighlightVisible));
public event EventHandler Clicked;
public string Text
{
get => this.GetTypedValue<string>(TextProperty);
set => this.SetTypedValue(TextProperty, value);
}
public Brush TransparentBackground
{
get => this.GetTypedValue<Brush>(TransparentBackgroundProperty);
set => this.SetTypedValue(TransparentBackgroundProperty, value);
}
public Brush Highlight
{
get => this.GetTypedValue<Brush>(HighlightProperty);
set => this.SetTypedValue(HighlightProperty, value);
}
public double BackgroundOpacity
{
get => this.GetTypedValue<double>(BackgroundOpacityProperty);
set => this.SetTypedValue(BackgroundOpacityProperty, value);
}
public double HighlightOpacity
{
get => this.GetTypedValue<double>(HighlightOpacityProperty);
set => this.SetTypedValue(HighlightOpacityProperty, value);
}
public bool HighlightVisible
{
get => this.GetTypedValue<bool>(HighlightVisibleProperty);
set => this.SetTypedValue(HighlightVisibleProperty, value);
}
[GenerateDependencyProperty(InitialValue = "")]
private string text = string.Empty;
[GenerateDependencyProperty]
private Brush transparentBackground = default!;
[GenerateDependencyProperty]
private Brush highlight = default!;
[GenerateDependencyProperty]
public double backgroundOpacity;
[GenerateDependencyProperty]
public double highlightOpacity;
[GenerateDependencyProperty]
public bool highlightVisible;
public OpaqueButton()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class SaveButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public SaveButton()
{
@@ -9,7 +9,7 @@ namespace Daybreak.Controls;
/// </summary>
public partial class SynchronizeButton : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public SynchronizeButton()
{
+32 -56
View File
@@ -1,68 +1,44 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Media;
namespace Daybreak.Controls
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for TileButton.xaml
/// </summary>
public partial class TileButton : UserControl
{
/// <summary>
/// Interaction logic for TileButton.xaml
/// </summary>
public partial class TileButton : UserControl
public event EventHandler? Clicked;
[GenerateDependencyProperty]
public bool highlighted;
[GenerateDependencyProperty(InitialValue = "")]
public string title = string.Empty;
[GenerateDependencyProperty]
public FrameworkElement InnerContent = default!;
[GenerateDependencyProperty]
public Brush HighlightColor = default!;
public TileButton()
{
public event EventHandler Clicked;
this.InitializeComponent();
}
public static readonly DependencyProperty HighlightedProperty =
DependencyProperty.Register("Highlighted", typeof(bool), typeof(TileButton), null);
public static readonly DependencyProperty HighlightColorProperty =
DependencyProperty.Register("HighlightColor", typeof(Brush), typeof(TileButton), null);
public static readonly DependencyProperty InnerContentProperty =
DependencyProperty.Register("InnerContent", typeof(FrameworkElement), typeof(TileButton), null);
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TileButton), null);
private void Grid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = true;
}
public TileButton()
{
this.InitializeComponent();
}
private void Grid_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = false;
}
public bool Highlighted
{
get => (bool)this.GetValue(HighlightedProperty);
set => this.SetValue(HighlightedProperty, value);
}
public string Title
{
get => this.GetValue(TitleProperty) as string;
set => this.SetValue(TitleProperty, value);
}
public FrameworkElement InnerContent
{
get => this.GetValue(InnerContentProperty) as FrameworkElement;
set => this.SetValue(InnerContentProperty, value);
}
public Brush HighlightColor
{
get => this.GetValue(HighlightColorProperty) as Brush;
set => this.SetValue(HighlightColorProperty, value);
}
private void Grid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = true;
}
private void Grid_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
this.Highlighted = false;
}
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Clicked?.Invoke(this, e);
}
private void Rectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
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;
@@ -28,15 +29,15 @@ namespace Daybreak.Controls
private const string BrowserDownloadLink = "https://developer.microsoft.com/en-us/microsoft-edge/webview2/";
private static CoreWebView2Environment coreWebView2Environment;
private static CoreWebView2Environment? coreWebView2Environment;
public event EventHandler<string> FavoriteUriChanged;
public event EventHandler MaximizeClicked;
public event EventHandler<Build> BuildDecoded;
public event EventHandler<string>? FavoriteUriChanged;
public event EventHandler? MaximizeClicked;
public event EventHandler<Build>? BuildDecoded;
private ILiveOptions<ApplicationConfiguration> liveOptions;
private ILogger<ChromiumBrowserWrapper> logger;
private IBuildTemplateManager buildTemplateManager;
private ILiveOptions<ApplicationConfiguration>? liveOptions;
private ILogger<ChromiumBrowserWrapper>? logger;
private IBuildTemplateManager? buildTemplateManager;
[GenerateDependencyProperty(InitialValue = true)]
private bool canDownloadBuild;
@@ -53,7 +54,9 @@ namespace Daybreak.Controls
[GenerateDependencyProperty]
private bool navigating;
[GenerateDependencyProperty]
private string favoriteAddress;
private string favoriteAddress = string.Empty;
[GenerateDependencyProperty(InitialValue = false)]
private bool preventDispose;
public string Address
{
get => this.GetTypedValue<string>(AddressProperty);
@@ -83,9 +86,9 @@ namespace Daybreak.Controls
public async Task InitializeDefaultBrowser()
{
var options = Launch.Launcher.Instance.ApplicationServiceManager.GetService<ILiveOptions<ApplicationConfiguration>>();
var buildTemplateManager = Launch.Launcher.Instance.ApplicationServiceManager.GetService<IBuildTemplateManager>();
var logger = Launch.Launcher.Instance.ApplicationServiceManager.GetService<ILogger<ChromiumBrowserWrapper>>();
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);
}
@@ -109,7 +112,7 @@ namespace Daybreak.Controls
private void InitializeEnvironment()
{
if (this.liveOptions.Value.BrowsersEnabled is false)
if (this.liveOptions!.Value.BrowsersEnabled is false)
{
this.BrowserSupported = false;
return;
@@ -117,16 +120,13 @@ namespace Daybreak.Controls
try
{
if (coreWebView2Environment is null)
{
coreWebView2Environment = System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", null));
}
coreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", null));
this.BrowserSupported = true;
}
catch(Exception e)
{
this.logger.LogWarning($"Browser initialization failed. Details: {e}");
this.logger!.LogWarning($"Browser initialization failed. Details: {e}");
this.BrowserSupported = false;
}
}
@@ -138,7 +138,7 @@ namespace Daybreak.Controls
this.WebBrowser.IsEnabled = true;
this.BrowserEnabled = true;
await this.WebBrowser.EnsureCoreWebView2Async(coreWebView2Environment);
this.AddressBarReadonly = this.liveOptions.Value.AddressBarReadonly;
this.AddressBarReadonly = this.liveOptions!.Value.AddressBarReadonly;
this.CanDownloadBuild = this.liveOptions.Value.ExperimentalFeatures.DynamicBuildLoading;
this.WebBrowser.CoreWebView2.NewWindowRequested += (browser, args) => args.Handled = true;
this.WebBrowser.NavigationStarting += (browser, args) =>
@@ -153,7 +153,7 @@ namespace Daybreak.Controls
}
};
this.WebBrowser.NavigationCompleted += (browser, args) => this.Navigating = false;
this.WebBrowser.WebMessageReceived += this.CoreWebView2_WebMessageReceived;
this.WebBrowser.WebMessageReceived += this.CoreWebView2_WebMessageReceived!;
this.WebBrowser.CoreWebView2.Settings.AreDevToolsEnabled = false;
this.WebBrowser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
if (this.CanDownloadBuild)
@@ -190,7 +190,7 @@ namespace Daybreak.Controls
{
var newAddress = sender.As<TextBox>().Text;
newAddress = SanitizeAddress(newAddress);
if (newAddress is null)
if (newAddress.IsNullOrWhiteSpace())
{
return;
}
@@ -203,20 +203,20 @@ namespace Daybreak.Controls
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs args)
{
BrowserPayload payload = default;
BrowserPayload payload = default!;
try
{
payload = args.WebMessageAsJson.Deserialize<BrowserPayload>();
}
catch(Exception e)
{
this.logger.LogError(e, $"Exception encountered when deserializing {nameof(BrowserPayload)}");
this.logger!.LogError(e, $"Exception encountered when deserializing {nameof(BrowserPayload)}");
}
if (payload?.Key == BrowserPayload.PayloadKeys.ContextMenu)
{
var contextMenuPayload = args.WebMessageAsJson.Deserialize<BrowserPayload<OnContextMenuPayload>>();
var maybeTemplate = contextMenuPayload.Value.Selection;
var maybeTemplate = contextMenuPayload.Value!.Selection;
if (string.IsNullOrWhiteSpace(maybeTemplate))
{
return;
@@ -245,7 +245,7 @@ namespace Daybreak.Controls
}
catch(Exception e)
{
this.logger.LogWarning($"Exception when decoding template {maybeTemplate}. Details {e}");
this.logger!.LogWarning($"Exception when decoding template {maybeTemplate}. Details {e}");
}
});
}
@@ -260,6 +260,11 @@ namespace Daybreak.Controls
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
if (this.PreventDispose)
{
return;
}
this.WebBrowser?.Dispose();
}
@@ -318,7 +323,7 @@ namespace Daybreak.Controls
{
if (string.IsNullOrWhiteSpace(address))
{
return null;
return default!;
}
if (address.StartsWith("www") is false &&
@@ -332,7 +337,7 @@ namespace Daybreak.Controls
}
Uri.TryCreate(address, UriKind.Absolute, out var uri);
return uri?.ToString();
return uri?.ToString() ?? string.Empty;
}
}
}
+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();
}
}
+18
View File
@@ -0,0 +1,18 @@
<UserControl x:Class="Daybreak.Controls.CogGlyph"
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">
<Viewbox>
<Grid>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="m76.89,45.018c0,-1.587 -0.151,-3.138 -0.383,-4.66l11.227,-6.48l-11.639,-20.136l-11.616,6.47c-2.421,-1.931 -5.479,-3.506 -8.479,-4.664l0,-11.548l-22,0l0,11.548c-4,1.112 -5.649,2.617 -8.005,4.448l-11.251,-6.604l-11.914,20.006l11.12,6.604c-0.262,1.643 -0.45,3.308 -0.45,5.016c0,1.587 0.153,3.136 0.376,4.658l-11.228,6.484l11.634,20.135l11.424,-6.472c2.431,1.933 4.294,3.506 8.294,4.665l0,12.512l22,0l0,-12.512c3,-1.115 5.839,-2.618 8.19,-4.449l11.348,6.604l11.96,-20.006l-11.095,-6.603c0.258,-1.64 0.487,-3.304 0.487,-5.016zm-31.704,14.137c-7.799,0 -14.138,-6.332 -14.138,-14.138c0,-7.808 6.339,-14.136 14.138,-14.136c7.81,0 14.14,6.328 14.14,14.136c-0.001,7.806 -6.331,14.138 -14.14,14.138z"></Path>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="m56.5,89l-22,0c-1.381,0 -2.5,-1.119 -2.5,-2.5l0,-10.199c-2.612,-0.955 -4.464,-2.126 -6.274,-3.49l-10.229,5.671c-1.192,0.661 -2.694,0.244 -3.377,-0.936l-11.633,-20.135c-0.331,-0.574 -0.422,-1.257 -0.25,-1.897s0.591,-1.188 1.165,-1.519l9.794,-5.654c-0.121,-1.164 -0.181,-2.262 -0.181,-3.323c0,-1.317 0.112,-2.592 0.254,-3.721l-9.656,-5.75c-0.57,-0.34 -0.982,-0.893 -1.145,-1.537c-0.163,-0.644 -0.063,-1.326 0.279,-1.896l11.977,-20.006c0.702,-1.173 2.218,-1.564 3.399,-0.878l9.927,5.762c1.618,-1.213 3.362,-2.346 5.949,-3.273l0,-10.219c0.001,-1.381 1.12,-2.5 2.501,-2.5l22,0c1.381,0 2.5,1.119 2.5,2.5l0,10.377c2.124,0.938 4.104,2.059 5.919,3.35l9.939,-5.657c1.19,-0.677 2.713,-0.269 3.4,0.922l11.639,20.135c0.332,0.574 0.423,1.257 0.251,1.897s-0.591,1.188 -1.165,1.519l-9.781,5.646c0.126,1.165 0.188,2.266 0.188,3.329c0,1.327 -0.108,2.597 -0.246,3.717l9.692,5.752c0.571,0.339 0.983,0.891 1.147,1.533c0.163,0.644 0.065,1.325 -0.274,1.896l-11.897,20.005c-0.703,1.183 -2.234,1.573 -3.416,0.876l-9.798,-5.765c-1.782,1.25 -3.634,2.29 -5.598,3.144l0,10.324c0,1.381 -1.119,2.5 -2.5,2.5zm-19.5,-5l17,0l0,-9.512c0,-1.045 0.649,-1.979 1.629,-2.344c2.624,-0.975 5.004,-2.309 7.275,-4.078c0.809,-0.629 1.921,-0.702 2.804,-0.182l9.078,5.341l9.347,-15.716l-8.974,-5.325c-0.88,-0.522 -1.354,-1.528 -1.194,-2.538c0.198,-1.259 0.425,-2.923 0.425,-4.629c0,-1.314 -0.116,-2.716 -0.354,-4.284c-0.154,-1.018 0.33,-2.026 1.222,-2.541l9.061,-5.23l-9.145,-15.818l-9.208,5.241c-0.888,0.505 -1.995,0.42 -2.795,-0.219c-2.199,-1.754 -4.747,-3.196 -7.57,-4.286c-0.964,-0.372 -1.6,-1.299 -1.6,-2.332l0,-9.548l-17.001,0l0,9.548c0,1.123 -0.748,2.107 -1.83,2.408c-3.47,0.965 -5.073,2.212 -7.293,3.938l-0.098,0.076c-0.804,0.625 -1.909,0.7 -2.789,0.188l-9.243,-5.365l-9.423,15.739l8.937,5.321c0.879,0.524 1.351,1.531 1.189,2.542c-0.202,1.27 -0.435,2.943 -0.435,4.622c0,1.318 0.112,2.724 0.342,4.297c0.148,1.013 -0.337,2.015 -1.224,2.526l-9.065,5.234l9.151,15.842l9.525,-5.28c0.883,-0.49 1.976,-0.4 2.768,0.229c2.384,1.896 4.207,3.214 7.684,4.221c1.069,0.31 1.804,1.29 1.804,2.402l0,9.512zm8.186,-22.345c-9.174,0 -16.638,-7.464 -16.638,-16.638c0,-9.173 7.464,-16.636 16.638,-16.636c9.175,0 16.64,7.463 16.64,16.636c-0.001,9.174 -7.466,16.638 -16.64,16.638zm0,-28.273c-6.417,0 -11.638,5.22 -11.638,11.636c0,6.417 5.221,11.638 11.638,11.638c6.418,0 11.64,-5.221 11.64,-11.638c-0.001,-6.416 -5.222,-11.636 -11.64,-11.636z"></Path>
</Grid>
</Viewbox>
</UserControl>
+14
View File
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for CogGlyph.xaml
/// </summary>
public partial class CogGlyph : UserControl
{
public CogGlyph()
{
this.InitializeComponent();
}
}
+5 -2
View File
@@ -10,11 +10,14 @@
<Viewbox>
<Grid>
<Line Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
X1="15" X2="26" Y1="11" Y2="0"></Line>
X1="15" X2="26" Y1="11" Y2="0"
StrokeThickness="2"></Line>
<Ellipse Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Width="4" Height="4"
Width="4" Height="4"
StrokeThickness="2"
Margin="8,13,13,8"></Ellipse>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
StrokeThickness="2"
Data="m15.025,0l-12.7,11.3c-3.1,3.1 -3.1,8.2 0,11.3s8.2,3.1 11.3,0l12.4,-11.6"></Path>
</Grid>
</Viewbox>
+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();
}
}
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class RefreshGlyph : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public RefreshGlyph()
{
+1 -1
View File
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class StarGlyph : UserControl
{
public event EventHandler Clicked;
public event EventHandler? Clicked;
public StarGlyph()
{
@@ -0,0 +1,17 @@
<UserControl x:Class="Daybreak.Controls.ThreeDotsGlyph"
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>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
<Ellipse Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1" Width="1"></Ellipse>
</StackPanel>
</Viewbox>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for ThreeDotsGlyph.xaml
/// </summary>
public partial class ThreeDotsGlyph : UserControl
{
public ThreeDotsGlyph()
{
this.InitializeComponent();
}
}
+93
View File
@@ -0,0 +1,93 @@
<UserControl x:Class="Daybreak.Controls.MenuList"
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"
xmlns:buttons="clr-namespace:Daybreak.Controls.Buttons"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<buttons:MenuButton Title="Game companion"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="GameCompanionButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:GoldenArrowGlyph Foreground="White"
Width="40"></local:GoldenArrowGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Manage builds"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="ManageBuildsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FireballGlyph Foreground="White"
Width="40"></local:FireballGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Account settings"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="AccountSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:AvatarGlyph Foreground="White"
Width="40"></local:AvatarGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Guildwars settings"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="GuildwarsSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FileGlyph Foreground="White"
Width="40"></local:FileGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Launcher settings"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="LauncherSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:CogGlyph Foreground="White"
Width="40"
Height="20"></local:CogGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Experimental settings"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="ExperimentalSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:ExperimentGlyph Foreground="White"
Width="40"></local:ExperimentGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Manage client version"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="VersionManagementButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:StaticGlyph Foreground="White"
Width="40"></local:StaticGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Logs"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="LogsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:LogsGlyph Foreground="White"
Width="40"></local:LogsGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</StackPanel>
</UserControl>
+78
View File
@@ -0,0 +1,78 @@
using Daybreak.Configuration;
using Daybreak.Launch;
using Daybreak.Services.Navigation;
using Daybreak.Views;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Configuration;
using System.Windows.Controls;
namespace Daybreak.Controls
{
/// <summary>
/// Interaction logic for MenuList.xaml
/// </summary>
public partial class MenuList : UserControl
{
public MenuList()
{
this.InitializeComponent();
}
private void GameCompanionButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<CompanionView>();
}
private void AccountSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<AccountsView>();
}
private void GuildwarsSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<ExecutablesView>();
}
private void LauncherSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<SettingsView>();
}
private void ExperimentalSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<ExperimentalSettingsView>();
}
private void ManageBuildsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
var options = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<ApplicationConfiguration>>();
if (options.Value.ExperimentalFeatures.DownloadIcons)
{
viewManager.ShowView<IconDownloadView>();
}
else
{
viewManager.ShowView<BuildsListView>();
}
}
private void VersionManagementButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<VersionManagementView>();
}
private void LogsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<LogsView>();
}
}
}
+2 -2
View File
@@ -11,12 +11,12 @@ namespace Daybreak.Controls
/// </summary>
public partial class SearchTextBox : UserControl
{
public event EventHandler<string> TextChanged;
public event EventHandler<string>? TextChanged;
[GenerateDependencyProperty(InitialValue = true)]
private bool placeholderVisibility;
[GenerateDependencyProperty]
private string searchText;
private string searchText = default!;
public SearchTextBox()
{
@@ -14,15 +14,15 @@ namespace Daybreak.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class AccountTemplate : UserControl
{
public event EventHandler RemoveClicked;
public event EventHandler DefaultClicked;
public event EventHandler? RemoveClicked;
public event EventHandler? DefaultClicked;
[GenerateDependencyProperty]
private string username;
private string username = string.Empty;
[GenerateDependencyProperty]
private string password;
private string password = string.Empty;
[GenerateDependencyProperty]
private string characterName;
private string characterName = string.Empty;
[GenerateDependencyProperty]
private bool isDefault;
@@ -14,8 +14,8 @@ namespace Daybreak.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class AttributeTemplate : UserControl
{
public event EventHandler<AttributeEntry> HelpClicked;
public event EventHandler<AttributeEntry> AttributeChanged;
public event EventHandler<AttributeEntry>? HelpClicked;
public event EventHandler<AttributeEntry>? AttributeChanged;
[GenerateDependencyProperty(InitialValue = false)]
private bool canAdd;
@@ -9,7 +9,7 @@ namespace Daybreak.Controls
/// </summary>
public partial class BuildEntryTemplate : UserControl
{
public event EventHandler<BuildEntry> RemoveClicked;
public event EventHandler<BuildEntry>? RemoveClicked;
public BuildEntryTemplate()
{
@@ -2,6 +2,7 @@
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.IconRetrieve;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
@@ -15,7 +16,6 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using Utils;
namespace Daybreak.Controls
{
@@ -31,35 +31,35 @@ namespace Daybreak.Controls
private bool suppressBuildChanged = false;
private bool loadedProperties = false;
private IIconBrowser iconBrowser;
private BuildEntry loadedBuild;
private SkillTemplate selectingSkillTemplate;
private CancellationTokenSource cancellationTokenSource = new();
private IIconBrowser? iconBrowser;
private BuildEntry? loadedBuild;
private SkillTemplate? selectingSkillTemplate;
private CancellationTokenSource? cancellationTokenSource = new();
public event EventHandler BuildChanged;
public event EventHandler? BuildChanged;
[GenerateDependencyProperty]
private string skillSearchText;
private string skillSearchText = string.Empty;
[GenerateDependencyProperty]
private Profession primaryProfession;
private Profession primaryProfession = default!;
[GenerateDependencyProperty]
private Profession secondaryProfession;
private Profession secondaryProfession = default!;
[GenerateDependencyProperty]
private Skill skill0;
private Skill skill0 = default!;
[GenerateDependencyProperty]
private Skill skill1;
private Skill skill1 = default!;
[GenerateDependencyProperty]
private Skill skill2;
private Skill skill2 = default!;
[GenerateDependencyProperty]
private Skill skill3;
private Skill skill3 = default!;
[GenerateDependencyProperty]
private Skill skill4;
private Skill skill4 = default!;
[GenerateDependencyProperty]
private Skill skill5;
private Skill skill5 = default!;
[GenerateDependencyProperty]
private Skill skill6;
private Skill skill6 = default!;
[GenerateDependencyProperty]
private Skill skill7;
private Skill skill7 = default!;
public ObservableCollection<Skill> AvailableSkills { get; } = new ObservableCollection<Skill>();
public ObservableCollection<AttributeEntry> Attributes { get; } = new ObservableCollection<AttributeEntry>();
public ObservableCollection<Profession> Professions { get; } = new ObservableCollection<Profession>(Profession.Professions);
@@ -105,11 +105,11 @@ namespace Daybreak.Controls
{
if (e.Property == PrimaryProfessionProperty)
{
this.loadedBuild.Build.Primary = this.PrimaryProfession;
this.loadedBuild!.Build!.Primary = this.PrimaryProfession;
}
else
{
this.loadedBuild.Build.Secondary = this.SecondaryProfession;
this.loadedBuild!.Build!.Secondary = this.SecondaryProfession;
}
this.LoadSkills();
@@ -131,14 +131,14 @@ namespace Daybreak.Controls
{
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.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());
}
}
@@ -146,7 +146,7 @@ namespace Daybreak.Controls
private void BuildTemplate_Unloaded(object sender, RoutedEventArgs e)
{
this.cancellationTokenSource.Cancel();
this.cancellationTokenSource?.Cancel();
}
private void InitializeProperties()
@@ -186,17 +186,17 @@ namespace Daybreak.Controls
if (this.PrimaryProfession != Profession.None)
{
possibleAttributes.Add(new AttributeEntry { Attribute = this.PrimaryProfession.PrimaryAttribute });
possibleAttributes.AddRange(this.PrimaryProfession.Attributes.Select(a => new AttributeEntry { Attribute = a }));
possibleAttributes.AddRange(this.PrimaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
if (this.SecondaryProfession != Profession.None && this.SecondaryProfession != this.PrimaryProfession)
{
possibleAttributes.AddRange(this.SecondaryProfession.Attributes.Select(a => new AttributeEntry { Attribute = a }));
possibleAttributes.AddRange(this.SecondaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
this.Attributes.ClearAnd().AddRange(possibleAttributes.Select(entry =>
{
var maybePresentAttribute = this.loadedBuild.Build.Attributes.Where(buildEntry => entry.Attribute == buildEntry.Attribute).FirstOrDefault();
var maybePresentAttribute = this.loadedBuild!.Build!.Attributes.Where(buildEntry => entry.Attribute == buildEntry.Attribute).FirstOrDefault();
if (maybePresentAttribute is null)
{
return entry;
@@ -206,7 +206,7 @@ namespace Daybreak.Controls
return entry;
}));
this.loadedBuild.Build.Attributes = this.Attributes.ToList();
this.loadedBuild!.Build!.Attributes = this.Attributes.ToList();
}
private async void LoadSkills()
@@ -277,7 +277,7 @@ namespace Daybreak.Controls
this.suppressBuildChanged = true;
var build = this.DataContext.As<BuildEntry>();
this.loadedBuild = build;
this.PrimaryProfession = build.Build.Primary;
this.PrimaryProfession = build.Build!.Primary;
this.SecondaryProfession = build.Build.Secondary;
this.Skill0 = build.Build.Skills[0];
this.Skill1 = build.Build.Skills[1];
@@ -332,7 +332,7 @@ namespace Daybreak.Controls
return;
}
this.BrowseToInfo(this.PrimaryProfession.Name);
this.BrowseToInfo(this.PrimaryProfession.Name!);
if (e is RoutedEventArgs routedEventArgs)
{
routedEventArgs.Handled = true;
@@ -346,7 +346,7 @@ namespace Daybreak.Controls
return;
}
this.BrowseToInfo(this.SecondaryProfession.Name);
this.BrowseToInfo(this.SecondaryProfession.Name!);
if (e is RoutedEventArgs routedEventArgs)
{
routedEventArgs.Handled = true;
@@ -355,7 +355,7 @@ namespace Daybreak.Controls
private void AttributeTemplate_HelpClicked(object _, AttributeEntry e)
{
this.BrowseToInfo(e.Attribute.Name);
this.BrowseToInfo(e.Attribute?.Name!);
}
private void AttributeTemplate_AttributeChanged(object _, AttributeEntry e)
@@ -375,7 +375,7 @@ namespace Daybreak.Controls
}
else
{
this.BrowseToInfo(skill.Name);
this.BrowseToInfo(skill.Name!);
}
e.Handled = true;
@@ -402,14 +402,14 @@ namespace Daybreak.Controls
this.selectingSkillTemplate.DataContext = sender.As<ListView>().SelectedItem;
this.HideSkillListView();
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.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;
}
private void ListView_NavigateWithMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
@@ -443,7 +443,7 @@ namespace Daybreak.Controls
private async IAsyncEnumerable<Skill> FilterSkills(string searchTerm)
{
// Replace symbols to ease search
searchTerm = searchTerm?.Replace("\"", "").Replace("!", "");
searchTerm = searchTerm?.Replace("\"", "").Replace("!", "")!;
foreach (var skill in Skill.Skills)
{
@@ -465,7 +465,7 @@ namespace Daybreak.Controls
continue;
}
var matchesName = await Task.Run(() => StringUtils.MatchesSearchString(skill.Name.Replace("\"", "").Replace("!", ""), searchTerm));
var matchesName = await Task.Run(() => StringUtils.MatchesSearchString(skill.Name!.Replace("\"", "").Replace("!", ""), searchTerm!));
if (matchesName)
{
yield return skill;
@@ -15,11 +15,11 @@ namespace Daybreak.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class GuildwarsPathTemplate : UserControl
{
public event EventHandler RemoveClicked;
public event EventHandler DefaultClicked;
public event EventHandler? RemoveClicked;
public event EventHandler? DefaultClicked;
[GenerateDependencyProperty]
private string path;
private string path = string.Empty;
[GenerateDependencyProperty]
private bool isDefault;
@@ -13,7 +13,7 @@ namespace Daybreak.Controls
private bool expanded;
[GenerateDependencyProperty]
private string message;
private string message = string.Empty;
public LogMessageTemplate()
{
@@ -15,12 +15,12 @@ namespace Daybreak.Controls.Templates
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class ScreenTemplate : UserControl
{
public event EventHandler<Screen> Clicked;
public event EventHandler<Screen>? Clicked;
[GenerateDependencyProperty]
private string screenId;
private string screenId = string.Empty;
[GenerateDependencyProperty]
private Brush highlight;
private Brush highlight = default!;
public ScreenTemplate()
{
@@ -16,13 +16,13 @@ namespace Daybreak.Controls
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class SkillTemplate : UserControl
{
public event EventHandler<RoutedEventArgs> Clicked;
public event EventHandler RemoveClicked;
public event EventHandler<RoutedEventArgs>? Clicked;
public event EventHandler? RemoveClicked;
private IIconCache iconRetriever;
private IIconCache? iconRetriever;
[GenerateDependencyProperty]
private ImageSource imageSource;
private ImageSource imageSource = default!;
[GenerateDependencyProperty]
private double borderOpacity;
@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Daybreak.Converters
{
public class MenuButtonHighlightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(double) &&
value is bool boolean)
{
return boolean ? 0.4 : 0;
}
else
{
throw new NotImplementedException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(bool) &&
value is double doubleValue)
{
return doubleValue == 0.4;
}
else
{
throw new NotImplementedException();
}
}
}
}
+14 -12
View File
@@ -2,15 +2,17 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<RootNamespace>Daybreak</RootNamespace>
<TargetFramework>net7.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<Platforms>AnyCPU</Platforms>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.6.2</Version>
<Version>0.9.7.5</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
</PropertyGroup>
@@ -34,24 +36,24 @@
</Target>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.12" />
<PackageReference Include="LiteDB" Version="5.0.15" />
<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.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1518.46" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<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>
@@ -105,7 +107,7 @@
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
<Exec Command="echo.&gt;$(Version).version" />
</Target>
+17 -13
View File
@@ -1,9 +1,11 @@
using Daybreak.Configuration;
using Daybreak.Exceptions;
using Daybreak.Services.Navigation;
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.IO;
using System.Linq;
@@ -18,9 +20,9 @@ namespace Daybreak.Launch
{
public readonly static Launcher Instance = new();
private ILogger logger;
private ILogger? logger;
public IServiceManager ApplicationServiceManager => this.ServiceManager;
public System.IServiceProvider ApplicationServiceProvider => this.ServiceProvider;
[STAThread]
public static int Main()
@@ -28,16 +30,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)
{
@@ -78,7 +79,7 @@ namespace Daybreak.Launch
return true;
}
}
else if (e.Message.Contains("Invalid window handle.") && e.StackTrace.Contains("CoreWebView2Environment.CreateCoreWebView2ControllerAsync"))
else if (e.Message.Contains("Invalid window handle.") && e.StackTrace?.Contains("CoreWebView2Environment.CreateCoreWebView2ControllerAsync") is true)
{
/*
* Ignore exception caused by browser failing to initialize due to missing window.
@@ -94,7 +95,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()
@@ -103,8 +107,8 @@ 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()
+55 -19
View File
@@ -3,11 +3,11 @@
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:webview="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:local="clr-namespace:Daybreak.Launch"
xmlns:wcl="clr-namespace:WCL;assembly=WCL"
mc:Ignorable="d"
xmlns:controls="clr-namespace:Daybreak.Controls"
xmlns:converters="clr-namespace:Daybreak.Converters"
xmlns:effects="clr-namespace:System.Windows.Media.Extensions.Effects;assembly=WpfExtended"
WindowStyle="None"
ResizeMode="NoResize"
@@ -25,14 +25,7 @@
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
<Viewbox Margin="5" Stretch="Uniform">
<Grid>
<Path Stroke="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="m76.89,45.018c0,-1.587 -0.151,-3.138 -0.383,-4.66l11.227,-6.48l-11.639,-20.136l-11.616,6.47c-2.421,-1.931 -5.479,-3.506 -8.479,-4.664l0,-11.548l-22,0l0,11.548c-4,1.112 -5.649,2.617 -8.005,4.448l-11.251,-6.604l-11.914,20.006l11.12,6.604c-0.262,1.643 -0.45,3.308 -0.45,5.016c0,1.587 0.153,3.136 0.376,4.658l-11.228,6.484l11.634,20.135l11.424,-6.472c2.431,1.933 4.294,3.506 8.294,4.665l0,12.512l22,0l0,-12.512c3,-1.115 5.839,-2.618 8.19,-4.449l11.348,6.604l11.96,-20.006l-11.095,-6.603c0.258,-1.64 0.487,-3.304 0.487,-5.016zm-31.704,14.137c-7.799,0 -14.138,-6.332 -14.138,-14.138c0,-7.808 6.339,-14.136 14.138,-14.136c7.81,0 14.14,6.328 14.14,14.136c-0.001,7.806 -6.331,14.138 -14.14,14.138z"></Path>
<Path Fill="{TemplateBinding Foreground}" VerticalAlignment="Center" HorizontalAlignment="Center"
Data="m56.5,89l-22,0c-1.381,0 -2.5,-1.119 -2.5,-2.5l0,-10.199c-2.612,-0.955 -4.464,-2.126 -6.274,-3.49l-10.229,5.671c-1.192,0.661 -2.694,0.244 -3.377,-0.936l-11.633,-20.135c-0.331,-0.574 -0.422,-1.257 -0.25,-1.897s0.591,-1.188 1.165,-1.519l9.794,-5.654c-0.121,-1.164 -0.181,-2.262 -0.181,-3.323c0,-1.317 0.112,-2.592 0.254,-3.721l-9.656,-5.75c-0.57,-0.34 -0.982,-0.893 -1.145,-1.537c-0.163,-0.644 -0.063,-1.326 0.279,-1.896l11.977,-20.006c0.702,-1.173 2.218,-1.564 3.399,-0.878l9.927,5.762c1.618,-1.213 3.362,-2.346 5.949,-3.273l0,-10.219c0.001,-1.381 1.12,-2.5 2.501,-2.5l22,0c1.381,0 2.5,1.119 2.5,2.5l0,10.377c2.124,0.938 4.104,2.059 5.919,3.35l9.939,-5.657c1.19,-0.677 2.713,-0.269 3.4,0.922l11.639,20.135c0.332,0.574 0.423,1.257 0.251,1.897s-0.591,1.188 -1.165,1.519l-9.781,5.646c0.126,1.165 0.188,2.266 0.188,3.329c0,1.327 -0.108,2.597 -0.246,3.717l9.692,5.752c0.571,0.339 0.983,0.891 1.147,1.533c0.163,0.644 0.065,1.325 -0.274,1.896l-11.897,20.005c-0.703,1.183 -2.234,1.573 -3.416,0.876l-9.798,-5.765c-1.782,1.25 -3.634,2.29 -5.598,3.144l0,10.324c0,1.381 -1.119,2.5 -2.5,2.5zm-19.5,-5l17,0l0,-9.512c0,-1.045 0.649,-1.979 1.629,-2.344c2.624,-0.975 5.004,-2.309 7.275,-4.078c0.809,-0.629 1.921,-0.702 2.804,-0.182l9.078,5.341l9.347,-15.716l-8.974,-5.325c-0.88,-0.522 -1.354,-1.528 -1.194,-2.538c0.198,-1.259 0.425,-2.923 0.425,-4.629c0,-1.314 -0.116,-2.716 -0.354,-4.284c-0.154,-1.018 0.33,-2.026 1.222,-2.541l9.061,-5.23l-9.145,-15.818l-9.208,5.241c-0.888,0.505 -1.995,0.42 -2.795,-0.219c-2.199,-1.754 -4.747,-3.196 -7.57,-4.286c-0.964,-0.372 -1.6,-1.299 -1.6,-2.332l0,-9.548l-17.001,0l0,9.548c0,1.123 -0.748,2.107 -1.83,2.408c-3.47,0.965 -5.073,2.212 -7.293,3.938l-0.098,0.076c-0.804,0.625 -1.909,0.7 -2.789,0.188l-9.243,-5.365l-9.423,15.739l8.937,5.321c0.879,0.524 1.351,1.531 1.189,2.542c-0.202,1.27 -0.435,2.943 -0.435,4.622c0,1.318 0.112,2.724 0.342,4.297c0.148,1.013 -0.337,2.015 -1.224,2.526l-9.065,5.234l9.151,15.842l9.525,-5.28c0.883,-0.49 1.976,-0.4 2.768,0.229c2.384,1.896 4.207,3.214 7.684,4.221c1.069,0.31 1.804,1.29 1.804,2.402l0,9.512zm8.186,-22.345c-9.174,0 -16.638,-7.464 -16.638,-16.638c0,-9.173 7.464,-16.636 16.638,-16.636c9.175,0 16.64,7.463 16.64,16.636c-0.001,9.174 -7.466,16.638 -16.64,16.638zm0,-28.273c-6.417,0 -11.638,5.22 -11.638,11.636c0,6.417 5.221,11.638 11.638,11.638c6.418,0 11.64,-5.221 11.64,-11.638c-0.001,-6.416 -5.222,-11.636 -11.64,-11.636z"></Path>
</Grid>
</Viewbox>
<controls:ThreeDotsGlyph Margin="13"></controls:ThreeDotsGlyph>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
@@ -63,30 +56,47 @@
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Border BorderBrush="Transparent" BorderThickness="0" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Transparent" BorderThickness="0" Grid.RowSpan="2" Grid.ColumnSpan="2">
<Border.Effect>
<BlurEffect Radius="5" KernelType="Gaussian" RenderingBias="Performance"></BlurEffect>
</Border.Effect>
<controls:ImageViewer x:Name="ImageViewer">
<controls:ImageViewer x:Name="ImageViewer"
MouseDown="TitleBar_MouseLeftButtonDown">
<controls:ImageViewer.Effect>
<effects:LightStreak Attenuation="0.4" BrightThreshold="0.9"></effects:LightStreak>
</controls:ImageViewer.Effect>
</controls:ImageViewer>
</Border>
<wcl:TitleBar x:Name="Titlebar"
Grid.ColumnSpan="2"
Background="Transparent" MouseLeftButtonDown="TitleBar_MouseLeftButtonDown"
WindowState="Normal" MinimizeButtonClicked="TitleBar_MinimizeButtonClicked"
MaximizeButtonClicked="TitleBar_MaximizeButtonClicked"
RestoreButtonClicked="TitleBar_RestoreButtonClicked"
CloseButtonClicked="TitleBar_CloseButtonClicked"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></wcl:TitleBar>
<ToggleButton Style="{StaticResource Window_SettingsButton}" Width="50" Height="30" HorizontalAlignment="Right"
Margin="0, 0, 150, 0" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Click="SettingsButton_Clicked"></ToggleButton>
<controls:OpaqueButton Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
Text="{Binding ElementName=_this, Path=CreditText, Mode=OneWay}" VerticalAlignment="Bottom"
HorizontalAlignment="Right" Margin="0, 0, 20, 30" FontSize="22" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
<controls:OpaqueButton Grid.Row="1"
Grid.Column="1"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0"
Opacity="0.6"
Text="{Binding ElementName=_this, Path=CreditText, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="0, 0, 20, 30"
FontSize="22"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Clicked="CreditTextBox_MouseLeftButtonDown" Cursor="Hand"></controls:OpaqueButton>
<WrapPanel Grid.Row="1" Margin="0, 0, 20, 10" VerticalAlignment="Bottom" HorizontalAlignment="Right">
<WrapPanel Grid.Row="1"
Grid.Column="1"
Margin="0, 0, 20, 10"
VerticalAlignment="Bottom"
HorizontalAlignment="Right">
<controls:OpaqueButton Grid.Row="1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Opacity="0.6"
Text="{Binding ElementName=_this, Path=CurrentVersionText, Mode=OneWay}" FontSize="10"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
@@ -100,9 +110,35 @@
Visibility="{Binding ElementName=_this, Path=IsRunningAsAdmin, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Clicked="VersionText_Clicked"></controls:OpaqueButton>
</WrapPanel>
<Grid x:Name="Container" Grid.Row="1">
<Grid x:Name="Container"
Grid.Column="1"
Grid.Row="1"/>
<Grid x:Name="MenuContainer"
Width="0"
Grid.RowSpan="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
Background="#A0202020">
<controls:MenuList Margin="0, 30, 0, 0"/>
</Grid>
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Active="True"></wcl:Border>
<ToggleButton Style="{StaticResource Window_SettingsButton}"
Width="50"
Height="30"
HorizontalAlignment="Left"
Margin="0, 0, 0, 0"
Grid.ColumnSpan="2"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Click="SettingsButton_Clicked"></ToggleButton>
<ToggleButton Style="{StaticResource Window_SettingsButton}"
Width="50"
Height="30"
HorizontalAlignment="Left"
Margin="0, 0, 0, 0"
Grid.ColumnSpan="2"
Foreground="White"
Visibility="{Binding ElementName=_this, Path=IsShowingDropdown, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Click="SettingsButton_Clicked"></ToggleButton>
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Grid.ColumnSpan="2" Active="True"></wcl:Border>
<controls:ChromiumBrowserWrapper x:Name="BackgroundWebView"
VerticalAlignment="Center"
HorizontalAlignment="Center"
+56 -6
View File
@@ -1,12 +1,12 @@
using Daybreak.Configuration;
using Daybreak.Services.Bloogum;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.Navigation;
using Daybreak.Services.Privilege;
using Daybreak.Services.Screenshots;
using Daybreak.Services.Updater;
using Daybreak.Services.ViewManagement;
using Daybreak.Utils;
using Daybreak.Views;
using Pepa.Wpf.Utilities;
using System;
using System.Configuration;
using System.Core.Extensions;
@@ -14,10 +14,13 @@ using System.Diagnostics;
using System.Extensions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Extensions;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
namespace Daybreak.Launch
@@ -39,11 +42,13 @@ namespace Daybreak.Launch
private readonly CancellationTokenSource cancellationToken = new();
[GenerateDependencyProperty]
private string creditText;
private string creditText = string.Empty;
[GenerateDependencyProperty]
private string currentVersionText;
private string currentVersionText = string.Empty;
[GenerateDependencyProperty]
private bool isRunningAsAdmin;
[GenerateDependencyProperty]
private bool isShowingDropdown;
public MainWindow(
IViewManager viewManager,
@@ -76,6 +81,12 @@ namespace Daybreak.Launch
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton is not MouseButton.Left &&
e.LeftButton is not MouseButtonState.Pressed)
{
return;
}
this.DragMove();
}
@@ -144,7 +155,46 @@ namespace Daybreak.Launch
private void SettingsButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<SettingsCategoryView>();
sender.As<ToggleButton>().IsEnabled = false;
var widthAnimation = new DoubleAnimation
{
From = this.IsShowingDropdown ?
this.MenuContainer.ActualWidth :
0,
To = this.IsShowingDropdown ?
0 :
300,
Duration = new Duration(TimeSpan.FromMilliseconds(200)),
DecelerationRatio = 0.7
};
var opacityAnimation = new DoubleAnimation
{
From = this.IsShowingDropdown ?
this.MenuContainer.Opacity :
0,
To = this.IsShowingDropdown ?
0 :
1,
Duration = new Duration(TimeSpan.FromMilliseconds(100)),
DecelerationRatio = 0.7
};
var storyBoard = new Storyboard();
storyBoard.Children.Add(widthAnimation);
Storyboard.SetTarget(widthAnimation, this.MenuContainer);
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Grid.WidthProperty));
storyBoard.Children.Add(opacityAnimation);
Storyboard.SetTarget(opacityAnimation, this.MenuContainer);
Storyboard.SetTargetProperty(opacityAnimation, new PropertyPath(Grid.OpacityProperty));
storyBoard.Completed += (_, _) =>
{
sender.As<ToggleButton>().IsEnabled = true;
this.IsShowingDropdown = !this.IsShowingDropdown;
};
storyBoard.Begin();
}
private void CreditTextBox_MouseLeftButtonDown(object sender, EventArgs e)
@@ -187,7 +237,7 @@ namespace Daybreak.Launch
}
else
{
this.viewManager.ShowView<MainView>();
this.viewManager.ShowView<CompanionView>();
this.PeriodicallyCheckForUpdates();
}
}
+1 -1
View File
@@ -19,6 +19,6 @@ namespace Daybreak.Models.Browser
public sealed class BrowserPayload<T> : BrowserPayload
{
[JsonProperty("Value")]
public T Value { get; set; }
public T? Value { get; set; }
}
}
@@ -9,6 +9,6 @@ namespace Daybreak.Models.Browser
[JsonProperty("Y")]
public double Y { get; set; }
[JsonProperty("Selection")]
public string Selection { get; set; }
public string? Selection { get; set; }
}
}
+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; }
}
+3 -3
View File
@@ -97,7 +97,7 @@ namespace Daybreak.Models.Builds
public static bool TryParse(int id, out Attribute attribute)
{
attribute = Attributes.Where(attr => attr.Id == id).FirstOrDefault();
attribute = Attributes.Where(attr => attr.Id == id).FirstOrDefault()!;
if (attribute is null)
{
return false;
@@ -107,7 +107,7 @@ namespace Daybreak.Models.Builds
}
public static bool TryParse(string name, out Attribute attribute)
{
attribute = Attributes.Where(attr => attr.Name == name).FirstOrDefault();
attribute = Attributes.Where(attr => attr.Name == name).FirstOrDefault()!;
if (attribute is null)
{
return false;
@@ -135,7 +135,7 @@ namespace Daybreak.Models.Builds
}
public int Id { get; private set; }
public string Name { get; private set; }
public string? Name { get; private set; }
private Attribute()
{
}
+3 -3
View File
@@ -4,10 +4,10 @@ namespace Daybreak.Models.Builds
{
public sealed class AttributeEntry : INotifyPropertyChanged
{
private Attribute attribute;
private Attribute? attribute;
private int points;
public Attribute Attribute
public Attribute? Attribute
{
get => this.attribute;
set
@@ -26,6 +26,6 @@ namespace Daybreak.Models.Builds
}
}
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ namespace Daybreak.Models.Builds
{
public sealed class Build
{
public BuildMetadata BuildMetadata { get; set; }
public BuildMetadata? BuildMetadata { get; set; }
public Profession Primary { get; set; } = Profession.None;
public Profession Secondary { get; set; } = Profession.None;
public List<AttributeEntry> Attributes { get; set; } = new();
+6 -6
View File
@@ -4,12 +4,12 @@ namespace Daybreak.Models.Builds
{
public sealed class BuildEntry : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
private Build build;
public event PropertyChangedEventHandler? PropertyChanged;
private string? name;
private Build? build;
public string PreviousName { get; set; }
public string Name
public string? PreviousName { get; set; }
public string? Name
{
get => this.name;
set
@@ -18,7 +18,7 @@ namespace Daybreak.Models.Builds
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Name)));
}
}
public Build Build
public Build? Build
{
get => this.build;
set
+2 -2
View File
@@ -4,8 +4,8 @@ namespace Daybreak.Models.Builds
{
public sealed class BuildMetadata
{
public List<int> Base64Decoded { get; set; }
public List<string> BinaryDecoded { get; set; }
public List<int>? Base64Decoded { get; set; }
public List<string>? BinaryDecoded { get; set; }
public int Header { get; set; }
public int VersionNumber { get; set; }
public int ProfessionIdLength { get; set; }
+5 -5
View File
@@ -33,7 +33,7 @@ namespace Daybreak.Models.Builds
};
public static bool TryParse(int id, out Profession profession)
{
profession = Professions.Where(prof => prof.Id == id).FirstOrDefault();
profession = Professions.Where(prof => prof.Id == id).FirstOrDefault()!;
if (profession is null)
{
return false;
@@ -43,7 +43,7 @@ namespace Daybreak.Models.Builds
}
public static bool TryParse(string name, out Profession profession)
{
profession = Professions.Where(prof => prof.Name == name).FirstOrDefault();
profession = Professions.Where(prof => prof.Name == name).FirstOrDefault()!;
if (profession is null)
{
return false;
@@ -70,10 +70,10 @@ namespace Daybreak.Models.Builds
return profession;
}
public string Name { get; private set; }
public string? Name { get; private set; }
public int Id { get; set; }
public Attribute PrimaryAttribute { get; private set; }
public List<Attribute> Attributes { get; private set; }
public Attribute? PrimaryAttribute { get; private set; }
public List<Attribute>? Attributes { get; private set; }
private Profession()
{
}
+6 -6
View File
@@ -2982,7 +2982,7 @@ namespace Daybreak.Models.Builds
public static bool TryParse(int id, out Skill skill)
{
skill = Skills.Where(skill => skill.Id == id).FirstOrDefault();
skill = Skills.Where(skill => skill.Id == id).FirstOrDefault()!;
if (skill is null)
{
return false;
@@ -2992,7 +2992,7 @@ namespace Daybreak.Models.Builds
}
public static bool TryParse(string name, out Skill skill)
{
skill = Skills.Where(skill => skill.Name == name).FirstOrDefault();
skill = Skills.Where(skill => skill.Name == name).FirstOrDefault()!;
if (skill is null)
{
return false;
@@ -3019,10 +3019,10 @@ namespace Daybreak.Models.Builds
return skill;
}
public Profession Profession { get; private set; }
public string Name { get; private set; }
public int Id { get; private set; }
public string AlternativeName { get; private set; }
public Profession? Profession { get; private set; }
public string? Name { get; private set; }
public int? Id { get; private set; }
public string? AlternativeName { get; private set; }
private Skill()
{
}
+3 -3
View File
@@ -4,8 +4,8 @@ namespace Daybreak.Models
{
public sealed class ElevationRequest
{
public object DataContext { get; set; }
public Type View { get; set; }
public string MessageToUser { get; set; }
public object? DataContext { get; set; }
public Type? View { get; set; }
public string? MessageToUser { get; set; }
}
}
+3 -3
View File
@@ -5,10 +5,10 @@ namespace Daybreak.Models.Github
public sealed class GithubRefTag
{
[JsonProperty("ref")]
public string Ref;
public string? Ref;
[JsonProperty("node_id")]
public string NodeId;
public string? NodeId;
[JsonProperty("url")]
public string Url;
public string? Url;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Daybreak.Models
public sealed class GuildwarsPath
{
[JsonProperty("path")]
public string Path { get; set; }
public string? Path { get; set; }
[JsonProperty("default")]
public bool Default { get; set; }
}
+2 -2
View File
@@ -2,7 +2,7 @@
{
public sealed class IconPayload
{
public string SkillUrl { get; set; }
public string SkillImage { get; set; }
public string? SkillUrl { get; set; }
public string? SkillImage { get; set; }
}
}
+2 -2
View File
@@ -4,8 +4,8 @@ namespace Daybreak.Models
{
public sealed class IconRequest
{
public Skill Skill { get; set; }
public string IconBase64 { get; set; }
public Skill? Skill { get; set; }
public string? IconBase64 { get; set; }
public bool Finished { get; set; }
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Daybreak.Models
{
public sealed class KeyMacro
{
public List<Keys> Keys { get; set; }
public List<Keys>? Keys { get; set; }
public Keys TargetKey { get; set; }
}
}
+4 -4
View File
@@ -5,11 +5,11 @@ namespace Daybreak.Models
{
public sealed class Log
{
public string Message { get; set; }
public string Category { get; set; }
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 string? CorrelationVector { get; set; }
public string? EventId { get; set; }
public DateTime LogTime { get; set; }
}
}
+3 -3
View File
@@ -2,9 +2,9 @@
{
public sealed class LoginCredentials
{
public string Username { get; set; }
public string Password { get; set; }
public string CharacterName { get; set; }
public string? Username { get; set; }
public string? Password { get; set; }
public string? CharacterName { get; set; }
public bool Default { get; set; }
}
}
@@ -13,7 +13,7 @@ namespace Daybreak.Models.Progress
private IconDownloadStep currentStep = StartingStep;
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public IconDownloadStep CurrentStep
{
+1 -1
View File
@@ -13,7 +13,7 @@ namespace Daybreak.Models.Progress
private UpdateStep currentStep = StartingStep;
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public UpdateStep CurrentStep
{
+3 -3
View File
@@ -5,11 +5,11 @@ namespace Daybreak.Models
public sealed class ProtectedLoginCredentials
{
[JsonProperty("ProtectedUsername")]
public string ProtectedUsername { get; set; }
public string? ProtectedUsername { get; set; }
[JsonProperty("ProtectedPassword")]
public string ProtectedPassword { get; set; }
public string? ProtectedPassword { get; set; }
[JsonProperty("CharacterName")]
public string CharacterName { get; set; }
public string? CharacterName { get; set; }
[JsonProperty("Default")]
public bool Default { get; set; }
}
+19 -18
View File
@@ -11,24 +11,24 @@ namespace Daybreak.Models
{
public static SecureString Empty { get => new(string.Empty); }
private byte[] encryptedBytes;
private byte[]? encryptedBytes;
private readonly byte[] key;
private byte[] DecryptedValue
private byte[]? DecryptedValue
{
get => this.encryptedBytes.DecryptBytes(this.key);
set => this.encryptedBytes = value.EncryptBytes(this.key);
get => this.encryptedBytes?.DecryptBytes(this.key);
set => this.encryptedBytes = value?.EncryptBytes(this.key);
}
[JsonProperty("value")]
public string Value
public string? Value
{
get
{
return this.encryptedBytes.DecryptBytes(this.key).AsString();
return this.encryptedBytes?.DecryptBytes(this.key).AsString();
}
set
{
this.encryptedBytes = value.AsBytes().EncryptBytes(this.key);
this.encryptedBytes = value?.AsBytes().EncryptBytes(this.key);
}
}
private SecureString(byte[] value)
@@ -46,26 +46,26 @@ namespace Daybreak.Models
this.Value = value;
}
public static implicit operator string(SecureString ss) => ss is null ? string.Empty : ss.Value;
public static implicit operator string?(SecureString ss) => ss is null ? string.Empty : ss.Value;
public static implicit operator SecureString(string s) => new(s);
public static SecureString operator +(SecureString ss1, SecureString ss2)
{
if (ss1 is null) throw new ArgumentNullException(nameof(ss1));
if (ss2 is null) throw new ArgumentNullException(nameof(ss2));
return new SecureString(ss1.DecryptedValue.Concat(ss2.DecryptedValue).ToArray());
return new SecureString(ss1.DecryptedValue!.Concat(ss2.DecryptedValue!).ToArray());
}
public static SecureString operator +(SecureString ss1, string s2)
{
if (ss1 is null) throw new ArgumentNullException(nameof(ss1));
return new SecureString(ss1.DecryptedValue.Concat(s2.AsBytes()).ToArray());
return new SecureString(ss1.DecryptedValue!.Concat(s2.AsBytes()).ToArray());
}
public static SecureString operator +(SecureString ss1, char c)
{
if (ss1 is null) throw new ArgumentNullException(nameof(ss1));
return new SecureString(ss1.DecryptedValue.Append(Convert.ToByte(c)).ToArray());
return new SecureString(ss1.DecryptedValue!.Append(Convert.ToByte(c)).ToArray());
}
public static bool operator ==(SecureString ss1, SecureString ss2)
{
@@ -76,12 +76,13 @@ namespace Daybreak.Models
var ss1Value = ss1.DecryptedValue;
var ss2Value = ss2.DecryptedValue;
if (ss1Value.Length != ss2Value.Length) return false;
if (ss1Value!.Length != ss2Value!.Length) return false;
for (int i = 0; i < ss1Value.Length; i++)
{
if (ss1Value[i] != ss2Value[i]) return false;
}
return true;
}
public static bool operator !=(SecureString ss1, SecureString ss2)
@@ -101,15 +102,15 @@ namespace Daybreak.Models
return !(ss1.Value == s2);
}
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (obj is string)
{
return this == (obj as string);
return this == (obj as string)!;
}
else if (obj is SecureString)
{
return this == (obj as SecureString);
return this == (obj as SecureString)!;
}
else
{
@@ -119,12 +120,12 @@ namespace Daybreak.Models
public override int GetHashCode()
{
return this.Value.GetHashCode();
return this.Value!.GetHashCode();
}
public override string ToString()
{
return this.Value;
return this.Value!;
}
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Daybreak.Models;
public sealed class SynchronizationBuild
{
public string? Name { get; set; }
public string? TemplateCode { get; set; }
public bool Changed { get; set; }
}
+2 -2
View File
@@ -58,7 +58,7 @@ namespace Daybreak.Models.Versioning
{
if (TryParseParts(version, out var parts, out var hasPrefix) is false)
{
parsedVersion = null;
parsedVersion = default!;
return false;
}
@@ -85,7 +85,7 @@ namespace Daybreak.Models.Versioning
private static bool TryParseParts(string version, out List<VersionToken> parts, out bool hasPrefix)
{
hasPrefix = false;
parts = null;
parts = default!;
if (version.IsNullOrWhiteSpace())
{
return false;
@@ -3,10 +3,10 @@ using Daybreak.Exceptions;
using Daybreak.Services.Credentials;
using Daybreak.Services.Mutex;
using Daybreak.Services.Privilege;
using Daybreak.Utils;
using Daybreak.Views;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using Pepa.Wpf.Utilities;
using System;
using System.Collections.Generic;
using System.Configuration;
@@ -65,14 +65,14 @@ namespace Daybreak.Services.ApplicationLauncher
{
if (this.privilegeManager.AdminPrivileges is false)
{
this.privilegeManager.RequestAdminPrivileges<MainView>("You need administrator rights in order to start using multi-launch");
this.privilegeManager.RequestAdminPrivileges<CompanionView>("You need administrator rights in order to start using multi-launch");
return false;
}
this.ClearGwLocks();
}
return await this.LaunchGuildwarsProcess(credentials.Username, credentials.Password, credentials.CharacterName);
return await this.LaunchGuildwarsProcess(credentials.Username!, credentials.Password!, credentials.CharacterName!);
},
onNone: () =>
{
@@ -162,12 +162,12 @@ namespace Daybreak.Services.ApplicationLauncher
"-email",
email,
"-password",
password
password!
};
if (!string.IsNullOrWhiteSpace(character))
{
args.Add("-character");
args.Add(character);
args.Add($"\"character\"");
}
var identity = this.liveOptions.Value.ExperimentalFeatures.LaunchGuildwarsAsCurrentUser ?
@@ -202,7 +202,7 @@ namespace Daybreak.Services.ApplicationLauncher
throw new InvalidOperationException("Newly launched gw process not detected");
}
if (gwProcess.MainWindowHandle == IntPtr.Zero)
if (gwProcess!.MainWindowHandle == IntPtr.Zero)
{
continue;
}
@@ -232,7 +232,7 @@ namespace Daybreak.Services.ApplicationLauncher
return false;
}
return Process.GetProcessesByName(ProcessName).Where(process => string.Equals(path.Path, process.MainModule.FileName, StringComparison.Ordinal)).Any();
return Process.GetProcessesByName(ProcessName).Where(process => string.Equals(path.Path, process.MainModule!.FileName, StringComparison.Ordinal)).Any();
}
catch
{
@@ -264,8 +264,8 @@ namespace Daybreak.Services.ApplicationLauncher
try
{
var registryKey = GetGuildwarsRegistryKey(true);
registryKey.SetValue("Path", gamePath);
registryKey.SetValue("Src", gamePath);
registryKey.SetValue("Path", gamePath!);
registryKey.SetValue("Src", gamePath!);
registryKey.Close();
}
catch (SecurityException ex)
@@ -44,7 +44,6 @@ namespace Daybreak.Services.BuildTemplates
var emptyBuild = new Build();
var name = Guid.NewGuid().ToString();
var entry = new BuildEntry { Build = emptyBuild, Name = name, PreviousName = string.Empty };
this.SaveBuild(entry);
return entry;
}
@@ -52,19 +51,24 @@ namespace Daybreak.Services.BuildTemplates
{
var emptyBuild = new Build();
var entry = new BuildEntry { Build = emptyBuild, Name = name, PreviousName = string.Empty };
this.SaveBuild(entry);
return entry;
}
public void SaveBuild(BuildEntry buildEntry)
{
var encodedBuild = this.EncodeTemplate(buildEntry.Build);
if (string.IsNullOrWhiteSpace(buildEntry.PreviousName))
var encodedBuild = this.EncodeTemplate(buildEntry.Build!);
var newPath = Path.Combine(BuildsPath, $"{buildEntry.Name}.txt");
if (string.IsNullOrWhiteSpace(buildEntry.PreviousName) is false)
{
File.Delete($"{BuildsPath}\\{buildEntry.PreviousName}.txt");
var oldPath = Path.GetFullPath(Path.Combine(BuildsPath, $"{buildEntry.PreviousName}.txt"));
if (File.Exists(oldPath))
{
File.Delete(oldPath);
}
}
File.WriteAllText($"{BuildsPath}\\{buildEntry.Name}.txt", encodedBuild);
Directory.CreateDirectory(Path.GetDirectoryName(newPath)!);
File.WriteAllText(newPath, encodedBuild);
}
public void RemoveBuild(BuildEntry buildEntry)
@@ -80,9 +84,26 @@ namespace Daybreak.Services.BuildTemplates
}
}
public async Task<Result<BuildEntry, Exception>> GetBuild(string name)
{
var path = Path.Combine(BuildsPath, $"{name}.txt");
if (File.Exists(path) is false)
{
return new InvalidOperationException("Unable to find build file");
}
var content = await File.ReadAllTextAsync(path);
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))
foreach(var file in Directory.GetFiles(BuildsPath, "*.txt", SearchOption.AllDirectories))
{
File.Delete(file);
}
@@ -90,7 +111,12 @@ namespace Daybreak.Services.BuildTemplates
public async IAsyncEnumerable<BuildEntry> GetBuilds()
{
foreach (var file in Directory.GetFiles(BuildsPath))
if (Directory.Exists(BuildsPath) is false)
{
yield break;
}
foreach (var file in Directory.GetFiles(BuildsPath, "*.txt", SearchOption.AllDirectories))
{
var maybeBuild = this.DecodeTemplateInner(await File.ReadAllTextAsync(file));
var build = maybeBuild.Switch(
@@ -98,12 +124,12 @@ namespace Daybreak.Services.BuildTemplates
onFailure: exception =>
{
this.logger.LogError(exception, "Failed to parse build");
return default;
return default!;
});
if (build is not null)
{
yield return new BuildEntry { Build = build, Name = Path.GetFileNameWithoutExtension(file), PreviousName = Path.GetFileNameWithoutExtension(file) };
yield return new BuildEntry { Build = build, Name = Path.GetRelativePath(BuildsPath, file).Replace(".txt", string.Empty), PreviousName = Path.GetRelativePath(BuildsPath, file).Replace(".txt", string.Empty) };
}
}
}
@@ -121,7 +147,7 @@ namespace Daybreak.Services.BuildTemplates
var maybeBuild = this.DecodeTemplateInner(template);
(var result, var parsedBuild) = maybeBuild.Switch(
onSuccess: parsedBuild => (true, parsedBuild),
onFailure: _ => (false, null));
onFailure: _ => (false, default!));
build = parsedBuild;
return result;
@@ -199,9 +225,9 @@ namespace Daybreak.Services.BuildTemplates
PrimaryProfessionId = build.Primary.Id,
SecondaryProfessionId = build.Secondary.Id,
AttributeCount = build.Attributes.Count,
AttributesIds = build.Attributes.Select(attrEntry => attrEntry.Attribute.Id).ToList(),
AttributesIds = build.Attributes.Select(attrEntry => attrEntry.Attribute!.Id).ToList(),
AttributePoints = build.Attributes.Select(attrEntry => attrEntry.Points).ToList(),
SkillIds = build.Skills.Select(skill => skill.Id).ToList(),
SkillIds = build.Skills.Select(skill => skill.Id!.Value).ToList(),
TailPresent = true
};
@@ -260,7 +286,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);
@@ -297,7 +323,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
{
@@ -12,6 +15,7 @@ namespace Daybreak.Services.BuildTemplates
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);
@@ -14,7 +14,7 @@ namespace Daybreak.Services.Configuration
private ApplicationConfiguration applicationConfiguration;
private readonly ILogger<ConfigurationManager> logger;
public event EventHandler ConfigurationChanged;
public event EventHandler? ConfigurationChanged;
public ConfigurationManager(ILogger<ConfigurationManager> logger)
{
@@ -87,8 +87,8 @@ namespace Daybreak.Services.Credentials
{
try
{
var usrbytes = Convert.FromBase64String(protectedLoginCredentials.ProtectedUsername);
var psdBytes = Convert.FromBase64String(protectedLoginCredentials.ProtectedPassword);
var usrbytes = Convert.FromBase64String(protectedLoginCredentials.ProtectedUsername!);
var psdBytes = Convert.FromBase64String(protectedLoginCredentials.ProtectedPassword!);
return new LoginCredentials
{
Username = Encoding.UTF8.GetString(ProtectedData.Unprotect(usrbytes, Entropy, DataProtectionScope.LocalMachine)),
@@ -107,8 +107,8 @@ namespace Daybreak.Services.Credentials
{
try
{
var usrBytes = Encoding.UTF8.GetBytes(loginCredentials.Username);
var psdBytes = Encoding.UTF8.GetBytes(loginCredentials.Password);
var usrBytes = Encoding.UTF8.GetBytes(loginCredentials.Username!);
var psdBytes = Encoding.UTF8.GetBytes(loginCredentials.Password!);
return new ProtectedLoginCredentials
{
ProtectedUsername = Convert.ToBase64String(ProtectedData.Protect(usrBytes, Entropy, DataProtectionScope.LocalMachine)),
+232 -65
View File
@@ -3,19 +3,19 @@ using Daybreak.Controls;
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Graph.Models;
using Daybreak.Services.ViewManagement;
using Daybreak.Services.Navigation;
using Daybreak.Views;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
@@ -27,8 +27,8 @@ 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 Scopes = "Files.Read Files.Read.All Files.ReadWrite Files.ReadWrite.All User.Read offline_access";
private const string RedirectUri = "http://localhost:42111";
private const string QueryStateKey = "state";
private const string QueryCodeKey = "code";
@@ -36,17 +36,17 @@ public sealed class GraphClient : IGraphClient
private const string RedirectUriPlaceholder = "[RedirectUri]";
private const string ScopesPlaceholder = "[Scopes]";
private const string StatePlaceholder = "[State]";
private const string RefreshTokenPlaceholder = "[RefreshToken]";
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 RefreshTokenUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/token?client_id={ClientIdPlaceholder}&refresh_token={RefreshTokenPlaceholder}";
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/{BackupFileName}";
private const string SyncFileUri = $"me/drive/root:/Daybreak/Builds/daybreak.json{ContentSuffix}";
private const string ContentSuffix = ":/content";
private const string BackupFileName = "daybreak_backup.json";
private static readonly byte[] Entropy = Convert.FromBase64String("R3VpbGR3YXJz");
private static readonly string ApplicationId = SecretManager.GetSecret(SecretKeys.AadApplicationId);
private static readonly string TenantId = SecretManager.GetSecret(SecretKeys.AadTenantId);
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IViewManager viewManager;
@@ -54,6 +54,8 @@ public sealed class GraphClient : IGraphClient
private readonly IHttpClient<GraphClient> httpClient;
private readonly ILogger<GraphClient> logger;
private List<BuildFile>? buildsCache;
public GraphClient(
IBuildTemplateManager buildTemplateManager,
IViewManager viewManager,
@@ -75,7 +77,7 @@ public sealed class GraphClient : IGraphClient
ChromiumBrowserWrapper chromiumBrowserWrapper,
CancellationToken cancellationToken = default)
{
var maybeAuthCode = await this.RetrieveAuthorizationCode(chromiumBrowserWrapper, cancellationToken);
var maybeAuthCode = await RetrieveAuthorizationCode(chromiumBrowserWrapper, cancellationToken);
if (maybeAuthCode.TryExtractSuccess(out var authCode) is false)
{
return maybeAuthCode.SwitchAny(onFailure: exception => exception);
@@ -86,13 +88,22 @@ public sealed class GraphClient : IGraphClient
return maybeAccessToken.Switch(
onSuccess: token =>
{
var accessToken = AccessToken.FromTokenResponse(token);
this.SaveAccessToken(accessToken);
this.SaveTokenResponse(token);
return true;
},
onFailure: exception => exception);
}
public Task<Result<bool, Exception>> LogOut()
{
this.ResetAccessToken();
this.ResetRefreshToken();
this.ResetAuthorization();
//TODO: Currently revoking only one refresh token is not supported. Follow up when MS Graph implements refresh token revocation.
return Task.FromResult(Result<bool, Exception>.Success(true));
}
public async Task<Result<User, Exception>> GetUserProfile<TViewType>()
where TViewType : UserControl
{
@@ -105,8 +116,15 @@ public sealed class GraphClient : IGraphClient
if (DateTime.Now > accessToken.ExpirationDate)
{
this.viewManager.ShowView<GraphAuthorizationView>(new ViewRedirectContext { CallingView = typeof(TViewType) });
return new InvalidOperationException("Client authorization expired");
var maybeToken = await this.RefreshAccessToken();
if (maybeToken.ExtractValue() is not TokenResponse tokenResponse)
{
this.viewManager.ShowView<GraphAuthorizationView>(new ViewRedirectContext { CallingView = typeof(TViewType) });
return new InvalidOperationException("Client authorization expired");
}
(var newAccessToken, _) = this.SaveTokenResponse(tokenResponse);
accessToken = newAccessToken;
}
this.httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token);
@@ -120,7 +138,7 @@ public sealed class GraphClient : IGraphClient
try
{
var profile = JsonConvert.DeserializeObject<User>(await response.Content.ReadAsStringAsync());
return profile;
return profile!;
}
catch(Exception e)
{
@@ -130,24 +148,29 @@ public sealed class GraphClient : IGraphClient
public async Task<Result<bool, Exception>> UploadBuilds()
{
var compiledBuilds = await this.buildTemplateManager.GetBuilds().Select(b => new BuildFile { FileName = b.Name, TemplateCode = this.buildTemplateManager.EncodeTemplate(b.Build) }).ToListAsync();
var serializedBuilds = JsonConvert.SerializeObject(compiledBuilds);
await this.UploadBackupItem(serializedBuilds);
return true;
var builds = await this.buildTemplateManager.GetBuilds().ToListAsync();
return await this.PutBuilds(builds);
}
public async Task<Result<bool, Exception>> DownloadBuilds()
{
var maybeBuilds = await this.RetrieveBuildsList();
if (maybeBuilds.TryExtractSuccess(out var builds) is false)
var retrieveBuildsResponse = await this.RetrieveBuildsList();
if (retrieveBuildsResponse.TryExtractFailure(out var failure))
{
return maybeBuilds.SwitchAny(onFailure: exception => exception);
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();
_ = builds.Select(buildFile =>
var compiledBuilds = this.buildsCache?.Select(buildFile =>
{
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode, out var build) is false)
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode!, out var build) is false)
{
return null;
}
@@ -158,82 +181,171 @@ public sealed class GraphClient : IGraphClient
Name = buildFile.FileName,
PreviousName = buildFile.FileName
};
}).Where(entry => entry is not null)
.Do(this.buildTemplateManager.SaveBuild).ToList();
}).Where(entry => entry is not null).ToList();
_ = compiledBuilds.Do(this.buildTemplateManager.SaveBuild!).ToList();
return true;
}
public async Task<Result<List<BuildFile>, Exception>> RetrieveBuildsList()
public async Task<Result<bool, Exception>> DownloadBuild(string buildName)
{
var maybeCompiledBuilds = await this.GetBackupItemContent();
if (maybeCompiledBuilds.ExtractValue() is not string compiledBuilds)
var retrieveBuildsResponse = await this.RetrieveBuildsList();
if (retrieveBuildsResponse.TryExtractFailure(out var failure))
{
return new InvalidOperationException("No backup found");
this.logger.LogError(failure, "Unable to download builds");
return false;
}
var builds = JsonConvert.DeserializeObject<List<BuildFile>>(compiledBuilds);
return builds;
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<DateTime, Exception>> GetLastUpdateTime()
public async Task<Result<bool, Exception>> UploadBuild(string buildName)
{
var maybeDriveItem = await this.GetDriveItem();
if (maybeDriveItem.ExtractValue() is not DriveItem driveItem)
var getBuildResult = await this.buildTemplateManager.GetBuild(buildName);
if (getBuildResult.TryExtractSuccess(out var buildEntry) is false)
{
return new InvalidOperationException("Unable to retrieve the drive file");
return getBuildResult.SwitchAny(
onFailure: exception => exception);
}
return driveItem.LastModifiedDateTime;
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();
this.ResetRefreshToken();
}
private async Task<Optional<string>> GetBackupItemContent()
private async Task<Optional<TokenResponse>> RefreshAccessToken()
{
var maybeDriveItem = await this.GetDriveItem();
if (maybeDriveItem.ExtractValue() is not DriveItem driveItem)
var maybeRefreshToken = this.LoadRefreshToken();
if (maybeRefreshToken.ExtractValue() is not RefreshToken refreshToken)
{
return Optional.None<string>();
return Optional.None<TokenResponse>();
}
if (driveItem.Name != BackupFileName)
using var httpContent = new FormUrlEncodedContent(new Dictionary<string, string>
{
return Optional.None<string>();
}
var response = await this.httpClient.GetAsync(driveItem.DownloadUrl);
{ "client_id", ApplicationId },
{ "refresh_token", refreshToken.Token! },
{ "grant_type", "refresh_token" }
});
using var response = await this.httpClient.PostAsync(TokenUrlPlaceholder, httpContent);
if (response.IsSuccessStatusCode is false)
{
return Optional.None<string>();
return Optional.None<TokenResponse>();
}
return await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync())!;
}
private async Task<bool> UploadBackupItem(string serializedBuilds)
private async Task<bool> PutBuild(BuildEntry buildEntry)
{
using var stringContent = new StringContent(serializedBuilds);
var response = await this.httpClient.PutAsync(SyncFileUri + ContentSuffix, stringContent);
return response.IsSuccessStatusCode;
}
if (this.buildsCache is null)
{
_ = await this.RetrieveBuildsList();
}
private async Task<Optional<DriveItem>> GetDriveItem()
{
var response = await this.httpClient.GetAsync(SyncFileUri);
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 Optional.None<DriveItem>();
return false;
}
var driveItemContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<DriveItem>(driveItemContent);
this.buildsCache = buildList;
return true;
}
private async Task<Result<string, Exception>> RetrieveAuthorizationCode(
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)
{
@@ -251,18 +363,29 @@ public sealed class GraphClient : IGraphClient
.Replace(' ', '+'))
.Replace(StatePlaceholder, state);
while (chromiumBrowserWrapper.Address.StartsWith(RedirectUri) is false)
NameValueCollection query = default!;
bool finished = false;
chromiumBrowserWrapper.WebBrowser.CoreWebView2.SourceChanged += (_, sourceArgs) =>
{
if (chromiumBrowserWrapper.Address.StartsWith(RedirectUri))
{
query = HttpUtility.ParseQueryString(chromiumBrowserWrapper.Address.Split('?').Skip(1).FirstOrDefault()!);
finished = true;
chromiumBrowserWrapper.WebBrowser.CoreWebView2.NavigateToString(string.Empty);
}
};
while (finished is false)
{
if (cancellationToken.IsCancellationRequested)
{
return new TaskCanceledException();
}
await Task.Delay(1000).ConfigureAwait(true);
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)
if (query?.GetValues(QueryStateKey) is string[] states is false)
{
throw new InvalidOperationException("Response doesn't have state key in response");
}
@@ -316,7 +439,17 @@ public sealed class GraphClient : IGraphClient
return new InvalidOperationException($"Invalid access token response. Status code [{response.StatusCode}]");
}
return JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync());
return JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync())!;
}
private (AccessToken, RefreshToken) SaveTokenResponse(TokenResponse token)
{
var accessToken = AccessToken.FromTokenResponse(token);
var refreshToken = RefreshToken.FromTokenResponse(token);
this.SaveAccessToken(accessToken);
this.SaveRefreshToken(refreshToken);
return (accessToken, refreshToken);
}
private void ResetAccessToken()
@@ -332,6 +465,19 @@ public sealed class GraphClient : IGraphClient
this.liveUpdateableOptions.UpdateOption();
}
private void SaveRefreshToken(RefreshToken refreshToken)
{
var codeBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(refreshToken));
this.liveUpdateableOptions.Value.ProtectedGraphRefreshToken = Convert.ToBase64String(ProtectedData.Protect(codeBytes, Entropy, DataProtectionScope.CurrentUser));
this.liveUpdateableOptions.UpdateOption();
}
private void ResetRefreshToken()
{
this.liveUpdateableOptions.Value.ProtectedGraphRefreshToken = null;
this.liveUpdateableOptions.UpdateOption();
}
private Optional<AccessToken> LoadAccessToken()
{
var protectedCode = this.liveUpdateableOptions.Value.ProtectedGraphAccessToken;
@@ -340,10 +486,10 @@ public sealed class GraphClient : IGraphClient
return Optional.None<AccessToken>();
}
var codeBytes = ProtectedData.Unprotect(Convert.FromBase64String(protectedCode), Entropy, DataProtectionScope.CurrentUser);
var codeBytes = ProtectedData.Unprotect(Convert.FromBase64String(protectedCode!), Entropy, DataProtectionScope.CurrentUser);
try
{
return JsonConvert.DeserializeObject<AccessToken>(Encoding.UTF8.GetString(codeBytes));
return JsonConvert.DeserializeObject<AccessToken>(Encoding.UTF8.GetString(codeBytes))!;
}
catch(Exception e)
{
@@ -353,6 +499,27 @@ public sealed class GraphClient : IGraphClient
}
}
private Optional<RefreshToken> LoadRefreshToken()
{
var protectedCode = this.liveUpdateableOptions.Value.ProtectedGraphRefreshToken;
if (protectedCode.IsNullOrWhiteSpace())
{
return Optional.None<RefreshToken>();
}
var codeBytes = ProtectedData.Unprotect(Convert.FromBase64String(protectedCode!), Entropy, DataProtectionScope.CurrentUser);
try
{
return JsonConvert.DeserializeObject<RefreshToken>(Encoding.UTF8.GetString(codeBytes))!;
}
catch (Exception e)
{
this.logger.LogError(e, "Failed to load refresh token. Resetting refresh token");
this.ResetAccessToken();
return Optional.None<RefreshToken>();
}
}
private static string GetNewState()
{
return Guid.NewGuid().ToString();
+4 -2
View File
@@ -14,9 +14,11 @@ 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>> LogOut();
Task<Result<bool, Exception>> UploadBuilds();
Task<Result<bool, Exception>> DownloadBuilds();
Task<Result<List<BuildFile>, Exception>> RetrieveBuildsList();
Task<Result<DateTime, Exception>> GetLastUpdateTime();
Task<Result<bool, Exception>> UploadBuild(string buildName);
Task<Result<bool, Exception>> DownloadBuild(string buildName);
Task<Result<IEnumerable<BuildFile>, Exception>> RetrieveBuildsList();
void ResetAuthorization();
}
@@ -4,7 +4,7 @@ namespace Daybreak.Services.Graph.Models;
public sealed class AccessToken
{
public string Token { get; set; }
public string? Token { get; set; }
public DateTime ExpirationDate { get; set; }
public static AccessToken FromTokenResponse(TokenResponse tokenResponse)
+2 -2
View File
@@ -2,6 +2,6 @@
public sealed class BuildFile
{
public string TemplateCode { get; set; }
public string FileName { get; set; }
public string? TemplateCode { get; set; }
public string? FileName { get; set; }
}
@@ -3,12 +3,12 @@ using System;
namespace Daybreak.Services.Graph.Models;
public sealed class DriveItem
public sealed class FileItem
{
[JsonProperty("@microsoft.graph.downloadUrl")]
public string DownloadUrl { get; set; }
public string? DownloadUrl { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
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,14 @@
namespace Daybreak.Services.Graph.Models;
public sealed class RefreshToken
{
public string? Token { get; set; }
public static RefreshToken FromTokenResponse(TokenResponse tokenResponse)
{
return new RefreshToken
{
Token = tokenResponse.RefreshToken,
};
}
}
@@ -5,13 +5,13 @@ namespace Daybreak.Services.Graph.Models;
public sealed class TokenResponse
{
[JsonProperty("token_type")]
public string TokenType { get; set; }
public string? TokenType { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
public string? Scope { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("access_token")]
public string AccessToken { get; set; }
public string? AccessToken { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
public string? RefreshToken { get; set; }
}
+2 -2
View File
@@ -5,8 +5,8 @@ namespace Daybreak.Services.Graph.Models;
public sealed class User
{
[JsonProperty("displayName")]
public string DisplayName { get; set; }
public string? DisplayName { get; set; }
[JsonProperty("mail")]
public string Email { get; set; }
public string? Email { get; set; }
}
@@ -4,5 +4,5 @@ namespace Daybreak.Services.Graph.Models;
public sealed class ViewRedirectContext
{
public Type CallingView { get; set; }
public Type? CallingView { get; set; }
}
@@ -34,7 +34,7 @@ namespace Daybreak.Services.IconRetrieve
private readonly ConcurrentQueue<IconRequest> iconRequests = new();
private readonly ILogger<IconBrowser> logger;
private ChromiumBrowserWrapper browserWrapper;
private ChromiumBrowserWrapper? browserWrapper;
private CancellationToken cancellationToken;
public IconBrowser(
@@ -101,9 +101,9 @@ namespace Daybreak.Services.IconRetrieve
this.logger.LogError(e, "Encountered error when initializing browser");
}
var curedSkillName = request.Skill.AlternativeName.IsNullOrWhiteSpace() ?
request.Skill.Name.Replace(" ", "_") :
request.Skill.AlternativeName.Replace(" ", "_");
var curedSkillName = request.Skill!.AlternativeName.IsNullOrWhiteSpace() ?
request.Skill.Name!.Replace(" ", "_") :
request.Skill.AlternativeName!.Replace(" ", "_");
var skillIconUrl = $"{BaseUrl}/{QueryUrl.Replace(NamePlaceholder, curedSkillName)}";
logger.LogInformation($"Looking for icon at {skillIconUrl}");
@@ -138,7 +138,7 @@ namespace Daybreak.Services.IconRetrieve
continue;
}
var potentialBase64 = iconPayload.SkillImage.Split(',').Skip(1).FirstOrDefault();
var potentialBase64 = iconPayload.SkillImage!.Split(',').Skip(1).FirstOrDefault();
if (potentialBase64 == FaultyBase64 ||
potentialBase64 == LargeFaultyBase64)
{
@@ -150,7 +150,7 @@ namespace Daybreak.Services.IconRetrieve
byte[] bytes;
try
{
bytes = Convert.FromBase64String(potentialBase64);
bytes = Convert.FromBase64String(potentialBase64!);
}
catch
{
@@ -160,7 +160,7 @@ namespace Daybreak.Services.IconRetrieve
}
await SaveIconLocally(request.Skill, bytes);
request.IconBase64 = potentialBase64;
request.IconBase64 = potentialBase64!;
request.Finished = true;
break;
}
@@ -171,11 +171,11 @@ namespace Daybreak.Services.IconRetrieve
private static async Task<string> SaveIconLocally(Skill skill, byte[] data)
{
var curedSkillName = skill.Name
var curedSkillName = skill.Name?
.Replace(" ", "_")
.Replace("\"", "");
await File.WriteAllBytesAsync(IconsLocation.Replace(NamePlaceholder, curedSkillName), data);
return curedSkillName;
return curedSkillName!;
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ namespace Daybreak.Services.IconRetrieve
private Optional<Uri> GetLocalIcon(Skill skill)
{
var curedSkillName = skill.Name
var curedSkillName = skill.Name?
.Replace(" ", "_")
.Replace("\"", "");
this.logger.LogInformation("Checking local icon cache");
@@ -28,9 +28,9 @@ namespace Daybreak.Services.IconRetrieve
private readonly ILogger<IconDownloader> logger;
private readonly ILiveOptions<ApplicationConfiguration> liveOptions;
private readonly ILogger<ChromiumBrowserWrapper> browserLogger;
private ChromiumBrowserWrapper browserWrapper;
private CancellationTokenSource cancellationTokenSource;
private IconDownloadStatus iconDownloadStatus;
private ChromiumBrowserWrapper? browserWrapper;
private CancellationTokenSource? cancellationTokenSource;
private IconDownloadStatus? iconDownloadStatus;
public bool DownloadComplete { get; private set; }
public bool Downloading => this.cancellationTokenSource is not null;
@@ -73,7 +73,7 @@ namespace Daybreak.Services.IconRetrieve
if (this.Downloading)
{
this.logger.LogInformation("Download already running");
return this.iconDownloadStatus;
return this.iconDownloadStatus!;
}
this.cancellationTokenSource = new();
@@ -86,7 +86,7 @@ namespace Daybreak.Services.IconRetrieve
{
this.cancellationTokenSource?.Cancel();
this.cancellationTokenSource?.Dispose();
this.cancellationTokenSource = null;
this.cancellationTokenSource = default!;
}
private async Task DownloadIcons()
@@ -95,7 +95,7 @@ namespace Daybreak.Services.IconRetrieve
if (await TestBrowserSupported() is false)
{
this.logger.LogError("Browser not supported. Icon downloading stopped");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.BrowserNotSupported;
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.BrowserNotSupported;
return;
}
@@ -111,7 +111,7 @@ namespace Daybreak.Services.IconRetrieve
var logger = this.logger.CreateScopedLogger(nameof(this.DownloadIcons), skill.Name);
logger.LogInformation("Verifying if icon exists");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Checking(skill.Name, progressValue);
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.Checking(skill.Name!, progressValue);
if ((await this.iconCache.GetIconUri(skill)).ExtractValue() is not null)
{
progressValue += progressIncrement;
@@ -125,18 +125,18 @@ namespace Daybreak.Services.IconRetrieve
if (skillsToDownload.Count == 0)
{
this.logger.LogInformation("No icons missing. Stopping download");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Finished;
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.Finished;
return;
}
this.iconBrowser.InitializeWebView(this.browserWrapper, this.cancellationTokenSource.Token);
this.iconBrowser.InitializeWebView(this.browserWrapper!, this.cancellationTokenSource!.Token);
var incomplete = false;
foreach (var skill in skillsToDownload)
{
if (this.cancellationTokenSource?.IsCancellationRequested is null or true)
{
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Stopped(progressValue);
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.Stopped(progressValue);
return;
}
@@ -147,7 +147,7 @@ namespace Daybreak.Services.IconRetrieve
}
this.logger.LogInformation("Downloading icon");
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Downloading(skill.Name, progressValue);
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.Downloading(skill.Name!, progressValue);
var request = new IconRequest { Skill = skill };
this.iconBrowser.QueueIconRequest(request);
@@ -178,10 +178,10 @@ namespace Daybreak.Services.IconRetrieve
{
Application.Current.Dispatcher.Invoke(() =>
{
this.browserWrapper.IsEnabled = false;
this.browserWrapper!.IsEnabled = false;
this.browserWrapper.WebBrowser.Dispose();
});
this.iconDownloadStatus.CurrentStep = IconDownloadStatus.Finished;
this.iconDownloadStatus!.CurrentStep = IconDownloadStatus.Finished;
this.DownloadComplete = true;
}
}
@@ -1,11 +0,0 @@
using Daybreak.Models;
using System;
using System.Windows.Extensions.Services;
namespace Daybreak.Services.KeyboardHook
{
public interface IKeyboardHookService : IApplicationLifetimeService
{
event EventHandler<KeyboardHookEventArgs> KeyboardPressed;
}
}
@@ -1,116 +0,0 @@
using Daybreak.Models;
using Microsoft.Extensions.Logging;
using Pepa.Wpf.Utilities;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Extensions;
using System.Linq;
using System.Runtime.InteropServices;
namespace Daybreak.Services.KeyboardHook
{
// Based on https://gist.github.com/Stasonix
// https://stackoverflow.com/questions/604410/global-keyboard-capture-in-c-sharp-application
public sealed class KeyboardHookService : IKeyboardHookService, IDisposable
{
private readonly ILogger<KeyboardHookService> logger;
private IntPtr windowsHookHandle;
private IntPtr user32LibraryHandle;
private NativeMethods.HookProc hookProc;
public event EventHandler<KeyboardHookEventArgs> KeyboardPressed;
public KeyboardHookService(
ILogger<KeyboardHookService> logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
this.Setup();
}
public void OnStartup()
{
}
public void OnClosing()
{
this.Dispose();
}
private void Setup()
{
this.windowsHookHandle = IntPtr.Zero;
this.hookProc = this.LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.
this.user32LibraryHandle = NativeMethods.LoadLibrary("User32");
if (this.user32LibraryHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
this.logger.LogError($"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
this.windowsHookHandle = NativeMethods.SetWindowsHookEx(NativeMethods.WH_KEYBOARD_LL, this.hookProc, this.user32LibraryHandle, 0);
if (this.windowsHookHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
this.logger.LogError($"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
}
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
var handled = false;
var wparamTyped = wParam.ToInt32();
if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
{
var p = Marshal.PtrToStructure(lParam, typeof(KeyboardInput)).Cast<KeyboardInput>();
var eventArguments = new KeyboardHookEventArgs(wparamTyped.Cast<KeyboardState>(), p);
this.KeyboardPressed?.Invoke(this, eventArguments);
handled = eventArguments.Handled;
}
return handled ? (IntPtr)1 : NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
private void Dispose(bool disposing)
{
if (disposing)
{
// because we can unhook only in the same thread, not in garbage collector thread
if (this.windowsHookHandle != IntPtr.Zero)
{
if (NativeMethods.UnhookWindowsHookEx(this.windowsHookHandle) is false)
{
int errorCode = Marshal.GetLastWin32Error();
this.logger.LogCritical($"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
this.windowsHookHandle = IntPtr.Zero;
this.hookProc -= this.LowLevelKeyboardProc;
}
}
if (this.user32LibraryHandle != IntPtr.Zero)
{
if (NativeMethods.FreeLibrary(this.user32LibraryHandle) is false) // reduces reference to library by 1.
{
int errorCode = Marshal.GetLastWin32Error();
this.logger.LogCritical($"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
this.user32LibraryHandle = IntPtr.Zero;
}
}
~KeyboardHookService()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
}
}
@@ -1,8 +0,0 @@
using System.Windows.Extensions.Services;
namespace Daybreak.Services.KeyboardMacros
{
public interface IMacroService : IApplicationLifetimeService
{
}
}
@@ -1,106 +0,0 @@
using Daybreak.Configuration;
using Daybreak.Models;
using Daybreak.Services.KeyboardHook;
using Pepa.Wpf.Utilities;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Extensions;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Daybreak.Services.KeyboardMacros
{
public sealed class MacroService : IMacroService
{
private readonly CancellationTokenSource cancellationTokenSource = new();
private readonly IKeyboardHookService keyboardHookService;
private readonly ILiveOptions<ApplicationConfiguration> liveOptions;
private readonly HashSet<Keys> KeysDown = new();
private bool gameActive;
private IntPtr gwWindowHwnd;
public MacroService(
IKeyboardHookService keyboardHookService,
ILiveOptions<ApplicationConfiguration> liveOptions)
{
this.keyboardHookService = keyboardHookService.ThrowIfNull(nameof(keyboardHookService));
this.liveOptions = liveOptions.ThrowIfNull(nameof(liveOptions));
this.keyboardHookService.KeyboardPressed += this.KeyboardHookService_KeyboardPressed;
this.SetupGameActiveChecker();
}
private void SetupGameActiveChecker()
{
TaskExtensions.RunPeriodicAsync(() =>
{
var windowHandle = NativeMethods.GetForegroundWindow();
var windowNameLength = NativeMethods.GetWindowTextLength(windowHandle);
var sb = new StringBuilder(windowNameLength);
_ = NativeMethods.GetWindowText(windowHandle, sb, windowNameLength + 1);
if (sb.ToString() != "Guild Wars")
{
this.gameActive = false;
return;
}
this.gwWindowHwnd = windowHandle;
this.gameActive = true;
},
TimeSpan.Zero,
TimeSpan.FromMilliseconds(33),
this.cancellationTokenSource.Token);
}
private void KeyboardHookService_KeyboardPressed(object sender, KeyboardHookEventArgs e)
{
if (this.gameActive && this.liveOptions.Value.ExperimentalFeatures.CanInterceptKeys)
{
if (e.KeyboardState == KeyboardState.KeyDown)
{
this.KeysDown.Add(e.KeyboardInput.Key);
}
else if (e.KeyboardState == KeyboardState.KeyUp)
{
this.KeysDown.Remove(e.KeyboardInput.Key);
return;
}
e.Handled = this.liveOptions.Value.ExperimentalFeatures.Macros
.Where(keyMacro => MacroContainsKey(keyMacro, e.KeyboardInput.Key))
.Where(this.MacroHit)
.Do(this.HandleMacro)
.Any();
}
}
public void OnClosing()
{
this.cancellationTokenSource.Cancel();
}
public void OnStartup()
{
}
private bool MacroHit(KeyMacro keyMacro)
{
return this.KeysDown.Intersect(keyMacro.Keys).OrderBy(key => key).SequenceEqual(keyMacro.Keys.OrderBy(key => key));
}
private void HandleMacro(KeyMacro keyMacro)
{
// TODO: Propagate key to guildwars executable.
}
private static bool MacroContainsKey(KeyMacro keyMacro, Keys lastKey)
{
return keyMacro.Keys.Contains(lastKey);
}
}
}

Some files were not shown because too many files have changed in this diff Show More