commit e86b971d6245480cb455cd1816fe162b206ca2aa Author: Selim Mustafaev Date: Wed Jul 19 00:42:11 2023 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39c9242 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea/ diff --git a/AutoCat.sln b/AutoCat.sln new file mode 100644 index 0000000..ac88a63 --- /dev/null +++ b/AutoCat.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoCat", "AutoCat\AutoCat.csproj", "{9FB32BFB-805B-49F2-B5A2-E1F5A0C06A62}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoCatCore", "AutoCatCore\AutoCatCore.csproj", "{39A58D42-E1FA-4442-A7E3-7616D35AC770}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9FB32BFB-805B-49F2-B5A2-E1F5A0C06A62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FB32BFB-805B-49F2-B5A2-E1F5A0C06A62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FB32BFB-805B-49F2-B5A2-E1F5A0C06A62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FB32BFB-805B-49F2-B5A2-E1F5A0C06A62}.Release|Any CPU.Build.0 = Release|Any CPU + {39A58D42-E1FA-4442-A7E3-7616D35AC770}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39A58D42-E1FA-4442-A7E3-7616D35AC770}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39A58D42-E1FA-4442-A7E3-7616D35AC770}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39A58D42-E1FA-4442-A7E3-7616D35AC770}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AutoCat.sln.DotSettings.user b/AutoCat.sln.DotSettings.user new file mode 100644 index 0000000..e702cc0 --- /dev/null +++ b/AutoCat.sln.DotSettings.user @@ -0,0 +1,6 @@ + + ShowAndRun + <AssemblyExplorer> + <Assembly Path="/Users/selim/.nuget/packages/avalonia/11.0.0/ref/net6.0/Avalonia.Markup.dll" /> + <Assembly Path="/Users/selim/Documents/dev/AutoCatAvalonia/AutoCat/bin/Debug/net7.0/Avalonia.Base.dll" /> +</AssemblyExplorer> \ No newline at end of file diff --git a/AutoCat/.DS_Store b/AutoCat/.DS_Store new file mode 100644 index 0000000..41f4f38 Binary files /dev/null and b/AutoCat/.DS_Store differ diff --git a/AutoCat/App.axaml b/AutoCat/App.axaml new file mode 100644 index 0000000..4527556 --- /dev/null +++ b/AutoCat/App.axaml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/AutoCat/App.axaml.cs b/AutoCat/App.axaml.cs new file mode 100644 index 0000000..7b79f40 --- /dev/null +++ b/AutoCat/App.axaml.cs @@ -0,0 +1,58 @@ +using AutoCat.Utils; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using AutoCat.ViewModels; +using AutoCat.Views; +using AutoCatCore.Services.Api; +using AutoCatCore.Services.Storage; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace AutoCat; + +public partial class App : Application +{ + public IHost AppHost { get; private set; } + + private static void RegisterDependencies(HostBuilderContext context, IServiceCollection services) + { + // Services + services.AddSingleton(); + services.AddSingleton(); + + // ViewModels + services.AddTransient(); + services.AddTransient(); + + // Windows + services.AddWindowFactory(); + services.AddWindowFactory(); + } + + public App() + { + AppHost = Host.CreateDefaultBuilder() + .ConfigureServices(RegisterDependencies) + .Build(); + } + + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var storageService = AppHost.Services.GetRequiredService(); + + desktop.MainWindow = storageService.IsLoggedIn + ? AppHost.Services.GetRequiredService() + : AppHost.Services.GetRequiredService(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/AutoCat/Assets/avalonia-logo.ico b/AutoCat/Assets/avalonia-logo.ico new file mode 100644 index 0000000..da8d49f Binary files /dev/null and b/AutoCat/Assets/avalonia-logo.ico differ diff --git a/AutoCat/AutoCat.csproj b/AutoCat/AutoCat.csproj new file mode 100644 index 0000000..4d7d7a2 --- /dev/null +++ b/AutoCat/AutoCat.csproj @@ -0,0 +1,34 @@ + + + WinExe + net7.0 + enable + true + app.manifest + false + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoCat/Extensions/TernaryExtension.cs b/AutoCat/Extensions/TernaryExtension.cs new file mode 100644 index 0000000..994d85c --- /dev/null +++ b/AutoCat/Extensions/TernaryExtension.cs @@ -0,0 +1,31 @@ +using System; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.MarkupExtensions; + +namespace AutoCat.Extensions; + +public class TernaryExtension : MarkupExtension +{ + public TernaryExtension() + { + } + + public string Path { get; set; } + + public object True { get; set; } + + public object False { get; set; } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var binding = new ReflectionBindingExtension(Path) + { + Mode = BindingMode.OneWay, + Converter = new FuncValueConverter(e => e ? True : False) + }; + + return binding.ProvideValue(serviceProvider); + } +} \ No newline at end of file diff --git a/AutoCat/Mocks/AuthWindowViewModelMock.cs b/AutoCat/Mocks/AuthWindowViewModelMock.cs new file mode 100644 index 0000000..79a7b17 --- /dev/null +++ b/AutoCat/Mocks/AuthWindowViewModelMock.cs @@ -0,0 +1,12 @@ +using System.Reactive; +using AutoCat.ViewModels; +using ReactiveUI; + +namespace AutoCat.Mocks; + +public class AuthWindowViewModelMock: ViewModelBase +{ + public string Email { get; set; } = ""; + public string Password { get; set; } = ""; + public ReactiveCommand LoginCommand { get; } = ReactiveCommand.Create(() => { }); +} \ No newline at end of file diff --git a/AutoCat/Program.cs b/AutoCat/Program.cs new file mode 100644 index 0000000..427afc4 --- /dev/null +++ b/AutoCat/Program.cs @@ -0,0 +1,22 @@ +using Avalonia; +using Avalonia.ReactiveUI; +using System; + +namespace AutoCat; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .LogToTrace() + .UseReactiveUI(); +} \ No newline at end of file diff --git a/AutoCat/Utils/AbstractFactory.cs b/AutoCat/Utils/AbstractFactory.cs new file mode 100644 index 0000000..b54cd54 --- /dev/null +++ b/AutoCat/Utils/AbstractFactory.cs @@ -0,0 +1,23 @@ +using System; + +namespace AutoCat.Utils; + +public interface IAbstractFactory +{ + T Create(); +} + +public class AbstractFactory : IAbstractFactory +{ + private readonly Func _factory; + + public AbstractFactory(Func factory) + { + _factory = factory; + } + + public T Create() + { + return _factory(); + } +} \ No newline at end of file diff --git a/AutoCat/Utils/DIExtentions.cs b/AutoCat/Utils/DIExtentions.cs new file mode 100644 index 0000000..5f9d529 --- /dev/null +++ b/AutoCat/Utils/DIExtentions.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia.Controls; +using Microsoft.Extensions.DependencyInjection; + +namespace AutoCat.Utils; + +public static class DependencyInjectionExtensions +{ + public static void AddWindowFactory(this IServiceCollection services) where TWindow: Window + { + services.AddTransient(); + services.AddSingleton>(x => () => x.GetService()!); + services.AddSingleton, AbstractFactory>(); + } +} \ No newline at end of file diff --git a/AutoCat/Utils/ViewModelProvider.cs b/AutoCat/Utils/ViewModelProvider.cs new file mode 100644 index 0000000..ab6b1f6 --- /dev/null +++ b/AutoCat/Utils/ViewModelProvider.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia; +using Avalonia.Markup.Xaml; + +namespace AutoCat.Utils; + +public class ViewModelProvider: MarkupExtension +{ + private readonly Type _viewModelType; + + public ViewModelProvider(Type viewModelType) + { + _viewModelType = viewModelType; + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + if (Application.Current is App app) + return app.AppHost.Services.GetService(_viewModelType) + ?? throw new Exception("Error finding ViewModel"); + else + throw new Exception("Error getting App instance"); + } +} \ No newline at end of file diff --git a/AutoCat/ViewLocator.cs b/AutoCat/ViewLocator.cs new file mode 100644 index 0000000..0ffcdd0 --- /dev/null +++ b/AutoCat/ViewLocator.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using AutoCat.ViewModels; + +namespace AutoCat; + +public class ViewLocator : IDataTemplate +{ + public Control Build(object data) + { + var name = data.GetType().FullName!.Replace("ViewModel", "View"); + var type = Type.GetType(name); + + if (type != null) + { + return (Control)Activator.CreateInstance(type)!; + } + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object data) + { + return data is ViewModelBase; + } +} \ No newline at end of file diff --git a/AutoCat/ViewModels/AuthWindowViewModel.cs b/AutoCat/ViewModels/AuthWindowViewModel.cs new file mode 100644 index 0000000..913424f --- /dev/null +++ b/AutoCat/ViewModels/AuthWindowViewModel.cs @@ -0,0 +1,83 @@ +using System; +using System.Reactive; +using System.Threading.Tasks; +using AutoCatCore.Services.Api; +using AutoCatCore.Services.Storage; +using ReactiveUI; +using MsBox.Avalonia; +using MsBox.Avalonia.Dto; +using MsBox.Avalonia.Enums; + +namespace AutoCat.ViewModels; + +public class AuthWindowViewModel: ViewModelBase +{ + #region Dependencies + + private readonly IApiService _apiService; + private readonly IStorageService _storageService; + + #endregion + + #region Fields + + private string _email = ""; + private string _password = ""; + + #endregion + + #region Properties + + public string Email + { + get => _email; + set => this.RaiseAndSetIfChanged(ref _email, value); + } + + public string Password + { + get => _password; + set => this.RaiseAndSetIfChanged(ref _password, value); + } + + #endregion + + #region Commands + + public ReactiveCommand LoginCommand { get; } + + #endregion + + public AuthWindowViewModel(IApiService apiService, IStorageService storageService) + { + _apiService = apiService; + _storageService = storageService; + + var loginEnabled = this.WhenAnyValue( + vm => vm.Email, + vm => vm.Password, + (login, password) => login.Length > 3 && password.Length > 3); + + LoginCommand = ReactiveCommand.CreateFromTask(Login, loginEnabled); + LoginCommand.ThrownExceptions.Subscribe(ShowError); + } + + private async Task Login() + { + var user = await _apiService.Login(Email, Password); + //await _storageService.SetUser(user); + Close(); + } + + private async void ShowError(Exception error) + { + var msgBox = MessageBoxManager.GetMessageBoxStandard(new MessageBoxStandardParams + { + ButtonDefinitions = ButtonEnum.Ok, + ContentTitle = "Error", + ContentMessage = error.Message + }); + + await msgBox.ShowAsync(); + } +} \ No newline at end of file diff --git a/AutoCat/ViewModels/MainWindowViewModel.cs b/AutoCat/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..ecfdb67 --- /dev/null +++ b/AutoCat/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,6 @@ +namespace AutoCat.ViewModels; + +public class MainWindowViewModel : ViewModelBase +{ + public string Greeting => "Welcome to Avalonia!"; +} \ No newline at end of file diff --git a/AutoCat/ViewModels/ViewModelBase.cs b/AutoCat/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..a54e22e --- /dev/null +++ b/AutoCat/ViewModels/ViewModelBase.cs @@ -0,0 +1,14 @@ +using System; +using ReactiveUI; + +namespace AutoCat.ViewModels; + +public class ViewModelBase : ReactiveObject +{ + public event EventHandler? ClosingRequest; + + protected void Close() + { + ClosingRequest?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file diff --git a/AutoCat/Views/AuthWindow.axaml b/AutoCat/Views/AuthWindow.axaml new file mode 100644 index 0000000..0670b33 --- /dev/null +++ b/AutoCat/Views/AuthWindow.axaml @@ -0,0 +1,37 @@ + + + + + + + + + +