mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-09-19 06:45:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
930aca4ffe | ||
|
|
1639cabe8e | ||
|
|
5961a57c5b | ||
|
|
75d9bc6dec | ||
|
|
4ccca32881 | ||
|
|
a7f5d9dd96 | ||
|
|
c8ba125abc |
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ using Daybreak.Services.Updater.PostUpdate;
|
||||
using System.Core.Extensions;
|
||||
using Daybreak.Services.Updater.PostUpdate.Actions;
|
||||
using Daybreak.Services.Graph;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Daybreak.Configuration
|
||||
{
|
||||
@@ -36,51 +37,72 @@ namespace Daybreak.Configuration
|
||||
{
|
||||
serviceManager.ThrowIfNull();
|
||||
|
||||
serviceManager.RegisterHttpFactory((serviceProvider, categoryType) =>
|
||||
{
|
||||
var loggerType = typeof(ILogger<>).MakeGenericType(categoryType);
|
||||
var logger = serviceProvider.GetService(loggerType).As<ILogger>();
|
||||
var handler = new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
|
||||
return handler;
|
||||
});
|
||||
serviceManager.RegisterOptionsManager<ApplicationConfigurationOptionsManager>();
|
||||
serviceManager.RegisterResolver(new LoggerResolver());
|
||||
serviceManager
|
||||
.RegisterHttpClient<ApplicationUpdater>()
|
||||
.WithMessageHandler(sp =>
|
||||
{
|
||||
var logger = sp.GetService<ILogger<ApplicationUpdater>>();
|
||||
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
|
||||
})
|
||||
.Build()
|
||||
.RegisterHttpClient<BloogumClient>()
|
||||
.WithMessageHandler(sp =>
|
||||
{
|
||||
var logger = sp.GetService<ILogger<BloogumClient>>();
|
||||
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
|
||||
})
|
||||
.Build()
|
||||
.RegisterHttpClient<GraphClient>()
|
||||
.WithMessageHandler(sp =>
|
||||
{
|
||||
var logger = sp.GetService<ILogger<GraphClient>>();
|
||||
return new LoggingHttpMessageHandler(logger) { InnerHandler = new HttpClientHandler() };
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
public static void RegisterServices(IServiceProducer serviceProducer)
|
||||
public static void RegisterServices(IServiceCollection services)
|
||||
{
|
||||
serviceProducer.ThrowIfNull();
|
||||
services.ThrowIfNull();
|
||||
|
||||
serviceProducer.RegisterSingleton<ILogsManager, JsonLogsManager>();
|
||||
serviceProducer.RegisterSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
|
||||
serviceProducer.RegisterSingleton<ILoggerFactory, LoggerFactory>(sp =>
|
||||
services.AddSingleton<ILogsManager, JsonLogsManager>();
|
||||
services.AddSingleton<IDebugLogsWriter, Services.Logging.DebugLogsWriter>();
|
||||
services.AddSingleton<ILoggerFactory, LoggerFactory>(sp =>
|
||||
{
|
||||
var factory = new LoggerFactory();
|
||||
factory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
|
||||
return factory;
|
||||
});
|
||||
serviceProducer.RegisterSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
|
||||
services.AddSingleton<ILogsWriter, CompositeLogsWriter>(sp => new CompositeLogsWriter(
|
||||
sp.GetService<ILogsManager>(),
|
||||
sp.GetService<IDebugLogsWriter>()));
|
||||
|
||||
serviceProducer.RegisterScoped((sp) => new ScopeMetadata(new CorrelationVector()));
|
||||
serviceProducer.RegisterSingleton<ViewManager>(registerAllInterfaces: true);
|
||||
serviceProducer.RegisterSingleton<PostUpdateActionManager>(registerAllInterfaces: true);
|
||||
serviceProducer.RegisterSingleton<IConfigurationManager, ConfigurationManager>();
|
||||
serviceProducer.RegisterSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
|
||||
serviceProducer.RegisterSingleton<IMutexHandler, MutexHandler>();
|
||||
serviceProducer.RegisterSingleton<IShortcutManager, ShortcutManager>();
|
||||
serviceProducer.RegisterSingleton<IIconBrowser, IconBrowser>();
|
||||
serviceProducer.RegisterSingleton<IIconDownloader, IconDownloader>();
|
||||
serviceProducer.RegisterScoped<ICredentialManager, CredentialManager>();
|
||||
serviceProducer.RegisterScoped<IApplicationLauncher, ApplicationLauncher>();
|
||||
serviceProducer.RegisterScoped<IScreenshotProvider, ScreenshotProvider>();
|
||||
serviceProducer.RegisterScoped<IBloogumClient, BloogumClient>();
|
||||
serviceProducer.RegisterScoped<IApplicationUpdater, ApplicationUpdater>();
|
||||
serviceProducer.RegisterScoped<IBuildTemplateManager, BuildTemplateManager>();
|
||||
serviceProducer.RegisterScoped<IIconCache, IconCache>();
|
||||
serviceProducer.RegisterScoped<IPrivilegeManager, PrivilegeManager>();
|
||||
serviceProducer.RegisterScoped<IScreenManager, ScreenManager>();
|
||||
serviceProducer.RegisterScoped<IGraphClient, GraphClient>();
|
||||
services.AddScoped((sp) => new ScopeMetadata(new CorrelationVector()));
|
||||
services.AddSingleton<ViewManager>();
|
||||
services.AddSingleton<IViewManager, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
|
||||
services.AddSingleton<IViewProducer, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
|
||||
services.AddSingleton<PostUpdateActionManager>();
|
||||
services.AddSingleton<IPostUpdateActionManager>(sp => sp.GetRequiredService<PostUpdateActionManager>());
|
||||
services.AddSingleton<IPostUpdateActionProducer>(sp => sp.GetRequiredService<PostUpdateActionManager>());
|
||||
services.AddSingleton<IPostUpdateActionProvider>(sp => sp.GetRequiredService<PostUpdateActionManager>());
|
||||
services.AddSingleton<IConfigurationManager, ConfigurationManager>();
|
||||
services.AddSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
|
||||
services.AddSingleton<IMutexHandler, MutexHandler>();
|
||||
services.AddSingleton<IShortcutManager, ShortcutManager>();
|
||||
services.AddSingleton<IIconBrowser, IconBrowser>();
|
||||
services.AddSingleton<IIconDownloader, IconDownloader>();
|
||||
services.AddScoped<ICredentialManager, CredentialManager>();
|
||||
services.AddScoped<IApplicationLauncher, ApplicationLauncher>();
|
||||
services.AddScoped<IScreenshotProvider, ScreenshotProvider>();
|
||||
services.AddScoped<IBloogumClient, BloogumClient>();
|
||||
services.AddScoped<IApplicationUpdater, ApplicationUpdater>();
|
||||
services.AddScoped<IBuildTemplateManager, BuildTemplateManager>();
|
||||
services.AddScoped<IIconCache, IconCache>();
|
||||
services.AddScoped<IPrivilegeManager, PrivilegeManager>();
|
||||
services.AddScoped<IScreenManager, ScreenManager>();
|
||||
services.AddScoped<IGraphClient, GraphClient>();
|
||||
}
|
||||
|
||||
public static void RegisterViews(IViewProducer viewProducer)
|
||||
|
||||
@@ -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;
|
||||
@@ -83,9 +84,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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
|
||||
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
|
||||
<Version>0.9.6.2</Version>
|
||||
<Version>0.9.6.7</Version>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
@@ -39,19 +39,19 @@
|
||||
<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.Web.WebView2" Version="1.0.1343.22" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.5" />
|
||||
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageReference Include="Slim" Version="1.7.3" />
|
||||
<PackageReference Include="Slim" Version="1.9.2" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="SystemExtensions.NetCore" Version="1.0.1" />
|
||||
<PackageReference Include="SystemExtensions.NetStandard" Version="1.5.0" />
|
||||
<PackageReference Include="WCL" Version="1.0.2" />
|
||||
<PackageReference Include="WpfExtended" Version="0.6.2" />
|
||||
<PackageReference Include="WpfExtended" Version="0.7.1" />
|
||||
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
|
||||
<PackageReference Include="WpfScreenHelper" Version="2.0.0" />
|
||||
<PackageReference Include="WpfScreenHelper" Version="2.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+19
-10
@@ -1,10 +1,17 @@
|
||||
using Daybreak.Configuration;
|
||||
using Daybreak.Exceptions;
|
||||
using Daybreak.Services.IconRetrieve;
|
||||
using Daybreak.Services.Privilege;
|
||||
using Daybreak.Services.Screenshots;
|
||||
using Daybreak.Services.Updater;
|
||||
using Daybreak.Services.Updater.PostUpdate;
|
||||
using Daybreak.Services.ViewManagement;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Slim;
|
||||
using Slim.Integration.ServiceCollection;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -20,7 +27,7 @@ namespace Daybreak.Launch
|
||||
|
||||
private ILogger logger;
|
||||
|
||||
public IServiceManager ApplicationServiceManager => this.ServiceManager;
|
||||
public System.IServiceProvider ApplicationServiceProvider => this.ServiceProvider;
|
||||
|
||||
[STAThread]
|
||||
public static int Main()
|
||||
@@ -28,16 +35,15 @@ namespace Daybreak.Launch
|
||||
return LaunchMainWindow();
|
||||
}
|
||||
|
||||
protected override void SetupServiceManager(IServiceManager serviceManager)
|
||||
protected override System.IServiceProvider SetupServiceProvider(IServiceCollection services)
|
||||
{
|
||||
var serviceManager = new ServiceManager();
|
||||
ProjectConfiguration.RegisterResolvers(serviceManager);
|
||||
return services.BuildSlimServiceProvider(serviceManager);
|
||||
}
|
||||
protected override void RegisterServices(IServiceProducer serviceProducer)
|
||||
protected override void RegisterServices(IServiceCollection services)
|
||||
{
|
||||
ProjectConfiguration.RegisterServices(this.ServiceManager);
|
||||
this.ServiceManager.BuildSingletons();
|
||||
ProjectConfiguration.RegisterViews(this.ServiceManager.GetService<IViewManager>());
|
||||
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceManager.GetService<IPostUpdateActionProducer>());
|
||||
ProjectConfiguration.RegisterServices(services);
|
||||
}
|
||||
protected override bool HandleException(Exception e)
|
||||
{
|
||||
@@ -94,7 +100,10 @@ namespace Daybreak.Launch
|
||||
}
|
||||
protected override void ApplicationStarting()
|
||||
{
|
||||
this.logger = this.ServiceManager.GetService<ILogger<Launcher>>();
|
||||
ProjectConfiguration.RegisterViews(this.ServiceProvider.GetService<IViewManager>());
|
||||
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceProvider.GetService<IPostUpdateActionProducer>());
|
||||
|
||||
this.logger = this.ServiceProvider.GetRequiredService<ILogger<Launcher>>();
|
||||
this.RegisterViewContainer();
|
||||
}
|
||||
protected override void ApplicationClosing()
|
||||
@@ -103,8 +112,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()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using Daybreak.Models.Builds;
|
||||
|
||||
namespace Daybreak.Models;
|
||||
|
||||
public sealed class BuildWithTemplateCode
|
||||
{
|
||||
public BuildEntry Build { get; set; }
|
||||
public string TemplateCode { get; set; }
|
||||
}
|
||||
@@ -80,6 +80,22 @@ namespace Daybreak.Services.BuildTemplates
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<BuildEntry, Exception>> GetBuild(string name)
|
||||
{
|
||||
if (File.Exists($"{BuildsPath}/{name}.txt") is false)
|
||||
{
|
||||
return new InvalidOperationException("Unable to find build file");
|
||||
}
|
||||
|
||||
var content = await File.ReadAllTextAsync($"{BuildsPath}/{name}.txt");
|
||||
if (this.TryDecodeTemplate(content, out var build) is false)
|
||||
{
|
||||
return new InvalidOperationException("Unable to parse build file");
|
||||
}
|
||||
|
||||
return new BuildEntry { Build = build, Name = name, PreviousName = name };
|
||||
}
|
||||
|
||||
public void ClearBuilds()
|
||||
{
|
||||
foreach(var file in Directory.GetFiles(BuildsPath))
|
||||
@@ -260,7 +276,7 @@ namespace Daybreak.Services.BuildTemplates
|
||||
buildMetadata.NewTemplate = false;
|
||||
}
|
||||
|
||||
buildMetadata.ProfessionIdLength = stream.Read(2) * 2 + 4;
|
||||
buildMetadata.ProfessionIdLength = (stream.Read(2) * 2) + 4;
|
||||
buildMetadata.PrimaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
|
||||
buildMetadata.SecondaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
|
||||
buildMetadata.AttributeCount = stream.Read(4);
|
||||
@@ -297,7 +313,7 @@ namespace Daybreak.Services.BuildTemplates
|
||||
|
||||
var desiredProfessionIdLength = GetBitLength(new List<int> { buildMetadata.PrimaryProfessionId, buildMetadata.SecondaryProfessionId }.Max());
|
||||
var professionIdLength = Math.Max((desiredProfessionIdLength - 4) / 2, 0);
|
||||
var finalProfessionIdLength = professionIdLength * 2 + 4;
|
||||
var finalProfessionIdLength = (professionIdLength * 2) + 4;
|
||||
stream.Write(professionIdLength, 2);
|
||||
stream.Write(buildMetadata.PrimaryProfessionId, finalProfessionIdLength);
|
||||
stream.Write(buildMetadata.SecondaryProfessionId, finalProfessionIdLength);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using Daybreak.Models.Builds;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.BuildTemplates
|
||||
{
|
||||
@@ -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);
|
||||
|
||||
@@ -15,7 +15,6 @@ 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;
|
||||
@@ -40,13 +39,11 @@ public sealed class GraphClient : IGraphClient
|
||||
private const string GraphBaseUrl = "https://graph.microsoft.com/v1.0/";
|
||||
private const string TokenUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
|
||||
private const string AuthorizationUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id={ClientIdPlaceholder}&response_type=code&redirect_uri={RedirectUriPlaceholder}&response_mode=query&scope={ScopesPlaceholder}&state={StatePlaceholder}";
|
||||
private const string SyncFileUri = $"me/drive/root:/Daybreak/{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 +51,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 +74,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);
|
||||
@@ -130,22 +129,27 @@ 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)
|
||||
{
|
||||
@@ -158,33 +162,66 @@ 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()
|
||||
@@ -192,48 +229,80 @@ public sealed class GraphClient : IGraphClient
|
||||
this.ResetAccessToken();
|
||||
}
|
||||
|
||||
private async Task<Optional<string>> GetBackupItemContent()
|
||||
private async Task<bool> PutBuild(BuildEntry buildEntry)
|
||||
{
|
||||
var maybeDriveItem = await this.GetDriveItem();
|
||||
if (maybeDriveItem.ExtractValue() is not DriveItem driveItem)
|
||||
if (this.buildsCache is null)
|
||||
{
|
||||
return Optional.None<string>();
|
||||
_ = await this.RetrieveBuildsList();
|
||||
}
|
||||
|
||||
if (driveItem.Name != BackupFileName)
|
||||
var buildFile = new BuildFile
|
||||
{
|
||||
return Optional.None<string>();
|
||||
}
|
||||
FileName = buildEntry.Name,
|
||||
TemplateCode = this.buildTemplateManager.EncodeTemplate(buildEntry.Build)
|
||||
};
|
||||
|
||||
var response = await this.httpClient.GetAsync(driveItem.DownloadUrl);
|
||||
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<string>();
|
||||
return false;
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
this.buildsCache = buildList;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> UploadBackupItem(string serializedBuilds)
|
||||
private async Task<bool> PutBuilds(List<BuildEntry> buildEntries)
|
||||
{
|
||||
using var stringContent = new StringContent(serializedBuilds);
|
||||
var response = await this.httpClient.PutAsync(SyncFileUri + ContentSuffix, stringContent);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
var buildFiles = buildEntries.Select(buildEntry => new BuildFile
|
||||
{
|
||||
FileName = buildEntry.Name,
|
||||
TemplateCode = this.buildTemplateManager.EncodeTemplate(buildEntry.Build)
|
||||
});
|
||||
|
||||
private async Task<Optional<DriveItem>> GetDriveItem()
|
||||
{
|
||||
var response = await this.httpClient.GetAsync(SyncFileUri);
|
||||
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 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<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)
|
||||
{
|
||||
@@ -258,7 +327,7 @@ public sealed class GraphClient : IGraphClient
|
||||
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());
|
||||
|
||||
@@ -16,7 +16,8 @@ public interface IGraphClient
|
||||
Task<Result<bool, Exception>> PerformAuthorizationFlow(ChromiumBrowserWrapper chromiumBrowserWrapper, CancellationToken cancellationToken = default);
|
||||
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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ using System;
|
||||
|
||||
namespace Daybreak.Services.Graph.Models;
|
||||
|
||||
public sealed class DriveItem
|
||||
public sealed class FileItem
|
||||
{
|
||||
[JsonProperty("@microsoft.graph.downloadUrl")]
|
||||
public string DownloadUrl { 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; }
|
||||
}
|
||||
@@ -5,14 +5,19 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
xmlns:glyps="clr-namespace:Daybreak.Controls.Glyphs"
|
||||
xmlns:converters="clr-namespace:Daybreak.Converters"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d"
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" TriggerValue="False"></converters:BooleanToVisibilityConverter>
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True"></converters:BooleanToVisibilityConverter>
|
||||
</UserControl.Resources>
|
||||
<Grid
|
||||
Background="#A0202020">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
@@ -20,24 +25,24 @@
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
|
||||
Clicked="BackButton_Clicked" VerticalAlignment="Top"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:BackButton>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 45, 5"
|
||||
Clicked="DownloadButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}">
|
||||
<controls:BackButton.RenderTransform>
|
||||
<RotateTransform Angle="270" CenterX="15" CenterY="15"></RotateTransform>
|
||||
</controls:BackButton.RenderTransform>
|
||||
</controls:BackButton>
|
||||
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 5, 5"
|
||||
Clicked="UploadButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}">
|
||||
<controls:BackButton.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="15" CenterY="15"></RotateTransform>
|
||||
</controls:BackButton.RenderTransform>
|
||||
</controls:BackButton>
|
||||
<TextBlock HorizontalAlignment="Center"
|
||||
<WrapPanel HorizontalAlignment="Center">
|
||||
<TextBlock
|
||||
Text="Build templates synchronization"
|
||||
FontSize="24"
|
||||
Foreground="White" />
|
||||
<Grid Margin="5"
|
||||
VerticalAlignment="Center">
|
||||
<glyps:GoodGlyph Foreground="LimeGreen"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Visibility="{Binding ElementName=_this, Path=Synchronized, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<glyps:BadGlyph Foreground="OrangeRed"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Visibility="{Binding ElementName=_this, Path=Synchronized, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
|
||||
</Grid>
|
||||
</WrapPanel>
|
||||
|
||||
<WrapPanel HorizontalAlignment="Center"
|
||||
Grid.Row="1">
|
||||
<TextBlock Text="Logged in as: "
|
||||
@@ -47,32 +52,45 @@
|
||||
FontSize="20"
|
||||
Foreground="White"></TextBlock>
|
||||
</WrapPanel>
|
||||
<WrapPanel HorizontalAlignment="Center"
|
||||
Grid.Row="2">
|
||||
<TextBlock Text="Last uploaded: "
|
||||
Foreground="White"
|
||||
FontSize="18"/>
|
||||
<TextBlock Text="{Binding ElementName=_this, Path=LastUploadDate, Mode=OneWay}"
|
||||
Foreground="White"
|
||||
FontSize="20"/>
|
||||
</WrapPanel>
|
||||
<Grid Grid.Row="3">
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Preview uploaded templates"
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="Local"
|
||||
FontSize="14"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"/>
|
||||
<ListView Grid.Row="1" Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntries, Mode=OneWay}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
<controls:OpaqueButton Grid.Column="0"
|
||||
Text="Upload all"
|
||||
Foreground="White"
|
||||
Background="DarkGray"
|
||||
HorizontalAlignment="Right"
|
||||
Padding="5"
|
||||
Margin="5"
|
||||
Clicked="UploadAllButton_Clicked"
|
||||
Width="80"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
|
||||
<ListView
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Background="Transparent"
|
||||
ItemsSource="{Binding ElementName=_this, Path=LocalBuildEntries, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=SelectedLocalBuild, Mode=TwoWay}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<TextBlock FontSize="14"
|
||||
Foreground="White"
|
||||
Text="{Binding FileName}"
|
||||
Text="{Binding Build.Name}"
|
||||
HorizontalAlignment="Left"></TextBlock>
|
||||
<TextBlock FontSize="14"
|
||||
Foreground="White"
|
||||
@@ -82,6 +100,69 @@
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="Remote"
|
||||
FontSize="14"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
<controls:OpaqueButton Grid.Column="2"
|
||||
Text="Download all"
|
||||
Foreground="White"
|
||||
Background="DarkGray"
|
||||
HorizontalAlignment="Left"
|
||||
Padding="5"
|
||||
Margin="5"
|
||||
Clicked="DownloadAllButton_Clicked"
|
||||
Width="80"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
|
||||
<ListView
|
||||
Grid.Column="2"
|
||||
Grid.Row="1"
|
||||
Background="Transparent"
|
||||
ItemsSource="{Binding ElementName=_this, Path=RemoteBuildEntries, Mode=OneWay}"
|
||||
SelectedItem="{Binding ElementName=_this, Path=SelectedRemoteBuild, Mode=TwoWay}"
|
||||
HorizontalContentAlignment="Stretch">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<TextBlock FontSize="14"
|
||||
Foreground="White"
|
||||
Text="{Binding TemplateCode}"
|
||||
HorizontalAlignment="Left"></TextBlock>
|
||||
<TextBlock FontSize="14"
|
||||
Foreground="White"
|
||||
Text="{Binding FileName}"
|
||||
HorizontalAlignment="Right"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<StackPanel Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center">
|
||||
<controls:OpaqueButton Text="Download Build"
|
||||
Foreground="White"
|
||||
Background="DarkGray"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Clicked="DownloadButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton Text="Upload Build"
|
||||
Foreground="White"
|
||||
Background="DarkGray"
|
||||
Margin="5"
|
||||
Padding="5"
|
||||
Clicked="UploadButton_Clicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.RowSpan="3"
|
||||
Background="#A0202020"
|
||||
Visibility="{Binding ElementName=_this, Path=ShowLoading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<controls:CircularLoadingWidget MaxWidth="200"
|
||||
MaxHeight="200"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -10,6 +10,10 @@ using Daybreak.Services.Graph.Models;
|
||||
using System.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Daybreak.Services.BuildTemplates;
|
||||
using System.Linq;
|
||||
using Daybreak.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Views;
|
||||
|
||||
@@ -18,11 +22,13 @@ namespace Daybreak.Views;
|
||||
/// </summary>
|
||||
public partial class BuildsSynchronizationView : UserControl
|
||||
{
|
||||
private readonly IBuildTemplateManager buildTemplateManager;
|
||||
private readonly IGraphClient graphClient;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILogger<BuildsSynchronizationView> logger;
|
||||
|
||||
public ObservableCollection<BuildFile> BuildEntries { get; } = new();
|
||||
public ObservableCollection<BuildFile> RemoteBuildEntries { get; } = new();
|
||||
public ObservableCollection<BuildWithTemplateCode> LocalBuildEntries { get; } = new();
|
||||
|
||||
[GenerateDependencyProperty(InitialValue = true)]
|
||||
private bool buttonsEnabled;
|
||||
@@ -30,12 +36,22 @@ public partial class BuildsSynchronizationView : UserControl
|
||||
private string displayName;
|
||||
[GenerateDependencyProperty]
|
||||
private string lastUploadDate;
|
||||
[GenerateDependencyProperty]
|
||||
private BuildFile selectedRemoteBuild;
|
||||
[GenerateDependencyProperty]
|
||||
private BuildWithTemplateCode selectedLocalBuild;
|
||||
[GenerateDependencyProperty]
|
||||
private bool showLoading;
|
||||
[GenerateDependencyProperty]
|
||||
private bool synchronized;
|
||||
|
||||
public BuildsSynchronizationView(
|
||||
IBuildTemplateManager buildTemplateManager,
|
||||
IGraphClient graphClient,
|
||||
IViewManager viewManager,
|
||||
ILogger<BuildsSynchronizationView> logger)
|
||||
{
|
||||
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
|
||||
this.graphClient = graphClient.ThrowIfNull();
|
||||
this.viewManager = viewManager.ThrowIfNull();
|
||||
this.logger = logger.ThrowIfNull();
|
||||
@@ -45,6 +61,7 @@ public partial class BuildsSynchronizationView : UserControl
|
||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.ButtonsEnabled = false;
|
||||
this.ShowLoading = true;
|
||||
var profile = await this.graphClient.GetUserProfile<BuildsSynchronizationView>();
|
||||
if (profile.TryExtractSuccess(out var user) is false)
|
||||
{
|
||||
@@ -53,30 +70,32 @@ public partial class BuildsSynchronizationView : UserControl
|
||||
}
|
||||
|
||||
this.DisplayName = user.DisplayName;
|
||||
await this.PopulateLastUpdateTime();
|
||||
await this.PopulateBuilds();
|
||||
this.ButtonsEnabled = true;
|
||||
}
|
||||
|
||||
private async Task PopulateLastUpdateTime()
|
||||
{
|
||||
var maybeDateTime = await this.graphClient.GetLastUpdateTime();
|
||||
if (maybeDateTime.TryExtractSuccess(out var dateTime))
|
||||
{
|
||||
this.LastUploadDate = dateTime.ToString("G");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LastUploadDate = "Never";
|
||||
}
|
||||
this.ShowLoading = false;
|
||||
}
|
||||
|
||||
private async Task PopulateBuilds()
|
||||
{
|
||||
var maybeBuilds = await this.graphClient.RetrieveBuildsList();
|
||||
if (maybeBuilds.TryExtractSuccess(out var builds))
|
||||
var getBuildsResponse = await this.graphClient.RetrieveBuildsList();
|
||||
if (getBuildsResponse.TryExtractSuccess(out var builds) is false)
|
||||
{
|
||||
this.BuildEntries.ClearAnd().AddRange(builds);
|
||||
builds = new List<BuildFile>();
|
||||
}
|
||||
|
||||
this.RemoteBuildEntries.ClearAnd().AddRange(builds);
|
||||
var localBuilds = await this.buildTemplateManager.GetBuilds().ToListAsync();
|
||||
this.LocalBuildEntries.ClearAnd().AddRange(localBuilds.Select(build => new BuildWithTemplateCode { Build = build, TemplateCode = this.buildTemplateManager.EncodeTemplate(build.Build) }));
|
||||
|
||||
if (this.LocalBuildEntries.Count == this.RemoteBuildEntries.Count &&
|
||||
this.LocalBuildEntries.Select(b => b.Build.Name).Except(this.RemoteBuildEntries.Select(b => b.FileName)).None() &&
|
||||
this.LocalBuildEntries.Select(b => b.TemplateCode).Except(this.RemoteBuildEntries.Select(b => b.TemplateCode)).None())
|
||||
{
|
||||
this.Synchronized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Synchronized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,31 +106,64 @@ public partial class BuildsSynchronizationView : UserControl
|
||||
|
||||
private async void UploadButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.ButtonsEnabled = false;
|
||||
var result = await this.graphClient.UploadBuilds();
|
||||
result.DoAny(
|
||||
onFailure: (failure) =>
|
||||
{
|
||||
this.logger.LogError(failure, $"Failed to upload builds");
|
||||
});
|
||||
// TODO: Handle failures to upload
|
||||
if (this.SelectedLocalBuild is not BuildWithTemplateCode buildWithTemplateCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this.PopulateLastUpdateTime();
|
||||
this.ButtonsEnabled = false;
|
||||
this.ShowLoading = true;
|
||||
await this.graphClient.UploadBuild(buildWithTemplateCode.Build.Name);
|
||||
await this.PopulateBuilds();
|
||||
this.ButtonsEnabled = true;
|
||||
this.ShowLoading = false;
|
||||
}
|
||||
|
||||
private async void DownloadButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (this.SelectedRemoteBuild is not BuildFile buildFile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.ButtonsEnabled = false;
|
||||
var result = await this.graphClient.DownloadBuilds();
|
||||
this.ShowLoading = true;
|
||||
await this.graphClient.DownloadBuild(buildFile.FileName);
|
||||
await this.PopulateBuilds();
|
||||
this.ButtonsEnabled = true;
|
||||
this.ShowLoading = false;
|
||||
}
|
||||
|
||||
private async void DownloadAllButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.ButtonsEnabled = false;
|
||||
this.ShowLoading = true;
|
||||
var result = await this.graphClient.DownloadBuilds().ConfigureAwait(true);
|
||||
result.DoAny(
|
||||
onFailure: (failure) =>
|
||||
{
|
||||
this.logger.LogError(failure, $"Failed to download builds");
|
||||
});
|
||||
|
||||
await this.PopulateLastUpdateTime();
|
||||
await this.PopulateBuilds();
|
||||
this.ButtonsEnabled = true;
|
||||
this.ShowLoading = false;
|
||||
}
|
||||
|
||||
private async void UploadAllButton_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
this.ButtonsEnabled = false;
|
||||
this.ShowLoading = true;
|
||||
var result = await this.graphClient.UploadBuilds().ConfigureAwait(true);
|
||||
result.DoAny(
|
||||
onFailure: (failure) =>
|
||||
{
|
||||
this.logger.LogError(failure, $"Failed to upload builds");
|
||||
});
|
||||
|
||||
await this.PopulateBuilds();
|
||||
this.ButtonsEnabled = true;
|
||||
this.ShowLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user