Initial commit

This commit is contained in:
Selim Mustafaev 2023-07-19 00:42:11 +03:00
commit e86b971d62
44 changed files with 845 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
.idea/

22
AutoCat.sln Normal file
View File

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

View File

@ -0,0 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String>
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;
&lt;Assembly Path="/Users/selim/.nuget/packages/avalonia/11.0.0/ref/net6.0/Avalonia.Markup.dll" /&gt;
&lt;Assembly Path="/Users/selim/Documents/dev/AutoCatAvalonia/AutoCat/bin/Debug/net7.0/Avalonia.Base.dll" /&gt;
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

BIN
AutoCat/.DS_Store vendored Normal file

Binary file not shown.

14
AutoCat/App.axaml Normal file
View File

@ -0,0 +1,14 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AutoCat"
x:Class="AutoCat.App"
RequestedThemeVariant="Default">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme/>
</Application.Styles>
</Application>

58
AutoCat/App.axaml.cs Normal file
View File

@ -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<IApiService, ApiService>();
services.AddSingleton<IStorageService, StorageService>();
// ViewModels
services.AddTransient<AuthWindowViewModel>();
services.AddTransient<MainWindowViewModel>();
// Windows
services.AddWindowFactory<AuthWindow>();
services.AddWindowFactory<MainWindow>();
}
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<IStorageService>();
desktop.MainWindow = storageService.IsLoggedIn
? AppHost.Services.GetRequiredService<MainWindow>()
: AppHost.Services.GetRequiredService<AuthWindow>();
}
base.OnFrameworkInitializationCompleted();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

34
AutoCat/AutoCat.csproj Normal file
View File

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0" />
<PackageReference Include="MessageBox.Avalonia" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AutoCatCore\AutoCatCore.csproj" />
</ItemGroup>
</Project>

View File

@ -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<bool, object>(e => e ? True : False)
};
return binding.ProvideValue(serviceProvider);
}
}

View File

@ -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<Unit, Unit> LoginCommand { get; } = ReactiveCommand.Create(() => { });
}

22
AutoCat/Program.cs Normal file
View File

@ -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<App>()
.UsePlatformDetect()
.LogToTrace()
.UseReactiveUI();
}

View File

@ -0,0 +1,23 @@
using System;
namespace AutoCat.Utils;
public interface IAbstractFactory<out T>
{
T Create();
}
public class AbstractFactory<T> : IAbstractFactory<T>
{
private readonly Func<T> _factory;
public AbstractFactory(Func<T> factory)
{
_factory = factory;
}
public T Create()
{
return _factory();
}
}

View File

@ -0,0 +1,15 @@
using System;
using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
namespace AutoCat.Utils;
public static class DependencyInjectionExtensions
{
public static void AddWindowFactory<TWindow>(this IServiceCollection services) where TWindow: Window
{
services.AddTransient<TWindow>();
services.AddSingleton<Func<TWindow>>(x => () => x.GetService<TWindow>()!);
services.AddSingleton<IAbstractFactory<TWindow>, AbstractFactory<TWindow>>();
}
}

View File

@ -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");
}
}

27
AutoCat/ViewLocator.cs Normal file
View File

@ -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;
}
}

View File

@ -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<Unit, Unit> 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();
}
}

View File

@ -0,0 +1,6 @@
namespace AutoCat.ViewModels;
public class MainWindowViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
}

View File

@ -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);
}
}

View File

