diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 8e34846..1405b80 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -37,7 +37,9 @@ jobs: run: > dotnet publish ${{github.workspace}}/Client/UniScan.Client.App.Platform.Browser -c Release - -o ${{steps.strings.outputs.build-output-dir}} + -o ${{steps.strings.outputs.build-output-dir}} + /nr:false + /m:1 - name: Upload Artifact uses: actions/upload-pages-artifact@v5 @@ -50,6 +52,7 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build-client-web + if: github.ref == 'refs/heads/main' permissions: id-token: write pages: write diff --git a/Client/Directory.Packages.props b/Client/Directory.Packages.props index 529530b..a525dfe 100755 --- a/Client/Directory.Packages.props +++ b/Client/Directory.Packages.props @@ -1,44 +1,47 @@  - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Android/AndroidPaths.cs b/Client/UniScan.Client.App.Platform.Android/AndroidPaths.cs index 51f2b1b..41424d6 100644 --- a/Client/UniScan.Client.App.Platform.Android/AndroidPaths.cs +++ b/Client/UniScan.Client.App.Platform.Android/AndroidPaths.cs @@ -10,4 +10,6 @@ public class AndroidPaths : IPlatformStandardPaths public string DataPath => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); public string ConfigPath => Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + + public string CachePath => Path.Combine(DataPath, ".cache"); } \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Android/AssemblyInfo.cs b/Client/UniScan.Client.App.Platform.Android/AssemblyInfo.cs new file mode 100644 index 0000000..7d037e6 --- /dev/null +++ b/Client/UniScan.Client.App.Platform.Android/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using UniScan.Client.App.Core.Platform; + +[assembly: UniScanPlatform] \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Android/UniScan.Client.App.Platform.Android.csproj b/Client/UniScan.Client.App.Platform.Android/UniScan.Client.App.Platform.Android.csproj index 0a71920..b8ff1a5 100755 --- a/Client/UniScan.Client.App.Platform.Android/UniScan.Client.App.Platform.Android.csproj +++ b/Client/UniScan.Client.App.Platform.Android/UniScan.Client.App.Platform.Android.csproj @@ -9,10 +9,10 @@ 1.0 apk false - UniScan + UniScanProject UniScan Android - 0.0.2 - 0.0.2 + 0.0.3 + 0.0.3 diff --git a/Client/UniScan.Client.App.Platform.Browser/AssemblyInfo.cs b/Client/UniScan.Client.App.Platform.Browser/AssemblyInfo.cs new file mode 100644 index 0000000..7d037e6 --- /dev/null +++ b/Client/UniScan.Client.App.Platform.Browser/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using UniScan.Client.App.Core.Platform; + +[assembly: UniScanPlatform] \ No newline at end of file diff --git a/Platform/UniScan.Platform.Implementations.Web/BrowserPaths.cs b/Client/UniScan.Client.App.Platform.Browser/BrowserPaths.cs similarity index 54% rename from Platform/UniScan.Platform.Implementations.Web/BrowserPaths.cs rename to Client/UniScan.Client.App.Platform.Browser/BrowserPaths.cs index 80bd5f3..8f37f09 100644 --- a/Platform/UniScan.Platform.Implementations.Web/BrowserPaths.cs +++ b/Client/UniScan.Client.App.Platform.Browser/BrowserPaths.cs @@ -1,7 +1,10 @@ -namespace UniScan.Platform.Implementations.Web; +using UniScan.Platform; + +namespace UniScan.Client.App.Platform.Browser; public class BrowserPaths : IPlatformStandardPaths { public string DataPath => "data"; public string ConfigPath => "config"; + public string CachePath => "cache"; } \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Browser/UniScan.Client.App.Platform.Browser.csproj b/Client/UniScan.Client.App.Platform.Browser/UniScan.Client.App.Platform.Browser.csproj index b115f14..9253657 100755 --- a/Client/UniScan.Client.App.Platform.Browser/UniScan.Client.App.Platform.Browser.csproj +++ b/Client/UniScan.Client.App.Platform.Browser/UniScan.Client.App.Platform.Browser.csproj @@ -20,8 +20,8 @@ https://github.com/UniScanProject/UniScan UniScanProject UniScan Web - 0.1.1 - 0.1.1 + 0.1.2 + 0.1.2 diff --git a/Client/UniScan.Client.App.Platform.Desktop/AssemblyInfo.cs b/Client/UniScan.Client.App.Platform.Desktop/AssemblyInfo.cs new file mode 100644 index 0000000..7d037e6 --- /dev/null +++ b/Client/UniScan.Client.App.Platform.Desktop/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using UniScan.Client.App.Core.Platform; + +[assembly: UniScanPlatform] \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Desktop/DesktopPaths.cs b/Client/UniScan.Client.App.Platform.Desktop/DesktopPaths.cs index 11daee7..f1fa665 100644 --- a/Client/UniScan.Client.App.Platform.Desktop/DesktopPaths.cs +++ b/Client/UniScan.Client.App.Platform.Desktop/DesktopPaths.cs @@ -11,4 +11,8 @@ public class DesktopPaths : IPlatformStandardPaths public string ConfigPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "UniScan"); + + public string CachePath => OperatingSystem.IsLinux() ? + Path.Combine(Environment.GetEnvironmentVariable("XDG_CACHE_HOME") ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cache"), "UniScan") + : Path.Combine(DataPath, "cache"); } \ No newline at end of file diff --git a/Client/UniScan.Client.App.Platform.Desktop/UniScan.Client.App.Platform.Desktop.csproj b/Client/UniScan.Client.App.Platform.Desktop/UniScan.Client.App.Platform.Desktop.csproj index 2313850..e0575f4 100755 --- a/Client/UniScan.Client.App.Platform.Desktop/UniScan.Client.App.Platform.Desktop.csproj +++ b/Client/UniScan.Client.App.Platform.Desktop/UniScan.Client.App.Platform.Desktop.csproj @@ -5,10 +5,10 @@ See https://docs.avaloniaui.net/docs/guides/platforms/platform-specific-code/dotnet for more details.--> net10.0 enable - UniScan + UniScanProject UniScan Desktop - 0.0.2 - 0.0.2 + 0.0.3 + 0.0.3 diff --git a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.Initializers.cs b/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.Initializers.cs deleted file mode 100644 index 0ed109f..0000000 --- a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.Initializers.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using UniScan.Client.App.ViewModels; -using UniScan.Client.App.Views; - -namespace UniScan.Client.App.Core.Initialization; - -public partial class UniScanAppInitializationPipeline -{ - internal static async Task InitializeViews(TaskContexts.PostClient ctx) - { - ctx.Status.Value = "Initializing UI"; - - ctx.ServiceCollection.AddSingleton(); - ctx.ServiceCollection.AddSingleton(); - - ctx.ServiceCollection.AddSingleton(); - } - - internal static async Task InitializeServiceProvider(TaskContexts.PreServiceProvider ctx) - { - ctx.Status.Value = "Building ServiceProvider"; - - ctx.Services = ctx.ServiceCollection.BuildServiceProvider(); - } - - internal static async Task InitializeRemotes(TaskContexts.PostServiceProvider ctx) - { - ctx.Status.Value = "Saving remotes"; - - await ctx.Client!.RemoteManagerFile.SaveAsync(ctx.Client.RemoteManager); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Pipeline/ITaskContext.cs b/Client/UniScan.Client.App/Core/Pipeline/ITaskContext.cs deleted file mode 100644 index 137156f..0000000 --- a/Client/UniScan.Client.App/Core/Pipeline/ITaskContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -using R3; - -namespace UniScan.Client.App.Core.Pipeline; - -public interface ITaskContext -{ - public BindableReactiveProperty Status { get; }//todo why not give setter func to top level reactiveproperty??? -} - -public interface ITaskContext : ITaskContext - where TSelf : ITaskContext - where TOldContext : ITaskContext -{ - static abstract TSelf TransitionFrom(TOldContext oldContext); -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Pipeline/ITaskStage.cs b/Client/UniScan.Client.App/Core/Pipeline/ITaskStage.cs deleted file mode 100644 index 0b19f62..0000000 --- a/Client/UniScan.Client.App/Core/Pipeline/ITaskStage.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace UniScan.Client.App.Core.Pipeline; - -public interface ITaskStage -{ - int Count { get; } - IEnumerable> Tasks { get; } - - ITaskContext Transition(ITaskContext old); -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.Initializers.cs b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.Initializers.cs new file mode 100644 index 0000000..78e2c06 --- /dev/null +++ b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.Initializers.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using UniScan.Client.App.Views; +using UniScan.Client.App.Views.Settings; +using UniScan.Client.Core.Remote; +using UniScan.Network; +using UniScan.Network.Registry.Source.Sources; +using MainViewModel = UniScan.Client.App.Views.MainViewModel; + +namespace UniScan.Client.App.Core.Pipeline.Initialization; + +public partial class UniScanAppInitializationPipeline +{ + internal static async Task InitializeViews(TaskContexts.PostClient ctx, CancellationToken ct = default) + { + ctx.Status.Value = "Initializing UI"; + + ctx.ServiceCollection.AddSingleton(); + ctx.ServiceCollection.AddSingleton(); + + ctx.ServiceCollection.AddSingleton(); + } + + internal static async Task InitializeServiceProvider(TaskContexts.PreServiceProvider ctx, CancellationToken ct = default) + { + ctx.Status.Value = "Building ServiceProvider"; + + ctx.Services = ctx.ServiceCollection.BuildServiceProvider(); + } + + internal static async Task InitializeRemotes(TaskContexts.PostServiceProvider ctx, CancellationToken ct = default) + { + ctx.Status.Value = "Loading remotes"; + + IRemoteStorage remoteStorage = ctx.Services.GetRequiredService(); + await remoteStorage.LoadAsync(); + } + + internal Task RegisterPackets(UniScanAppInitializationPipeline.TaskContexts.PostServiceProvider ctx, + CancellationToken ct = default) + { + ctx.Status.Value = "Registering packets"; + + PacketRegistry registry = ctx.Services.GetRequiredService(); + registry.RegisterFromSource(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs similarity index 70% rename from Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs rename to Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs index a0e8d60..a6d43ea 100644 --- a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs +++ b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.TaskContexts.cs @@ -1,10 +1,10 @@ using System; using Microsoft.Extensions.DependencyInjection; using R3; -using UniScan.Client.App.Core.Pipeline; +using Shiki.TaskPipeline; using UniScan.Client.Core; -namespace UniScan.Client.App.Core.Initialization; +namespace UniScan.Client.App.Core.Pipeline.Initialization; public partial class UniScanAppInitializationPipeline { @@ -22,9 +22,7 @@ public class PreClient(string status, ServiceCollection serviceCollection) : ITa public BindableReactiveProperty Status { get; } = new(status); public ServiceCollection ServiceCollection { get; } = serviceCollection; - - public UniScanClient? Client { get; set; } = null; - + public PreClient(Early ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection) { } @@ -34,18 +32,14 @@ public PreClient(Early ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollectio public class PostClient( string status, - ServiceCollection serviceCollection, - UniScanClient client) + ServiceCollection serviceCollection) : ITaskContext { public BindableReactiveProperty Status { get; } = new(status); public ServiceCollection ServiceCollection { get; } = serviceCollection; - - public UniScanClient Client { get; } = client; - - public PostClient(PreClient ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection, - ctx.Client ?? throw new NullReferenceException()) + + public PostClient(PreClient ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection) { } @@ -54,20 +48,16 @@ public PostClient(PreClient ctx) : this(ctx.Status.CurrentValue, ctx.ServiceColl public class PreServiceProvider( string status, - ServiceCollection serviceCollection, - UniScanClient client) + ServiceCollection serviceCollection) : ITaskContext { public BindableReactiveProperty Status { get; } = new(status); public ServiceCollection ServiceCollection { get; } = serviceCollection; - - public UniScanClient Client { get; } = client; - + public IServiceProvider? Services { get; set; } = null; - public PreServiceProvider(PostClient ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection, - ctx.Client ?? throw new NullReferenceException()) + public PreServiceProvider(PostClient ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection) { } @@ -76,21 +66,14 @@ public PreServiceProvider(PostClient ctx) : this(ctx.Status.CurrentValue, ctx.Se public class PostServiceProvider( string status, - ServiceCollection serviceCollection, - UniScanClient client, IServiceProvider provider) : ITaskContext { public BindableReactiveProperty Status { get; } = new(status); - - public ServiceCollection ServiceCollection { get; } = serviceCollection; - - public UniScanClient Client { get; } = client; - + public IServiceProvider Services { get; } = provider; - public PostServiceProvider(PreServiceProvider ctx) : this(ctx.Status.CurrentValue, ctx.ServiceCollection, - ctx.Client ?? throw new NullReferenceException(), + public PostServiceProvider(PreServiceProvider ctx) : this(ctx.Status.CurrentValue, ctx.Services ?? throw new NullReferenceException()) { diff --git a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.cs b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.cs similarity index 88% rename from Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.cs rename to Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.cs index d4c3d52..c541e75 100644 --- a/Client/UniScan.Client.App/Core/Initialization/UniScanAppInitializationPipeline.cs +++ b/Client/UniScan.Client.App/Core/Pipeline/Initialization/UniScanAppInitializationPipeline.cs @@ -2,9 +2,9 @@ using System.Threading.Tasks; using R3; using Serilog; -using UniScan.Client.App.Core.Pipeline; +using Shiki.TaskPipeline; -namespace UniScan.Client.App.Core.Initialization; +namespace UniScan.Client.App.Core.Pipeline.Initialization; public partial class UniScanAppInitializationPipeline { @@ -17,7 +17,7 @@ public UniScanAppInitializationPipeline(UniScanApp app) Pipeline = new TaskPipelineBuilder() //early .ThenRun(app.InitializeEnvironment) - .ThenRun(_ => + .ThenRun((_, _) => { _logger = Log.ForContext(); @@ -33,8 +33,9 @@ public UniScanAppInitializationPipeline(UniScanApp app) .ThenRun(InitializeServiceProvider) .ThenTransitionTo() .ThenRun(InitializeRemotes) + .ThenRun(RegisterPackets) .ThenRun(app.FinishInitialization) - .ThenRun(_ => + .ThenRun((_, _) => { _subscription?.Dispose(); diff --git a/Client/UniScan.Client.App/Core/Pipeline/TaskPipeline.cs b/Client/UniScan.Client.App/Core/Pipeline/TaskPipeline.cs deleted file mode 100644 index 6af65b6..0000000 --- a/Client/UniScan.Client.App/Core/Pipeline/TaskPipeline.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using R3; - -namespace UniScan.Client.App.Core.Pipeline; - -public class TaskPipeline -{ - public BindableReactiveProperty Progress { get; } = new(0.0); - public BindableReactiveProperty Status { get; } = new("Starting"); - - private readonly List _stages = []; - - internal TaskPipeline() {}//only allow builder to call ctor - - internal void Add(ITaskStage stage) => _stages.Add(stage); - - public async Task RunAsync(ITaskContext initial) - { - ITaskContext currentContext = initial; - IDisposable? subscription = null; - - int tasks = _stages.Sum(s => s.Count); - int completed = 0; - try - { - foreach (ITaskStage stage in _stages) - { - currentContext = stage.Transition(currentContext); - - subscription?.Dispose(); //make sure old is disposed otherwise we will leak subscriptions - //todo see comment in itaskcontext - subscription = currentContext.Status.Subscribe(s => Status.Value = s); - - foreach (var task in stage.Tasks) - { - await task(currentContext); - - completed++; - Progress.Value = (completed * 100) / tasks; - } - } - }//todo catch exception? idk how to get this fucking exception to bubble - finally - { - subscription?.Dispose(); - } - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Pipeline/TaskPipelineBuilder.cs b/Client/UniScan.Client.App/Core/Pipeline/TaskPipelineBuilder.cs deleted file mode 100644 index 5d1c6df..0000000 --- a/Client/UniScan.Client.App/Core/Pipeline/TaskPipelineBuilder.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace UniScan.Client.App.Core.Pipeline; - -public class TaskPipelineBuilder - where TContext : class, ITaskContext -{ - private readonly TaskPipeline _pipeline; - private readonly TaskStage _stage; - - public TaskPipelineBuilder() - { - _pipeline = new TaskPipeline(); - _stage = new TaskStage(ctx => (TContext)ctx); - - _pipeline.Add(_stage); - } - - public TaskPipelineBuilder(TaskPipeline pipeline, TaskStage stage) - { - _pipeline = pipeline; - _stage = stage; - - _pipeline.Add(_stage); - } - - public TaskPipelineBuilder ThenRun(Func task) - { - _stage.Add(task); - return this; - } - - //idk better word than transition im too sleepy - public TaskPipelineBuilder ThenTransitionTo(Func transition) - where TNewContext : class, ITaskContext - => new(_pipeline, new TaskStage(ctx => transition((TContext)ctx))); - - public TaskPipelineBuilder ThenTransitionTo() - where TNewContext : class, ITaskContext - => new(_pipeline, new TaskStage(ctx => TNewContext.TransitionFrom((TContext)ctx))); - - public TaskPipeline Build() => _pipeline; -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Pipeline/TaskStage.cs b/Client/UniScan.Client.App/Core/Pipeline/TaskStage.cs deleted file mode 100644 index d7bbd21..0000000 --- a/Client/UniScan.Client.App/Core/Pipeline/TaskStage.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace UniScan.Client.App.Core.Pipeline; - -public class TaskStage : ITaskStage - where TOldContext : class, ITaskContext - where TContext : class, ITaskContext -{ - private readonly Func _transition; - private readonly List> _tasks = []; - - public int Count => _tasks.Count; - - public IEnumerable> Tasks - { - get - { - foreach (var task in _tasks) - yield return ctx => task((TContext)ctx); - } - } - - public TaskStage(Func transition) - { - _transition = transition; - } - - public void Add(Func task) => _tasks.Add(task); - public ITaskContext Transition(ITaskContext old) => old is not TOldContext ctx - ? throw new InvalidCastException() - : _transition(ctx); -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Core/Platform/UniScanPlatformAttribute.cs b/Client/UniScan.Client.App/Core/Platform/UniScanPlatformAttribute.cs new file mode 100644 index 0000000..d57d79d --- /dev/null +++ b/Client/UniScan.Client.App/Core/Platform/UniScanPlatformAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace UniScan.Client.App.Core.Platform; + +//hack to get correct platform assembly +//without this, android breaks +[AttributeUsage(AttributeTargets.Assembly)] +public class UniScanPlatformAttribute : Attribute; \ No newline at end of file diff --git a/Client/UniScan.Client.App/UI/ConnectionMethod/IConnectionMethodFactoryViewModel.cs b/Client/UniScan.Client.App/UI/ConnectionMethod/IConnectionMethodFactoryViewModel.cs index b438195..777b643 100644 --- a/Client/UniScan.Client.App/UI/ConnectionMethod/IConnectionMethodFactoryViewModel.cs +++ b/Client/UniScan.Client.App/UI/ConnectionMethod/IConnectionMethodFactoryViewModel.cs @@ -1,4 +1,4 @@ -using UniScan.Client.App.ViewModels; +using UniScan.Client.App.Views.ViewModel; using UniScan.Network.Client.Remote.Connection; namespace UniScan.Client.App.UI.ConnectionMethod; diff --git a/Client/UniScan.Client.App/UI/Util/RelativeUnitConverter.cs b/Client/UniScan.Client.App/UI/Conversion/RelativeUnitConverter.cs similarity index 96% rename from Client/UniScan.Client.App/UI/Util/RelativeUnitConverter.cs rename to Client/UniScan.Client.App/UI/Conversion/RelativeUnitConverter.cs index 537504e..3c25c59 100644 --- a/Client/UniScan.Client.App/UI/Util/RelativeUnitConverter.cs +++ b/Client/UniScan.Client.App/UI/Conversion/RelativeUnitConverter.cs @@ -4,7 +4,7 @@ using Avalonia.Data.Converters; using Avalonia.Markup.Xaml; -namespace UniScan.Client.App.UI.Util; +namespace UniScan.Client.App.UI.Conversion; public class RelativeUnitConverter : MarkupExtension, IValueConverter { diff --git a/Client/UniScan.Client.App/UI/ViewLocator.cs b/Client/UniScan.Client.App/UI/ViewLocator.cs index 2c27c69..78ddbdb 100755 --- a/Client/UniScan.Client.App/UI/ViewLocator.cs +++ b/Client/UniScan.Client.App/UI/ViewLocator.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Avalonia.Controls; using Avalonia.Controls.Templates; -using UniScan.Client.App.ViewModels; +using UniScan.Client.App.Views.ViewModel; namespace UniScan.Client.App.UI; diff --git a/Client/UniScan.Client.App/UniScan.Client.App.csproj b/Client/UniScan.Client.App/UniScan.Client.App.csproj index a8352e0..9980328 100755 --- a/Client/UniScan.Client.App/UniScan.Client.App.csproj +++ b/Client/UniScan.Client.App/UniScan.Client.App.csproj @@ -5,8 +5,8 @@ 14 true UniScanProject - 0.0.9 - 0.0.9 + 0.1.0 + 0.1.0 @@ -14,7 +14,9 @@ + + @@ -44,14 +46,128 @@ - + + RemoteInfoView.axaml + Code + + + RemoteLinkControl.axaml + Code + + + SidebarRemoteItemView.axaml + Code + + + DeviceInfoView.axaml + Code + + + DeviceListView.axaml + Code + + + DeviceSidebarView.axaml + Code + + MainRemotePageView.axaml Code - + + RemoteView.axaml + Code + + + NotConnectedRemotePageView.axaml + Code + + + TCPConnectionMethodFactoryView.axaml + Code + + + MainPageView.axaml + Code + + DisconnectedRemotePageView.axaml Code + + HeadedSeparatorControl.axaml + Code + + + ClientSettingsView.axaml + Code + + + LoadingView.axaml + Code + + + EmptyPageView.axaml + Code + + + AddRemoteDialog.axaml + Code + + + HeadedSeparatorControl.axaml + + + LoadingView.axaml + + + EmptyPageView.axaml + + + HomePageView.axaml + + + AddRemoteDialog.axaml + + + TCPConnectionMethodFactoryView.axaml + + + DisconnectedRemotePageView.axaml + + + NotConnectedRemotePageView.axaml + + + DeviceInfoControl.axaml + + + DeviceListControl.axaml + + + RemotePageView.axaml + + + RemoteInfoControl.axaml + + + RemoteLinkControl.axaml + + + SidebarRemoteItemControl.axaml + + + RemoteRootPageView.axaml + + + MainView.axaml + + + RootView.axaml + + + ClientSettingsView.axaml + @@ -83,9 +199,10 @@ - - + + + - + diff --git a/Client/UniScan.Client.App/UniScanApp.Initializers.cs b/Client/UniScan.Client.App/UniScanApp.Initializers.cs index f2dda92..8f71ce3 100644 --- a/Client/UniScan.Client.App/UniScanApp.Initializers.cs +++ b/Client/UniScan.Client.App/UniScanApp.Initializers.cs @@ -1,22 +1,27 @@ using System; using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Serilog; using Shiki.ModuleManagement; using Shiki.ModuleManagement.Implementations.Sources; -using UniScan.Client.App.Core.Initialization; using UniScan.Client.App.Core.Module; using UniScan.Client.App.Core.Module.Modules.Internal; +using UniScan.Client.App.Core.Pipeline.Initialization; using UniScan.Client.Core; using UniScan.Client.Core.DI.Factory; +using UniScan.Client.Core.Remote; +using UniScan.Network; +using UniScan.Network.Registry.Source.Sources; using UniScan.Platform.DependencyInjection; namespace UniScan.Client.App; public partial class UniScanApp { - internal async Task InitializeEnvironment(UniScanAppInitializationPipeline.TaskContexts.Early context) + internal async Task InitializeEnvironment(UniScanAppInitializationPipeline.TaskContexts.Early context, CancellationToken ct = default) { context.Status.Value = "Initializing environment"; @@ -33,10 +38,16 @@ internal async Task InitializeEnvironment(UniScanAppInitializationPipeline.TaskC .ForContext(); _hostEnvironment.AddToDi(context.ServiceCollection); - Log.Logger.Debug("Initialized Environment {Env}", _hostEnvironment); + + context.ServiceCollection.AddLogging(builder => + { + builder.ClearProviders(); + builder.AddSerilog(); + }); + Log.Debug("Initialized Environment {Env}", _hostEnvironment); } - internal async Task InitializeSoftwareInfo(UniScanAppInitializationPipeline.TaskContexts.Early ctx) + internal async Task InitializeSoftwareInfo(UniScanAppInitializationPipeline.TaskContexts.Early ctx, CancellationToken ct = default) { ctx.Status.Value = "Initializing SoftwareInfo"; @@ -44,7 +55,7 @@ internal async Task InitializeSoftwareInfo(UniScanAppInitializationPipeline.Task Log.Information("{Info}", SoftwareInfo); } - internal async Task InitializeModules(UniScanAppInitializationPipeline.TaskContexts.Early ctx) + internal async Task InitializeModules(UniScanAppInitializationPipeline.TaskContexts.Early ctx, CancellationToken ct = default) { ctx.Status.Value = "Initializing modules"; @@ -72,19 +83,17 @@ internal async Task InitializeModules(UniScanAppInitializationPipeline.TaskConte foreach (IUniScanClientAppModule module in ModuleStorage.Modules) { module.ConfigureDi(ctx.ServiceCollection); + ctx.ServiceCollection.AddSingleton(module); } } - internal async Task InitializeClient(UniScanAppInitializationPipeline.TaskContexts.PreClient ctx) + internal async Task InitializeClient(UniScanAppInitializationPipeline.TaskContexts.PreClient ctx, CancellationToken ct = default) { - ctx.Status.Value = "Initializing client and loading remotes"; - - ctx.Client = await UniScanClient.CreateInstanceAsync(_hostEnvironment, SoftwareInfo); - ctx.ServiceCollection.AddSingleton(ctx.Client); - ctx.ServiceCollection.AddSingleton(_ => ctx.Client.ServiceProvider.GetRequiredService()); + ctx.Status.Value = "Initializing client"; + ctx.ServiceCollection.AddUniScanClient(_hostEnvironment, SoftwareInfo); } - internal Task FinishInitialization(UniScanAppInitializationPipeline.TaskContexts.PostServiceProvider ctx) + internal Task FinishInitialization(UniScanAppInitializationPipeline.TaskContexts.PostServiceProvider ctx, CancellationToken ct = default) { try { diff --git a/Client/UniScan.Client.App/UniScanApp.axaml b/Client/UniScan.Client.App/UniScanApp.axaml index 8be1ca7..374ea27 100755 --- a/Client/UniScan.Client.App/UniScanApp.axaml +++ b/Client/UniScan.Client.App/UniScanApp.axaml @@ -1,14 +1,10 @@ @@ -17,7 +13,7 @@ - + diff --git a/Client/UniScan.Client.App/UniScanApp.axaml.cs b/Client/UniScan.Client.App/UniScanApp.axaml.cs index ca522f5..06f1930 100755 --- a/Client/UniScan.Client.App/UniScanApp.axaml.cs +++ b/Client/UniScan.Client.App/UniScanApp.axaml.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; @@ -14,9 +15,10 @@ using Shiki.Common.Identity; using Shiki.ModuleManagement; using Shiki.ModuleManagement.Implementations.Sources; -using UniScan.Client.App.Core.Initialization; using UniScan.Client.App.Core.Module; -using UniScan.Client.App.ViewModels; +using UniScan.Client.App.Core.Pipeline.Initialization; +using UniScan.Client.App.Core.Platform; +using UniScan.Client.App.Views.Global; using UniScan.Client.Core; using UniScan.Client.Core.DI.Factory; using UniScan.Client.Core.Module.Modules.Internal; @@ -25,10 +27,10 @@ using UniScan.Platform; using UniScan.Platform.DependencyInjection; using MainView = UniScan.Client.App.Views.MainView; -using MainViewModel = UniScan.Client.App.ViewModels.MainViewModel; +using MainViewModel = UniScan.Client.App.Views.MainViewModel; using MainWindow = UniScan.Client.App.Views.MainWindow; using RootView = UniScan.Client.App.Views.RootView; -using RootViewModel = UniScan.Client.App.ViewModels.RootViewModel; +using RootViewModel = UniScan.Client.App.Views.RootViewModel; namespace UniScan.Client.App; @@ -43,8 +45,10 @@ public partial class UniScanApp : Application private HostEnvironment _hostEnvironment = null!; + private static readonly Assembly PlatformAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.IsDefined(typeof(UniScanPlatformAttribute))) ?? typeof(UniScanApp).Assembly; + public static readonly Identifier Identifier = UniScanClient.ClientIdentifier.Derived("app"); - public static readonly SemVersion PlatformVersion = SemVersion.Parse(Assembly.GetEntryAssembly()!.InformationalVersionString); + public static readonly SemVersion PlatformVersion = SemVersion.Parse(PlatformAssembly.InformationalVersionString); public static readonly ClientSoftwareInfo SoftwareInfo = new( Identifier, @@ -75,7 +79,7 @@ public override void OnFrameworkInitializationCompleted() { Log.Information("Loading root view"); - RootViewModel = new RootViewModel(new LoadingViewModel(InitializationPipeline.Pipeline)); + RootViewModel = new RootViewModel(new LoadingViewModel("Loading...", InitializationPipeline.Pipeline)); switch (ApplicationLifetime) { case IClassicDesktopStyleApplicationLifetime desktop: diff --git a/Client/UniScan.Client.App/ViewModels/ClientSettingsViewModel.cs b/Client/UniScan.Client.App/ViewModels/ClientSettingsViewModel.cs deleted file mode 100644 index 106d0a2..0000000 --- a/Client/UniScan.Client.App/ViewModels/ClientSettingsViewModel.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace UniScan.Client.App.ViewModels; - -public class ClientSettingsViewModel : ViewModelBase -{ - -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Controls/DeviceInfoViewModel.cs b/Client/UniScan.Client.App/ViewModels/Controls/DeviceInfoViewModel.cs deleted file mode 100644 index b23239f..0000000 --- a/Client/UniScan.Client.App/ViewModels/Controls/DeviceInfoViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniScan.Client.Core.Config.Types; -using UniScan.Network.Data; - -namespace UniScan.Client.App.ViewModels.Controls; - -public class DeviceInfoViewModel(DeviceDto info, RemoteServer parentRemote) : ViewModelBase -{ - public DeviceDto Info { get; } = info; - public RemoteServer ParentRemote { get; } = parentRemote; - - public bool HasModelInfo => Info.Specs != null; -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Controls/DeviceListViewModel.cs b/Client/UniScan.Client.App/ViewModels/Controls/DeviceListViewModel.cs deleted file mode 100644 index f75925a..0000000 --- a/Client/UniScan.Client.App/ViewModels/Controls/DeviceListViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using ObservableCollections; -using UniScan.Client.Core.Config.Types; - -namespace UniScan.Client.App.ViewModels.Controls; - -public class DeviceListViewModel(RemoteServer server) : ViewModelBase, IDisposable -{ - public INotifyCollectionChangedSynchronizedViewList DevicesView { get; } = server.Devices.CreateView(kvp => new DeviceInfoViewModel(kvp.Value, server)).ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current); - - public void Dispose() - { - DevicesView.Dispose(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Controls/HeadedSeparatorControlModel.cs b/Client/UniScan.Client.App/ViewModels/Controls/HeadedSeparatorControlModel.cs deleted file mode 100644 index 18ec103..0000000 --- a/Client/UniScan.Client.App/ViewModels/Controls/HeadedSeparatorControlModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.ViewModels.Controls; - -public class HeadedSeparatorControlModel : UserControl -{ - -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Controls/RemoteControlViewModel.cs b/Client/UniScan.Client.App/ViewModels/Controls/RemoteControlViewModel.cs deleted file mode 100644 index 47dadd4..0000000 --- a/Client/UniScan.Client.App/ViewModels/Controls/RemoteControlViewModel.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using UniScan.Client.Core.Config.Types; - -namespace UniScan.Client.App.ViewModels.Controls; - -public partial class RemoteControlViewModel : ViewModelBase, IDisposable -{ - public RemoteServer Remote { get; } - - private readonly RemoteViewModel _viewModel; - - public RemoteControlViewModel(RemoteServer remote) - { - this.Remote = remote; - - _viewModel = new RemoteViewModel(Remote); - } - - [RelayCommand] - public async Task OnClicked() - { - WeakReferenceMessenger.Default.Send(new NavigationMessage(_viewModel), MainViewModel.Identifier); - } - - [RelayCommand] - public async Task OnConnectClicked() - { - - } - - public void Dispose() - { - _viewModel.Dispose(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Pages/EmptyPageViewModel.cs b/Client/UniScan.Client.App/ViewModels/Pages/EmptyPageViewModel.cs deleted file mode 100644 index 0bcec94..0000000 --- a/Client/UniScan.Client.App/ViewModels/Pages/EmptyPageViewModel.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace UniScan.Client.App.ViewModels.Pages; - -public class EmptyPageViewModel : ViewModelBase -{ - -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Pages/MainRemotePageViewModel.cs b/Client/UniScan.Client.App/ViewModels/Pages/MainRemotePageViewModel.cs deleted file mode 100644 index 104b9b3..0000000 --- a/Client/UniScan.Client.App/ViewModels/Pages/MainRemotePageViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniScan.Client.App.ViewModels.Controls; -using UniScan.Client.Core.Config.Types; - -namespace UniScan.Client.App.ViewModels.Pages; - -public partial class MainRemotePageViewModel(RemoteServer remote, DeviceListViewModel deviceList) : ViewModelBase -{ - public RemoteServer Remote { get; set; } = remote; - - public DeviceListViewModel DeviceList { get; } = deviceList; -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Pages/NotConnectedRemotePageViewModel.cs b/Client/UniScan.Client.App/ViewModels/Pages/NotConnectedRemotePageViewModel.cs deleted file mode 100644 index 5fb6ee7..0000000 --- a/Client/UniScan.Client.App/ViewModels/Pages/NotConnectedRemotePageViewModel.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.Input; -using Serilog; -using UniScan.Client.Core.Config.Types; - -namespace UniScan.Client.App.ViewModels.Pages; - -public partial class NotConnectedRemotePageViewModel(RemoteServer remote) : ViewModelBase -{ - public RemoteServer Remote { get; set; } = remote; - - public bool HasConnectionMethod { get; set; } = remote.ConnectionMethod != null; - - [RelayCommand] - public async Task OnAnonymousConnectClicked() - { - if (Remote.Socket.Connected) - return; - - try - { - await Remote.Socket.StartAsync(); - - if (Remote.Socket.Connected) - { - _ = Remote.RunConnectionAsync(); - } - else - { - Log.Error("Failed to connect!"); - } - } - catch (Exception ex) - { - Log.Information(ex, "FUCK!"); - } - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/RemoteViewModel.cs b/Client/UniScan.Client.App/ViewModels/RemoteViewModel.cs deleted file mode 100644 index ddaf31c..0000000 --- a/Client/UniScan.Client.App/ViewModels/RemoteViewModel.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using Avalonia.Threading; -using CommunityToolkit.Mvvm.Input; -using Shiki.Common.Identity; -using Shiki.Common.Identity.Slug; -using Shiki.Common.Identity.Slug.Formatting.Formatters; -using UniScan.Client.App.ViewModels.Controls; -using UniScan.Client.App.ViewModels.Pages; -using UniScan.Client.Core; -using UniScan.Client.Core.Config.Types; -using UniScan.Network.Util; - -namespace UniScan.Client.App.ViewModels; - -public class RemoteViewModel : SubPagedViewModelBase, IDisposable -{ - public RemoteServer Remote { get; set; } - - private readonly NotConnectedRemotePageViewModel _notConnectedPage; - private MainRemotePageViewModel? _mainPage; - - public DeviceListViewModel DeviceList { get; } - - public RemoteViewModel(RemoteServer remote) : base(new NotConnectedRemotePageViewModel(remote), UniScanApp.Identifier.Derived("view_model", "remote", new Slug(remote.DisplayName ?? Guid.NewGuid().ToString()))) - { - this._notConnectedPage = (NotConnectedRemotePageViewModel)CurrentSubpage; - this.Remote = remote; - - DeviceList = new DeviceListViewModel(remote); - - this.Remote.Socket.ConnectionState.Disconnected += OnDisconnected; - this.Remote.Socket.ConnectionState.Connected += OnConnected; - } - - private void OnDisconnected(object? sender, ConnectionStateTracker.ConnectionStateChangedEventArgs eventArgs) - { - Dispatcher.UIThread.Post(() => - { - string reason = "Unknown reason, maybe there is more info in the logs."; - if (eventArgs.Channel.HasAttribute(ServerAttributes.DisconnectReasonAttribute)) - { - reason = eventArgs.Channel.GetAttribute(ServerAttributes.DisconnectReasonAttribute).Get(); - } - - this.CurrentSubpage = new DisconnectedRemotePageViewModel(reason) - { - OkClicked = new RelayCommand(() => - { - this.CurrentSubpage = _notConnectedPage; - }) - }; - - if (_mainPage is IDisposable d) - { - d.Dispose(); - } - _mainPage = null; - }); - - } - - private void OnConnected(object? sender, ConnectionStateTracker.ConnectionStateChangedEventArgs eventArgs) - { - Dispatcher.UIThread.Post(() => - { - _mainPage ??= new MainRemotePageViewModel(Remote, DeviceList); - this.CurrentSubpage = _mainPage; - }); - } - - public void Dispose() - { - if (this.Remote?.Socket?.ConnectionState is { } state) - { - state.Disconnected -= OnDisconnected; - state.Connected -= OnConnected; - } - - DeviceList.Dispose(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/DeviceInfoView.axaml.cs b/Client/UniScan.Client.App/Views/Controls/DeviceInfoView.axaml.cs deleted file mode 100644 index 4bf7467..0000000 --- a/Client/UniScan.Client.App/Views/Controls/DeviceInfoView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Controls; - -public partial class DeviceInfoView : UserControl -{ - public DeviceInfoView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml b/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml deleted file mode 100644 index d860bd6..0000000 --- a/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml.cs b/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml.cs deleted file mode 100644 index 8b6e8c3..0000000 --- a/Client/UniScan.Client.App/Views/Controls/DeviceListView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Controls; - -public partial class DeviceListView : UserControl -{ - public DeviceListView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml.cs b/Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml.cs deleted file mode 100644 index 2b903db..0000000 --- a/Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Controls; - -public partial class DeviceSidebarView : UserControl -{ - public DeviceSidebarView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml.cs b/Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml.cs deleted file mode 100644 index 47038d2..0000000 --- a/Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Controls; - -public partial class RemoteControlView : UserControl -{ - public RemoteControlView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Dialogs/AddRemoteDialogView.axaml.cs b/Client/UniScan.Client.App/Views/Dialogs/AddRemoteDialogView.axaml.cs deleted file mode 100644 index dbb26b5..0000000 --- a/Client/UniScan.Client.App/Views/Dialogs/AddRemoteDialogView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Dialogs; - -public partial class AddRemoteDialogView : UserControl -{ - public AddRemoteDialogView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml b/Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml similarity index 83% rename from Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml rename to Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml index c046718..492c237 100644 --- a/Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml +++ b/Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml @@ -3,5 +3,5 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.Pages.EmptyPageView"> + x:Class="UniScan.Client.App.Views.Global.EmptyPageView"> diff --git a/Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml.cs b/Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml.cs similarity index 77% rename from Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml.cs rename to Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml.cs index 586f0a2..2ede4b4 100644 --- a/Client/UniScan.Client.App/Views/Pages/EmptyPageView.axaml.cs +++ b/Client/UniScan.Client.App/Views/Global/EmptyPageView.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace UniScan.Client.App.Views.Pages; +namespace UniScan.Client.App.Views.Global; public partial class EmptyPageView : UserControl { diff --git a/Client/UniScan.Client.App/Views/Global/EmptyPageViewModel.cs b/Client/UniScan.Client.App/Views/Global/EmptyPageViewModel.cs new file mode 100644 index 0000000..74914d6 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Global/EmptyPageViewModel.cs @@ -0,0 +1,8 @@ +using UniScan.Client.App.Views.ViewModel; + +namespace UniScan.Client.App.Views.Global; + +public class EmptyPageViewModel : ViewModelBase +{ + +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml similarity index 61% rename from Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml rename to Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml index fd0326e..6013cc9 100644 --- a/Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml +++ b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml @@ -2,13 +2,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:UniScan.Client.App.Views.Controls" - xmlns:controls="clr-namespace:UniScan.Client.App.Views.Controls" - xmlns:local1="clr-namespace:UniScan.Client.App.Views.Controls" + xmlns:global="clr-namespace:UniScan.Client.App.Views.Global" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.Controls.HeadedSeparatorControl"> + x:Class="UniScan.Client.App.Views.Global.HeadedSeparatorControl"> - + diff --git a/Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml.cs b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml.cs similarity index 91% rename from Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml.cs rename to Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml.cs index ee64e7b..4a25f25 100644 --- a/Client/UniScan.Client.App/Views/Controls/HeadedSeparatorControl.axaml.cs +++ b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControl.axaml.cs @@ -1,7 +1,7 @@ using Avalonia; using Avalonia.Controls; -namespace UniScan.Client.App.Views.Controls; +namespace UniScan.Client.App.Views.Global; public partial class HeadedSeparatorControl : UserControl { diff --git a/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControlViewModel.cs b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControlViewModel.cs new file mode 100644 index 0000000..0ad2355 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Global/HeadedSeparatorControlViewModel.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Global; + +public class HeadedSeparatorControlViewModel : UserControl +{ + +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/LoadingView.axaml b/Client/UniScan.Client.App/Views/Global/LoadingView.axaml similarity index 53% rename from Client/UniScan.Client.App/Views/LoadingView.axaml rename to Client/UniScan.Client.App/Views/Global/LoadingView.axaml index 1d1af18..a0c6ec0 100644 --- a/Client/UniScan.Client.App/Views/LoadingView.axaml +++ b/Client/UniScan.Client.App/Views/Global/LoadingView.axaml @@ -2,24 +2,21 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:viewModels="clr-namespace:UniScan.Client.App.ViewModels" - xmlns:viewModels1="clr-namespace:UniScan.Client.App.ViewModels" - xmlns:viewModels2="clr-namespace:UniScan.Client.App.ViewModels" - xmlns:app="clr-namespace:UniScan.Client.App" + xmlns:global="clr-namespace:UniScan.Client.App.Views.Global" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.LoadingView" - x:DataType="viewModels2:LoadingViewModel"> + x:Class="UniScan.Client.App.Views.Global.LoadingView" + x:DataType="global:LoadingViewModel"> - - Loading... + + - + diff --git a/Client/UniScan.Client.App/Views/LoadingView.axaml.cs b/Client/UniScan.Client.App/Views/Global/LoadingView.axaml.cs similarity index 77% rename from Client/UniScan.Client.App/Views/LoadingView.axaml.cs rename to Client/UniScan.Client.App/Views/Global/LoadingView.axaml.cs index 03755dd..f140d20 100644 --- a/Client/UniScan.Client.App/Views/LoadingView.axaml.cs +++ b/Client/UniScan.Client.App/Views/Global/LoadingView.axaml.cs @@ -1,6 +1,6 @@ using Avalonia.Controls; -namespace UniScan.Client.App.Views; +namespace UniScan.Client.App.Views.Global; public partial class LoadingView : UserControl { diff --git a/Client/UniScan.Client.App/ViewModels/LoadingViewModel.cs b/Client/UniScan.Client.App/Views/Global/LoadingViewModel.cs similarity index 52% rename from Client/UniScan.Client.App/ViewModels/LoadingViewModel.cs rename to Client/UniScan.Client.App/Views/Global/LoadingViewModel.cs index af4cc85..a17e3b8 100644 --- a/Client/UniScan.Client.App/ViewModels/LoadingViewModel.cs +++ b/Client/UniScan.Client.App/Views/Global/LoadingViewModel.cs @@ -1,15 +1,18 @@ -using UniScan.Client.App.Core.Pipeline; +using Shiki.TaskPipeline; +using UniScan.Client.App.Views.ViewModel; -namespace UniScan.Client.App.ViewModels; +namespace UniScan.Client.App.Views.Global; public class LoadingViewModel : ViewModelBase { public static string VersionString => $"UniScan Client v{UniScanApp.SoftwareInfo.Version} (Platform v{UniScanApp.PlatformVersion})"; public TaskPipeline TaskPipeline { get; } + public string LoadingText { get; } - public LoadingViewModel(TaskPipeline taskPipeline) + public LoadingViewModel(string loadingText, TaskPipeline taskPipeline) { + LoadingText = loadingText; TaskPipeline = taskPipeline; } } \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Home/HomePageView.axaml b/Client/UniScan.Client.App/Views/Home/HomePageView.axaml new file mode 100644 index 0000000..8273b1d --- /dev/null +++ b/Client/UniScan.Client.App/Views/Home/HomePageView.axaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/UniScan.Client.App/Views/Home/HomePageView.axaml.cs b/Client/UniScan.Client.App/Views/Home/HomePageView.axaml.cs new file mode 100644 index 0000000..c24b492 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Home/HomePageView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Home; + +public partial class HomePageView : ContentPage +{ + public HomePageView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/ViewModels/Pages/MainPageViewModel.cs b/Client/UniScan.Client.App/Views/Home/HomePageViewModel.cs similarity index 77% rename from Client/UniScan.Client.App/ViewModels/Pages/MainPageViewModel.cs rename to Client/UniScan.Client.App/Views/Home/HomePageViewModel.cs index b288070..0397a14 100644 --- a/Client/UniScan.Client.App/ViewModels/Pages/MainPageViewModel.cs +++ b/Client/UniScan.Client.App/Views/Home/HomePageViewModel.cs @@ -2,33 +2,34 @@ using System.Collections.Generic; using ObservableCollections; using R3; -using UniScan.Client.Core.Config.Remote; -using UniScan.Client.Core.Config.Types; +using UniScan.Client.App.Views.Remote; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Client.Core.Remote; -namespace UniScan.Client.App.ViewModels.Pages; +namespace UniScan.Client.App.Views.Home; -public class RemoteConnectionStateFilter : ISynchronizedViewFilter +public class RemoteConnectionStateFilter : ISynchronizedViewFilter { - public bool IsMatch(RemoteServer value, RemoteViewModel view) + public bool IsMatch(RemoteServer value, RemoteRootPageViewModel rootPageView) { return value.Connected.CurrentValue; } } -public partial class MainPageViewModel : ViewModelBase, IDisposable +public partial class HomePageViewModel : ViewModelBase, IDisposable { - public INotifyCollectionChangedSynchronizedViewList ConnectedRemotesView { get; } - private readonly ISynchronizedView _remotesView; + public INotifyCollectionChangedSynchronizedViewList ConnectedRemotesView { get; } + private readonly ISynchronizedView _remotesView; private readonly Dictionary _subscriptions = []; private readonly IRemoteManager _remoteManager; - public MainPageViewModel(IRemoteManager remoteManager) + public HomePageViewModel(IServiceProvider provider, IRemoteManager remoteManager) { _remoteManager = remoteManager; - _remotesView = remoteManager.Remotes.CreateView(remote => new RemoteViewModel(remote)); + _remotesView = remoteManager.Remotes.CreateView(remote => new RemoteRootPageViewModel(provider, remote)); _remotesView.AttachFilter(new RemoteConnectionStateFilter()); ConnectedRemotesView = _remotesView.ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current); diff --git a/Client/UniScan.Client.App/Views/MainView.axaml b/Client/UniScan.Client.App/Views/MainView.axaml index c4ef707..f134d3e 100644 --- a/Client/UniScan.Client.App/Views/MainView.axaml +++ b/Client/UniScan.Client.App/Views/MainView.axaml @@ -1,30 +1,22 @@ + xmlns:views="clr-namespace:UniScan.Client.App.Views" + xmlns:global="clr-namespace:UniScan.Client.App.Views.Global" + xmlns:remote="clr-namespace:UniScan.Client.App.Views.Remote" + x:DataType="views:MainViewModel"> - + + DrawerLayoutBehavior="CompactOverlay" x:Name="MainDrawerPage" + CompactDrawerLength="60"> @@ -32,27 +24,27 @@ - + - + - - + + - --> - - - - - - - - - - diff --git a/Client/UniScan.Client.App/Views/Pages/MainRemotePageView.axaml.cs b/Client/UniScan.Client.App/Views/Pages/MainRemotePageView.axaml.cs deleted file mode 100644 index 9081ab0..0000000 --- a/Client/UniScan.Client.App/Views/Pages/MainRemotePageView.axaml.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Avalonia.Controls; - -namespace UniScan.Client.App.Views.Pages; - -public partial class MainRemotePageView : UserControl -{ - public MainRemotePageView() - { - InitializeComponent(); - } -} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Pages/NotConnectedRemotePageView.axaml b/Client/UniScan.Client.App/Views/Pages/NotConnectedRemotePageView.axaml deleted file mode 100644 index 4d76553..0000000 --- a/Client/UniScan.Client.App/Views/Pages/NotConnectedRemotePageView.axaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - + - + - - - - diff --git a/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControl.axaml.cs b/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControl.axaml.cs new file mode 100644 index 0000000..f3e377f --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControl.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote.Device; + +public partial class DeviceInfoControl : UserControl +{ + public DeviceInfoControl() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControlViewModel.cs b/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControlViewModel.cs new file mode 100644 index 0000000..1b9a3b6 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/DeviceInfoControlViewModel.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.Input; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Client.Core.Remote; +using UniScan.Network.Data; +using UniScan.Network.Packet.Packets.Serverbound.Subscription; + +namespace UniScan.Client.App.Views.Remote.Device; + +public partial class DeviceInfoControlViewModel(DeviceDto info, RemoteServer parentRemote) : ViewModelBase +{ + public DeviceDto Info { get; } = info; + public RemoteServer ParentRemote { get; } = parentRemote; + + public bool HasModelInfo => Info.Specs != null; + + [RelayCommand] + public async Task OnSubscribe() + { + await ParentRemote.Socket.SendRequestAsync(SubscribePacket.CreateRequest(Info.ScannerIdentifier)); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml new file mode 100644 index 0000000..d00873e --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml.cs b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml.cs new file mode 100644 index 0000000..96446d5 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControl.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote.Device; + +public partial class DeviceListControl : UserControl +{ + public DeviceListControl() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControlViewModel.cs b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControlViewModel.cs new file mode 100644 index 0000000..51e7f25 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/DeviceListControlViewModel.cs @@ -0,0 +1,16 @@ +using System; +using ObservableCollections; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Client.Core.Remote; + +namespace UniScan.Client.App.Views.Remote.Device; + +public class DeviceListControlViewModel(RemoteServer server) : ViewModelBase, IDisposable +{ + public INotifyCollectionChangedSynchronizedViewList DevicesView { get; } = server.Devices.CreateView(kvp => new DeviceInfoControlViewModel(kvp.Value, server)).ToNotifyCollectionChanged(SynchronizationContextCollectionEventDispatcher.Current); + + public void Dispose() + { + DevicesView.Dispose(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml b/Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml similarity index 81% rename from Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml rename to Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml index ea1652e..29ed018 100644 --- a/Client/UniScan.Client.App/Views/Controls/DeviceSidebarView.axaml +++ b/Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml @@ -3,6 +3,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.Controls.DeviceSidebarView"> + x:Class="UniScan.Client.App.Views.Remote.Device.SidebarDeviceItemControl"> Welcome to Avalonia! diff --git a/Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml.cs b/Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml.cs new file mode 100644 index 0000000..b6820a8 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/Device/SidebarDeviceItemControl.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote.Device; + +public partial class SidebarDeviceItemControl : UserControl +{ + public SidebarDeviceItemControl() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml new file mode 100644 index 0000000..92fc214 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml.cs b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml.cs new file mode 100644 index 0000000..68b4edb --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControl.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote; + +public partial class RemoteInfoControl : UserControl +{ + public RemoteInfoControl() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteInfoControlViewModel.cs b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControlViewModel.cs new file mode 100644 index 0000000..331550f --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteInfoControlViewModel.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Network.Data.Info.Remote; + +namespace UniScan.Client.App.Views.Remote; + +public class RemoteInfoControlViewModel(RemoteInfo info) : ViewModelBase +{ + public RemoteInfo Info { get; } = info; + + public List Links { get; set; } = + [.. info.Branding.Links.Select(l => new RemoteLinkControlViewModel(l))]; +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml new file mode 100644 index 0000000..b0634fd --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml.cs b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml.cs new file mode 100644 index 0000000..b3aefb7 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControl.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote; + +public partial class RemoteLinkControl : UserControl +{ + public RemoteLinkControl() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteLinkControlViewModel.cs b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControlViewModel.cs new file mode 100644 index 0000000..0d6910e --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteLinkControlViewModel.cs @@ -0,0 +1,9 @@ +using UniScan.Client.App.Views.ViewModel; +using UniScan.Network.Data.Info.Remote; + +namespace UniScan.Client.App.Views.Remote; + +public class RemoteLinkControlViewModel(RemoteLink link) : ViewModelBase +{ + public RemoteLink Link { get; } = link; +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml b/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml new file mode 100644 index 0000000..fc0055a --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml.cs b/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml.cs new file mode 100644 index 0000000..726bdf1 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemotePageView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote; + +public partial class RemotePageView : UserControl +{ + public RemotePageView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemotePageViewModel.cs b/Client/UniScan.Client.App/Views/Remote/RemotePageViewModel.cs new file mode 100644 index 0000000..a988c32 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemotePageViewModel.cs @@ -0,0 +1,37 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using R3; +using UniScan.Client.App.Views.Remote.Device; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Client.Core.Remote; + +namespace UniScan.Client.App.Views.Remote; + +public partial class RemotePageViewModel : ViewModelBase, IDisposable +{ + public RemoteServer Remote { get; set; } + + [ObservableProperty] + public partial RemoteInfoControlViewModel? RemoteInfoViewModel { get; set; } + + public DeviceListControlViewModel DeviceListControl { get; } + + private readonly CompositeDisposable _disposables = new(); + + public RemotePageViewModel(RemoteServer remote, DeviceListControlViewModel deviceListControl, Observable remoteInfoViewModel) + { + Remote = remote; + DeviceListControl = deviceListControl; + + remoteInfoViewModel.Subscribe(r => + { + RemoteInfoViewModel = r; + }).AddTo(_disposables); + } + + public void Dispose() + { + DeviceListControl.Dispose(); + _disposables.Dispose(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/RemoteView.axaml b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml similarity index 61% rename from Client/UniScan.Client.App/Views/RemoteView.axaml rename to Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml index 7da8933..575f971 100644 --- a/Client/UniScan.Client.App/Views/RemoteView.axaml +++ b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml @@ -2,10 +2,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:viewModels="clr-namespace:UniScan.Client.App.ViewModels" - xmlns:viewModels1="clr-namespace:UniScan.Client.App.ViewModels" + xmlns:remote="clr-namespace:UniScan.Client.App.Views.Remote" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.RemoteView" - x:DataType="viewModels1:RemoteViewModel"> + x:Class="UniScan.Client.App.Views.Remote.RemoteRootPageView" + x:DataType="remote:RemoteRootPageViewModel"> diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml.cs b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml.cs new file mode 100644 index 0000000..a6ce130 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace UniScan.Client.App.Views.Remote; + +public partial class RemoteRootPageView : UserControl +{ + public RemoteRootPageView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Remote/RemoteRootPageViewModel.cs b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageViewModel.cs new file mode 100644 index 0000000..4f20044 --- /dev/null +++ b/Client/UniScan.Client.App/Views/Remote/RemoteRootPageViewModel.cs @@ -0,0 +1,109 @@ +using System; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.Input; +using R3; +using Shiki.Common.Identity.Slug; +using Shiki.Common.Identity.Slug.Formatting.Formatters; +using UniScan.Client.App.Views.Global; +using UniScan.Client.App.Views.Remote.Connection; +using UniScan.Client.App.Views.Remote.Device; +using UniScan.Client.App.Views.ViewModel; +using UniScan.Client.Core.Remote; +using UniScan.Network.Util; + +namespace UniScan.Client.App.Views.Remote; + +public class RemoteRootPageViewModel : SubPagedViewModelBase, IDisposable +{ + public RemoteServer Remote { get; set; } + + private readonly NotConnectedRemotePageViewModel _notConnectedPage; + private RemotePageViewModel? _mainPage; + + public DeviceListControlViewModel DeviceListControl { get; } + + public BehaviorSubject InfoViewModelStream { get; } = new(null); + public RemoteInfoControlViewModel? InfoViewModel => InfoViewModelStream.Value; + + public RemoteRootPageViewModel(IServiceProvider provider, RemoteServer remote) : base(new NotConnectedRemotePageViewModel(provider, remote), UniScanApp.Identifier.Derived("view_model", "remote", new Slug(Guid.NewGuid().ToString()))) + { + this._notConnectedPage = (NotConnectedRemotePageViewModel)CurrentSubpage; + this._notConnectedPage.OnConnecting += (pipeline) => + { + this.CurrentSubpage = new LoadingViewModel("Connecting...", pipeline.Pipeline); + }; + + this._notConnectedPage.OnConnectFailed += (ex) => + { + this.CurrentSubpage = new DisconnectedRemotePageViewModel("Failed to connect to server! " + ex.Message, Remote) + { + OkClicked = new RelayCommand(() => + { + this.CurrentSubpage = _notConnectedPage; + }) + }; + }; + + this.Remote = remote; + Remote.RemoteInfo.Subscribe((v) => + { + RemoteInfoControlViewModel? n = v != null ? new RemoteInfoControlViewModel(v) : null; + + InfoViewModelStream.OnNext(n); + OnPropertyChanged(nameof(InfoViewModel)); + }); + + DeviceListControl = new DeviceListControlViewModel(remote); + + this.Remote.Socket.ConnectionState.Disconnected += OnDisconnected; + this.Remote.Socket.ConnectionState.Connected += OnConnected; + } + + private void OnDisconnected(object? sender, ConnectionStateTracker.ConnectionStateChangedEventArgs eventArgs) + { + Dispatcher.UIThread.Post(() => + { + string reason = "Unknown reason, maybe there is more info in the logs."; + if (eventArgs.Channel.HasAttribute(ServerAttributes.DisconnectReasonAttribute)) + { + reason = eventArgs.Channel.GetAttribute(ServerAttributes.DisconnectReasonAttribute).Get(); + } + + this.CurrentSubpage = new DisconnectedRemotePageViewModel(reason, Remote) + { + OkClicked = new RelayCommand(() => + { + this.CurrentSubpage = _notConnectedPage; + }) + }; + + //todo can we PLEASE manage state better!!!!!! + if (_mainPage is IDisposable d) + { + d.Dispose(); + } + _mainPage = null; + }); + + } + + private void OnConnected(object? sender, ConnectionStateTracker.ConnectionStateChangedEventArgs eventArgs) + { + Dispatcher.UIThread.Post(() => + { + _mainPage ??= new RemotePageViewModel(Remote, DeviceListControl, InfoViewModelStream.AsObservable()); + this.CurrentSubpage = _mainPage; + }); + } + + public void Dispose() + { + if (this.Remote?.Socket?.ConnectionState is { } state) + { + state.Disconnected -= OnDisconnected; + state.Connected -= OnConnected; + } + + DeviceListControl.Dispose(); + } +} \ No newline at end of file diff --git a/Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml b/Client/UniScan.Client.App/Views/Remote/SidebarRemoteItemControl.axaml similarity index 53% rename from Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml rename to Client/UniScan.Client.App/Views/Remote/SidebarRemoteItemControl.axaml index 1e60a00..82d20b1 100644 --- a/Client/UniScan.Client.App/Views/Controls/RemoteControlView.axaml +++ b/Client/UniScan.Client.App/Views/Remote/SidebarRemoteItemControl.axaml @@ -2,16 +2,16 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:controls="clr-namespace:UniScan.Client.App.ViewModels.Controls" - xmlns:controls1="clr-namespace:UniScan.Client.App.Views.Controls" - xmlns:controls2="clr-namespace:UniScan.Client.App.ViewModels.Controls" + xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia" + xmlns:remote="clr-namespace:UniScan.Client.App.Views.Remote" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="UniScan.Client.App.Views.Controls.RemoteControlView" - x:DataType="controls2:RemoteControlViewModel"> + x:Class="UniScan.Client.App.Views.Remote.SidebarRemoteItemControl" + x:DataType="remote:SidebarRemoteItemControlViewModel">