@ -0,0 +1,37 @@
<Window xmlns="https://github.com/avaloniaui"
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:vm="using:AutoCat.ViewModels"
xmlns:utils="using:AutoCat.Utils"
xmlns:mocks="using:AutoCat.Mocks"
xmlns:ext="using:AutoCat.Extensions"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AutoCat.Views.AuthWindow"
x:DataType="vm:AuthWindowViewModel"
Title="AuthWindow"
Width="800" Height="600"
DataContext="{utils:ViewModelProvider vm:AuthWindowViewModel}">
<Design.DataContext>
<mocks:AuthWindowViewModelMock/>
</Design.DataContext>
<StackPanel Orientation="Vertical"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Margin="100, 0"
Spacing="16">
<TextBox Watermark="Email"
Text="{Binding Email}"
IsEnabled="{Binding !LoginCommand.IsExecuting^}"/>
<TextBox Watermark="Password" PasswordChar="*"
Text="{Binding Password}"
IsEnabled="{Binding !LoginCommand.IsExecuting^}"/>
<Button Content="{ext:Ternary Path=LoginCommand.IsExecuting^, True=Logging In..., False=Log In}"
Command="{Binding LoginCommand}"
IsEnabled="{Binding !LoginCommand.IsExecuting^}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"/>
</StackPanel>
</Window>

View File

@ -0,0 +1,20 @@
using AutoCat.ViewModels;
using Avalonia.Controls;
namespace AutoCat.Views;
public partial class AuthWindow : Window
{
public AuthWindow()
{
InitializeComponent();
if (DataContext is ViewModelBase vm)
{
vm.ClosingRequest += (sender, args) =>
{
Close();
};
}
}
}

View File

@ -0,0 +1,22 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:AutoCat.ViewModels"
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"
Width="800"
Height="600"
x:Class="AutoCat.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="AutoCat">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>

View File

@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace AutoCat.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

18
AutoCat/app.manifest Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

BIN
AutoCatCore/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoCatCore.Model
{
class AutoCatDbContext: DbContext
{
public DbSet<User> Users { get; private set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("filename=autocat.db");
}
}
}

View File

@ -0,0 +1,16 @@
namespace AutoCatCore.Model
{
public class Osago
{
public double Date { get; set; }
public string Number { get; set; }
public string Vin { get; set; }
public string PlateNumber { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public string Restrictions { get; set; }
public string Insurant { get; set; }
public string Owner { get; set; }
public string UsageRegion { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoCat.Model.Requests
{
class Credentials
{
public string Email { get; set; }
public string Password { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AutoCatCore.Model.Requests
{
public class PagedResponse<T>
{
public int? Count { get; set; }
public string PageToken { get; set; }
public List<T> Items { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace AutoCatCore.Model.Requests;
internal class Response<T>
{
public bool Success { get; set; }
public T? Data { get; set; }
public string? Error { get; set; }
}

16
AutoCatCore/Model/User.cs Normal file
View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace AutoCatCore.Model
{
public class User
{
[Key]
public string Email { get; set; }
public string? Token { get; set; }
[JsonIgnore]
public string? FirebaseIdToken { get; set; }
[JsonIgnore]
public string? FirebaseRefreshToken { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
namespace AutoCatCore.Model
{
public class Vehicle
{
[Key]
public string Number { get; set; }
public string CurrentNumber { get; set; }
public string Vin1 { get; set; }
public string Vin2 { get; set; }
public string Sts { get; set; }
public string Pts { get; set; }
public VehicleBrand Brand { get; set; }
public VehicleModel Model { get; set; }
public string Color { get; set; }
public int Year { get; set; }
public string Category { get; set; }
public bool? IsRightWheel { get; set; }
public bool? IsJapanese { get; set; }
public double AddedDate { get; set; }
public double UpdatedDate { get; set; }
public string AddedBy { get; set; }
public VehicleEngine Engine { get; set; }
public List<VehiclePhoto> Photos { get; set; }
public List<VehicleEvent> Events { get; set; }
public List<Osago> OsagoContracts { get; set; } = new List<Osago>();
public List<VehicleOwnershipPeriod> OwnershipPeriods { get; set; } = new List<VehicleOwnershipPeriod>();
public List<VehicleAd> Ads { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace AutoCatCore.Model
{
public class VehicleAd
{
[Key]
public int Id { get; set; }
public string Url { get; set; }
public string Price { get; set; }
public double Date { get; set; }
public string Mileage { get; set; }
public string Region { get; set; }
public string City { get; set; }
public string AdDescription { get; set; }
public List<string> Photos { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace AutoCatCore.Model
{
public class VehicleBrand
{
public VehicleName Name { get; set; }
public string Logo { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace AutoCatCore.Model
{
public class VehicleEngine
{
public string Number { get; set; }
public int? Volume { get; set; }
public double? PowerKw { get; set; }
public double? PowerHp { get; set; }
public string FuelType { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace AutoCatCore.Model
{
public class VehicleEvent
{
[Key]
public string Id { get; set; }
public double Date { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Address { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace AutoCatCore.Model
{
public class VehicleModel
{
public VehicleName Name { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace AutoCatCore.Model
{
public class VehicleName
{
public string Original { get; set; }
public string Normalized { get; set; }
}
}

View File

@ -0,0 +1,17 @@
namespace AutoCatCore.Model
{
public class VehicleOwnershipPeriod
{
public string LastOperation { get; set; }
public string OwnerType { get; set; }
public Int64 From { get; set; }
public Int64 To { get; set; }
public string Region { get; set; }
public string RegistrationRegion { get; set; }
public string Locality { get; set; }
public string Code { get; set; }
public string Street { get; set; }
public string Building { get; set; }
public string Inn { get; set; }
}
}

View File

@ -0,0 +1,10 @@
namespace AutoCatCore.Model
{
public class VehiclePhoto
{
public string Brand { get; set; }
public string Model { get; set; }
public double Date { get; set; }
public string Url { get; set; }
}
}

View File

@ -0,0 +1,63 @@
using System.Text;
using System.Text.Json;
using AutoCat.Model.Requests;
using AutoCatCore.Model;
using AutoCatCore.Model.Requests;
namespace AutoCatCore.Services.Api;
public class ApiService : IApiService
{
private readonly HttpClient _httpClient;
public ApiService()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("https://vps.aliencat.pro:8443");
}
private static T GetDataOrThrow<T>(string response)
{
var resp = JsonSerializer.Deserialize<Response<T>>(response, new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
if (resp is null)
throw new Exception("Error deserializing response");
if (resp.Success)
return resp.Data ?? throw new Exception("Empty response");
throw new Exception(resp.Error);
}
private void SetAccessToken(string? token)
{
if (token is null)
return;
_httpClient.DefaultRequestHeaders.Remove("Authorization");
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
}
public async Task<User> Login(string email, string password)
{
var credentials = new Credentials { Email = email, Password = password };
var json = JsonSerializer.Serialize(credentials, new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("user/login", content);
var respStr = await response.Content.ReadAsStringAsync();
var result = GetDataOrThrow<User>(respStr);
SetAccessToken(result.Token);
return result;
}
public async Task<PagedResponse<Vehicle>> GetVehicles()
{
var response = await _httpClient.GetAsync("vehicles");
var respStr = await response.Content.ReadAsStringAsync();
return GetDataOrThrow<PagedResponse<Vehicle>>(respStr);
}
}

View File

@ -0,0 +1,10 @@
using AutoCatCore.Model;
using AutoCatCore.Model.Requests;
namespace AutoCatCore.Services.Api;
public interface IApiService
{
Task<User> Login(string email, string password);
Task<PagedResponse<Vehicle>> GetVehicles();
}

View File

@ -0,0 +1,10 @@
using AutoCatCore.Model;
namespace AutoCatCore.Services.Storage;
public interface IStorageService
{
public bool IsLoggedIn { get; }
public Task SetUser(User user);
}

View File

@ -0,0 +1,33 @@
using AutoCatCore.Model;
using Microsoft.EntityFrameworkCore;
namespace AutoCatCore.Services.Storage;
public class StorageService: IStorageService
{
private readonly AutoCatDbContext _dbContext;
public bool IsLoggedIn
{
get
{
if (_dbContext.Users.Any())
return _dbContext.Users.First().Token != null;
else
return false;
}
}
public async Task SetUser(User user)
{
await _dbContext.Users.ExecuteDeleteAsync();
await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
}
public StorageService()
{
_dbContext = new AutoCatDbContext();
_dbContext.Database.EnsureCreated();
}
}