diff --git a/Fuyu.Backend.Core/Controllers/AccountGamesController.cs b/Fuyu.Backend.Core/Controllers/AccountGamesController.cs new file mode 100644 index 0000000..0bb898e --- /dev/null +++ b/Fuyu.Backend.Core/Controllers/AccountGamesController.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Fuyu.Common.Networking; +using Fuyu.Common.Serialization; +using Fuyu.Backend.Core.DTO.Responses; +using Fuyu.Backend.Core.Services; + +namespace Fuyu.Backend.Core.Controllers +{ + public class AccountGamesController : HttpController + { + public AccountGamesController() : base("/account/games") + { + } + + public override async Task RunAsync(HttpContext context) + { + var sessionId = context.GetSessionId(); + var result = AccountService.GetGames(sessionId); + var response = new AccountGamesResponse() + { + Games = result + }; + + await context.SendJsonAsync(Json.Stringify(response)); + } + } +} diff --git a/Fuyu.Backend.Core/Controllers/AccountLogoutController.cs b/Fuyu.Backend.Core/Controllers/AccountLogoutController.cs new file mode 100644 index 0000000..dd41e15 --- /dev/null +++ b/Fuyu.Backend.Core/Controllers/AccountLogoutController.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Fuyu.Common.Networking; + +namespace Fuyu.Backend.Core.Controllers +{ + public class AccountLogoutController : HttpController + { + public AccountLogoutController() : base("/account/logout") + { + } + + public override async Task RunAsync(HttpContext context) + { + var sessionId = context.GetSessionId(); + CoreOrm.RemoveSession(sessionId); + + await context.SendJsonAsync("{}"); + } + } +} diff --git a/Fuyu.Backend.Core/Controllers/AccountRegisterController.cs b/Fuyu.Backend.Core/Controllers/AccountRegisterController.cs index 0274cee..3641845 100644 --- a/Fuyu.Backend.Core/Controllers/AccountRegisterController.cs +++ b/Fuyu.Backend.Core/Controllers/AccountRegisterController.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; -using Fuyu.Backend.Core.DTO.Requests; -using Fuyu.Backend.Core.DTO.Responses; using Fuyu.Common.Networking; using Fuyu.Common.Serialization; +using Fuyu.Backend.Core.DTO.Requests; +using Fuyu.Backend.Core.DTO.Responses; using Fuyu.Backend.Core.Services; namespace Fuyu.Backend.Core.Controllers diff --git a/Fuyu.Backend.Core/Controllers/AccountRegisterGameController.cs b/Fuyu.Backend.Core/Controllers/AccountRegisterGameController.cs index bb8d898..c3ab80e 100644 --- a/Fuyu.Backend.Core/Controllers/AccountRegisterGameController.cs +++ b/Fuyu.Backend.Core/Controllers/AccountRegisterGameController.cs @@ -1,8 +1,7 @@ using System.Threading.Tasks; -using Fuyu.Backend.Core.DTO.Requests; -using Fuyu.Backend.Core.DTO.Responses; using Fuyu.Common.Networking; using Fuyu.Common.Serialization; +using Fuyu.Backend.Core.DTO.Requests; using Fuyu.Backend.Core.Services; namespace Fuyu.Backend.Core.Controllers @@ -18,12 +17,8 @@ public override async Task RunAsync(HttpContext context) var request = await context.GetJsonAsync(); var sessionId = context.GetSessionId(); var result = AccountService.RegisterGame(sessionId, request.Game, request.Edition); - var response = new AccountRegisterGameResponse() - { - Status = result - }; - await context.SendJsonAsync(Json.Stringify(response)); + await context.SendJsonAsync(Json.Stringify(result)); } } } \ No newline at end of file diff --git a/Fuyu.Backend.Core/DTO/Response/AccountGamesResponse.cs b/Fuyu.Backend.Core/DTO/Response/AccountGamesResponse.cs new file mode 100644 index 0000000..e844c2a --- /dev/null +++ b/Fuyu.Backend.Core/DTO/Response/AccountGamesResponse.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Fuyu.Backend.Core.DTO.Responses +{ + [DataContract] + public class AccountGamesResponse + { + [DataMember] + public Dictionary Games; + } +} diff --git a/Fuyu.Backend.Core/DTO/Response/AccountRegisterGameResponse.cs b/Fuyu.Backend.Core/DTO/Response/AccountRegisterGameResponse.cs index 2f2e26b..0cc5efe 100644 --- a/Fuyu.Backend.Core/DTO/Response/AccountRegisterGameResponse.cs +++ b/Fuyu.Backend.Core/DTO/Response/AccountRegisterGameResponse.cs @@ -8,5 +8,8 @@ public class AccountRegisterGameResponse { [DataMember] public ERegisterStatus Status; + + [DataMember] + public int AccountId; } } \ No newline at end of file diff --git a/Fuyu.Backend.Core/Servers/CoreServer.cs b/Fuyu.Backend.Core/Servers/CoreServer.cs index c38d419..2d94749 100644 --- a/Fuyu.Backend.Core/Servers/CoreServer.cs +++ b/Fuyu.Backend.Core/Servers/CoreServer.cs @@ -12,8 +12,10 @@ public CoreServer() : base("core", "http://localhost:8000/") public void RegisterServices() { AddHttpController(); + AddHttpController(); AddHttpController(); AddHttpController(); + AddHttpController(); } } } \ No newline at end of file diff --git a/Fuyu.Backend.Core/Services/AccountService.cs b/Fuyu.Backend.Core/Services/AccountService.cs index 151cb09..876d1ed 100644 --- a/Fuyu.Backend.Core/Services/AccountService.cs +++ b/Fuyu.Backend.Core/Services/AccountService.cs @@ -189,14 +189,18 @@ public static ERegisterStatus RegisterAccount(string username, string password) return ERegisterStatus.Success; } - public static ERegisterStatus RegisterGame(string sessionId, string game, string edition) + public static AccountRegisterGameResponse RegisterGame(string sessionId, string game, string edition) { var account = CoreOrm.GetAccount(sessionId); // find existing game if (account.Games.ContainsKey(game) && account.Games[game].HasValue) { - return ERegisterStatus.AlreadyExists; + return new AccountRegisterGameResponse() + { + Status = ERegisterStatus.AlreadyExists, + AccountId = -1 + }; } // register game @@ -207,7 +211,17 @@ public static ERegisterStatus RegisterGame(string sessionId, string game, string CoreOrm.SetOrAddAccount(account); WriteToDisk(account); - return ERegisterStatus.Success; + return new AccountRegisterGameResponse() + { + Status = ERegisterStatus.Success, + AccountId = accountId + }; + } + + public static Dictionary GetGames(string sessionId) + { + var account = CoreOrm.GetAccount(sessionId); + return account.Games; } public static void WriteToDisk(Account account) diff --git a/Fuyu.Launcher.Core/Services/RequestService.cs b/Fuyu.Launcher.Core/Services/RequestService.cs index b83dfdb..f60a811 100644 --- a/Fuyu.Launcher.Core/Services/RequestService.cs +++ b/Fuyu.Launcher.Core/Services/RequestService.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Collections.Generic; using Fuyu.Common.Collections; using Fuyu.Common.Hashing; using Fuyu.Common.Networking; @@ -24,6 +25,16 @@ static RequestService() _httpClients.Add("arena", new EftHttpClient(SettingsService.ArenaAddress, string.Empty)); } + private static void HttpPut(string id, string path, T1 request) + { + var httpc = _httpClients.Get(id); + + var requestJson = Json.Stringify(request); + var requestBytes = Encoding.UTF8.GetBytes(requestJson); + + httpc.Put(path, requestBytes); + } + private static T2 HttpPost(string id, string path, T1 request) { var httpc = _httpClients.Get(id); @@ -38,6 +49,13 @@ private static T2 HttpPost(string id, string path, T1 request) return responseValue; } + public static void ResetSessions() + { + _httpClients.Set("fuyu", new EftHttpClient(SettingsService.FuyuAddress, string.Empty)); + _httpClients.Set("eft", new EftHttpClient(SettingsService.EftAddress, string.Empty)); + _httpClients.Set("arena", new EftHttpClient(SettingsService.ArenaAddress, string.Empty)); + } + public static void CreateSession(string id, string address, string sessionId) { _httpClients.Set(id, new EftHttpClient(address, sessionId)); @@ -58,7 +76,7 @@ public static ERegisterStatus RegisterAccount(string username, string password) return response.Status; } - public static string LoginAccount(string username, string password) + public static AccountLoginResponse LoginAccount(string username, string password) { var hashedPassword = Sha256.Generate(password); var request = new AccountLoginRequest() @@ -71,10 +89,30 @@ public static string LoginAccount(string username, string password) "/account/login", request); - return response.SessionId; + return response; } - public static ERegisterStatus RegisterGame(string game, string edition) + public static void LogoutAccount() + { + HttpPut( + "fuyu", + "/account/logout", + null); + + ResetSessions(); + } + + public static Dictionary GetGames() + { + var response = HttpPost( + "fuyu", + "/account/games", + null); + + return response.Games; + } + + public static AccountRegisterGameResponse RegisterGame(string game, string edition) { var request = new AccountRegisterGameRequest() { @@ -86,7 +124,7 @@ public static ERegisterStatus RegisterGame(string game, string edition) "/account/register/game", request); - return response.Status; + return response; } public static string LoginGame(string game, int accountId) diff --git a/Fuyu.Launcher/Components/AboutDialog.razor b/Fuyu.Launcher/Components/AboutDialog.razor new file mode 100644 index 0000000..5f606a5 --- /dev/null +++ b/Fuyu.Launcher/Components/AboutDialog.razor @@ -0,0 +1,40 @@ +@using System.Diagnostics; + + + + + + + + 冬 Fuyu + 1.0.0 + + + + + + + + + + + + Copyright © 2024 seionmoya + + + + +@code { + [CascadingParameter] + private MudDialogInstance MudDialog { get; set; } + + private void OpenUrlOnDefaultBrowser(string link) + { + var info = new ProcessStartInfo(link) + { + UseShellExecute = true + }; + + Process.Start(info); + } +} diff --git a/Fuyu.Launcher/Components/AddGameDialog.razor b/Fuyu.Launcher/Components/AddGameDialog.razor new file mode 100644 index 0000000..1f63aa6 --- /dev/null +++ b/Fuyu.Launcher/Components/AddGameDialog.razor @@ -0,0 +1,129 @@ +@using Fuyu.Backend.Core.DTO.Accounts + +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IState GamesState +@inject IDispatcher Dispatcher +@inject ISnackbar Snackbar + +@if (!GamesState.Value.Games.Values.Contains(null)) +{ + + + No more games left to add. + + + Close + + +} +else +{ + + + + + @foreach (KeyValuePair game in games) + { + if (GamesState.Value.Games[game.Key] == null) + { + @game.Value + } + } + + + @if (!string.IsNullOrWhiteSpace(_game)) + { + @foreach (KeyValuePair gameEdition in gameEditions[_game]) + { + @gameEdition.Value + } + } + + + + + Cancel + Add + + +} + + +@code { + [CascadingParameter] + private MudDialogInstance MudDialog { get; set; } + + private string _game = string.Empty; + private string _username = string.Empty; + private string _edition = string.Empty; + + // TODO: Remove both of these Dictionaries, get this from backend or a future localization service + private readonly Dictionary games = new() + { + { "eft", "Escape from Tarkov" }, + { "arena", "Escape from Tarkov: Arena" } + }; + + private readonly Dictionary> gameEditions = new() + { + { + "eft", + new() + { + { "standard", "Standard" }, + { "left behind", "Left Behind" }, + { "prepare to escape", "Prepare to Escape" }, + { "edge of darkness", "Edge of Darkness" }, + { "unheard", "Unheard" } + } + }, + { + "arena", + new() + { + { "standard", "Standard" }, + { "ryhzy", "Ryhzy" } + } + } + }; + + private void Game_Changed() + { + _edition = string.Empty; + } + + private bool Add_Disabled() + { + return string.IsNullOrWhiteSpace(_game) || string.IsNullOrWhiteSpace(_edition); + } + + private void Cancel_Clicked() + { + MudDialog.Cancel(); + } + + private void Add_Clicked() + { + if (Add_Disabled()) + { + return; + } + + var response = RequestService.RegisterGame(_game, _edition); + + switch (response.Status) + { + case ERegisterStatus.Success: + Dispatcher.Dispatch(new AddGameAction(_game, response.AccountId)); + Snackbar.Add("Successfully added game to account!", Severity.Success); + MudDialog.Close(DialogResult.Ok(true)); + break; + + case ERegisterStatus.AlreadyExists: + Snackbar.Add("Game already exists in account!", Severity.Warning); + break; + + } + } +} diff --git a/Fuyu.Launcher/Components/Background.razor b/Fuyu.Launcher/Components/Background.razor new file mode 100644 index 0000000..a5b7194 --- /dev/null +++ b/Fuyu.Launcher/Components/Background.razor @@ -0,0 +1,12 @@ +
+ @if (Glass) + { +
+ } +
+
+ +@code { + [Parameter] + public bool Glass { get; set; } = false; +} diff --git a/Fuyu.Launcher/Components/Background.razor.css b/Fuyu.Launcher/Components/Background.razor.css new file mode 100644 index 0000000..bc47ba6 --- /dev/null +++ b/Fuyu.Launcher/Components/Background.razor.css @@ -0,0 +1,26 @@ +.bg { + top: 0; + left: 0; + position: fixed; + overflow: hidden; + width: 100vw; + height: 100vh; +} + +.bg-img { + background-image: url("../img/bg.png"); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + background-attachment: fixed; + z-index: -2; +} + +.bg-glass { + left: 50%; + background: rgba( 255, 255, 255, 0 ); + box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 ); + backdrop-filter: blur( 3.5px ); + -webkit-backdrop-filter: blur( 3.5px ); + z-index: -1; +} diff --git a/Fuyu.Launcher/Components/Game.razor b/Fuyu.Launcher/Components/Game.razor new file mode 100644 index 0000000..5deec38 --- /dev/null +++ b/Fuyu.Launcher/Components/Game.razor @@ -0,0 +1,61 @@ +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IState SessionState +@inject IState GamesState +@inject IState ActiveGameState + +
+ + + @games[ActiveGameState.Value.GameId] + + + + Placeholder + + +
+ + + +
+
+ +@code { + // TODO: This Dictionary should be changed for a localization service in the future + private readonly Dictionary games = new() + { + { "eft", "Escape from Tarkov" }, + { "arena", "Escape from Tarkov: Arena" } + }; + + private void StartGame_Clicked() + { + if (!GamesState.Value.Games.ContainsKey(ActiveGameState.Value.GameId)) + { + return; + } + + var accountId = (int)GamesState.Value.Games[ActiveGameState.Value.GameId]; + + var gameSessionId = RequestService.LoginGame(ActiveGameState.Value.GameId, accountId); + + switch (ActiveGameState.Value.GameId) + { + case "eft": + using (var process = ProcessService.StartEft(SettingsService.EftDirectory, gameSessionId, SettingsService.EftAddress)) + { + process.Start(); + } + break; + + case "arena": + using (var process = ProcessService.StartArena(SettingsService.ArenaDirectory, gameSessionId, SettingsService.ArenaAddress)) + { + process.Start(); + } + break; + + } + } +} diff --git a/Fuyu.Launcher/Fuyu.Launcher.csproj b/Fuyu.Launcher/Fuyu.Launcher.csproj index e0f697b..fb75dab 100644 --- a/Fuyu.Launcher/Fuyu.Launcher.csproj +++ b/Fuyu.Launcher/Fuyu.Launcher.csproj @@ -11,16 +11,24 @@ true true true + Fuyu.Launcher Resources\icon.ico - - - + + + + + + + + + + @@ -29,10 +37,10 @@ - - Always - - + + Always + + diff --git a/Fuyu.Launcher/Layout/AppBar.razor b/Fuyu.Launcher/Layout/AppBar.razor new file mode 100644 index 0000000..34813ef --- /dev/null +++ b/Fuyu.Launcher/Layout/AppBar.razor @@ -0,0 +1,72 @@ +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IState SessionState +@inject IDispatcher Dispatcher +@inject NavigationManager Navigation + + + + + @if (SessionState.Value.IsLoggedIn) + { + + + @Char.ToUpper(SessionState.Value.UserName[0]) + + + + + @SessionState.Value.UserName + + Logout + + + + + } + else + { + Login + Register + } + + + + +@code { + private string _prevHref = "/"; + + private readonly string[] toggleableRoutes = [ "/settings" ]; + + private void Settings_Clicked() + { + ToggleNavigateTo("/settings"); + } + + private void ToggleNavigateTo(string uri) + { + var currHref = "/" + Navigation.ToBaseRelativePath(Navigation.Uri); + + if (currHref == uri) + { + Navigation.NavigateTo(_prevHref); + return; + } + + if (!toggleableRoutes.Contains(currHref)) + { + _prevHref = currHref; + } + + Navigation.NavigateTo(uri); + } + + private void Logout_Clicked() + { + RequestService.LogoutAccount(); + + Dispatcher.Dispatch(new LogoutSessionAction()); + + Navigation.NavigateTo("/login"); + } +} diff --git a/Fuyu.Launcher/Layout/MainLayout.razor b/Fuyu.Launcher/Layout/MainLayout.razor index b2332ae..9ca4730 100644 --- a/Fuyu.Launcher/Layout/MainLayout.razor +++ b/Fuyu.Launcher/Layout/MainLayout.razor @@ -1,21 +1,17 @@ @inherits LayoutComponentBase - - - - + + + + - + - - - Login - Settings - + + + - - - @Body - - - + + @Body + + diff --git a/Fuyu.Launcher/Layout/SideBar.razor b/Fuyu.Launcher/Layout/SideBar.razor new file mode 100644 index 0000000..0d4b970 --- /dev/null +++ b/Fuyu.Launcher/Layout/SideBar.razor @@ -0,0 +1,64 @@ +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IState SessionState +@inject IState GamesState +@inject IState ActiveGameState +@inject IDispatcher Dispatcher +@inject IDialogService DialogService + +@if (SessionState.Value.IsLoggedIn) +{ + + + @foreach (KeyValuePair game in GamesState.Value.Games) + { + if (game.Value != null) + { + + + + } + } + @if (GamesState.Value.Games.ContainsValue(null)) + { + + + + } + + + + + + + + +} + +@code { + // TODO: This Dictionary should be changed for a localization service in the future + private readonly Dictionary games = new() + { + { "eft", "EFT" }, + { "arena", "EFT: Arena" } + }; + + private void Game_Clicked(string gameId) + { + Dispatcher.Dispatch(new SetActiveGameAction(gameId)); + } + + private Task OpenAboutDialogAsync() + { + var options = new DialogOptions { CloseOnEscapeKey = true, CloseButton = true, BackdropClick = true }; + + return DialogService.ShowAsync("", options); + } + + private Task OpenAddGameDialogAsync() + { + var options = new DialogOptions { CloseOnEscapeKey = true, CloseButton = true, FullWidth = true }; + + return DialogService.ShowAsync("Add new Game", options); + } +} diff --git a/Fuyu.Launcher/MainWindow.xaml b/Fuyu.Launcher/MainWindow.xaml index 410a070..be1dcb8 100644 --- a/Fuyu.Launcher/MainWindow.xaml +++ b/Fuyu.Launcher/MainWindow.xaml @@ -8,7 +8,9 @@ mc:Ignorable="d" Icon="Resources\Icon.ico" WindowStartupLocation="CenterScreen" - Title="Fuyu.Launcher" Height="450" Width="800"> + Title="Fuyu Launcher" + MinHeight="600" MinWidth="1070" + Height="600" Width="1070"> diff --git a/Fuyu.Launcher/MainWindow.xaml.cs b/Fuyu.Launcher/MainWindow.xaml.cs index d6ec258..6beb8fc 100644 --- a/Fuyu.Launcher/MainWindow.xaml.cs +++ b/Fuyu.Launcher/MainWindow.xaml.cs @@ -1,6 +1,9 @@ using System.Windows; +using Dark.Net; using Microsoft.Extensions.DependencyInjection; -using Microsoft.FluentUI.AspNetCore.Components; +using MudBlazor.Services; +using Fluxor; +using MudBlazor; namespace Fuyu.Launcher { @@ -10,11 +13,20 @@ public MainWindow() { InitializeComponent(); + DarkNet.Instance.SetWindowThemeWpf(this, Theme.Dark); + var services = new ServiceCollection(); - services.AddWpfBlazorWebView(); - services.AddFluentUIComponents(); - services.AddBlazorWebViewDeveloperTools(); - Resources.Add("services", services.BuildServiceProvider()); + services.AddWpfBlazorWebView(); + services.AddMudServices(config => + { + config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight; + }); + + var currentAssembly = typeof(MainWindow).Assembly; + services.AddFluxor(options => options.ScanAssemblies(currentAssembly)); + + services.AddBlazorWebViewDeveloperTools(); + Resources.Add("services", services.BuildServiceProvider()); } } } diff --git a/Fuyu.Launcher/Pages/Account.razor b/Fuyu.Launcher/Pages/Account.razor deleted file mode 100644 index ec46845..0000000 --- a/Fuyu.Launcher/Pages/Account.razor +++ /dev/null @@ -1,42 +0,0 @@ -@using Fuyu.Backend.Core.DTO.Accounts -@using Fuyu.Launcher.Core.Services - -@page "/account" - - - - Start EFT - Start Arena - - - -@code -{ - [SupplyParameterFromQuery(Name = "sessionid")] - private string sessionId { get; set; } - - private void StartEft_Clicked() - { - // NOTE: hack until wipe screen is created - // -- seionmoya, 2024/09/03 - RequestService.RegisterGame("eft", "unheard"); - var accountId = 0; - var gameSessionId = RequestService.LoginGame("eft", accountId); - using (var process = ProcessService.StartEft(SettingsService.EftDirectory, gameSessionId, SettingsService.EftAddress)) - { - process.Start(); - } - } - - private void StartArena_Clicked() - { - // NOTE: hack until wipe screen is created - // -- seionmoya, 2024/09/03 - RequestService.RegisterGame("arena", "ryzhy"); - - using (var process = ProcessService.StartArena(SettingsService.ArenaDirectory, sessionId, SettingsService.ArenaAddress)) - { - process.Start(); - } - } -} diff --git a/Fuyu.Launcher/Pages/Login.razor b/Fuyu.Launcher/Pages/Login.razor index 34aa591..33b47d4 100644 --- a/Fuyu.Launcher/Pages/Login.razor +++ b/Fuyu.Launcher/Pages/Login.razor @@ -1,45 +1,80 @@ -@using Fuyu.Common.Hashing; -@using Fuyu.Launcher.Core.Services; +@using Fuyu.Backend.Core.DTO.Accounts +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IDispatcher Dispatcher +@inject ISnackbar Snackbar @inject NavigationManager Navigation -@page "/" @page "/login" - + + + + + + + + + Login + Enter your login credentials. + - - - Username: - - Password: - -
-
- Login - Register -
+ + + + +
+ Login +
+
+
+
+
+
+
-
+@code { + private bool _success; + private string[] _errors = []; + private MudForm _form; -@code -{ - string username = string.Empty; - string password = string.Empty; + private string _username = string.Empty; + private string _password = string.Empty; - private void Login_Clicked(EventArgs e) + public void Login_Clicked() { - var sessionId = RequestService.LoginAccount(username, password); - - if (sessionId == string.Empty) + _form.Validate(); + if (!_form.IsValid) { - // TODO: - // * show error: "username or password is wrong" - // -- seionmoya, 2024/09/02 return; } - RequestService.CreateSession("fuyu", SettingsService.FuyuAddress, sessionId); - Navigation.NavigateTo($"/account?sessionid={sessionId}"); + var response = RequestService.LoginAccount(_username, _password); + + switch (response.Status) + { + case ELoginStatus.Success: + RequestService.CreateSession("fuyu", SettingsService.FuyuAddress, response.SessionId); + + Dispatcher.Dispatch(new LoginSessionAction(_username)); + Dispatcher.Dispatch(new GetGamesAction()); + + Navigation.NavigateTo("/"); + break; + + case ELoginStatus.AccountBanned: + Snackbar.Add("Account is banned!", Severity.Warning); + break; + + case ELoginStatus.AccountNotFound: + Snackbar.Add("Account not found!", Severity.Warning); + break; + + case ELoginStatus.SessionAlreadyExists: + Snackbar.Add("Someone is already using this account.", Severity.Warning); + break; + + } } } diff --git a/Fuyu.Launcher/Pages/MainPage.razor b/Fuyu.Launcher/Pages/MainPage.razor new file mode 100644 index 0000000..5621c02 --- /dev/null +++ b/Fuyu.Launcher/Pages/MainPage.razor @@ -0,0 +1,44 @@ +@inherits Fluxor.Blazor.Web.Components.FluxorComponent + +@inject IState SessionState +@inject IState GamesState +@inject IState ActiveGameState +@inject IDialogService DialogService +@inject NavigationManager Navigation + +@page "/" + +@if (SessionState.Value.IsLoggedIn) +{ + + @if (string.IsNullOrWhiteSpace(ActiveGameState.Value.GameId)) + { + Welcome, @SessionState.Value.UserName + If you're seeing this page its because you haven't added a game to your account, so please add one over on the left side bar. + } + else + { + + } + +} + +@code { + protected override void OnInitialized() + { + base.OnInitialized(); + + if (!SessionState.Value.IsLoggedIn) + { + Navigation.NavigateTo("/login"); + return; + } + } + + private Task OpenAddGameDialogAsync() + { + var options = new DialogOptions { CloseOnEscapeKey = true, CloseButton = true, FullWidth = true }; + + return DialogService.ShowAsync("Add new Game", options); + } +} diff --git a/Fuyu.Launcher/Pages/Register.razor b/Fuyu.Launcher/Pages/Register.razor index 4ae6d4d..2a4ca92 100644 --- a/Fuyu.Launcher/Pages/Register.razor +++ b/Fuyu.Launcher/Pages/Register.razor @@ -1,72 +1,105 @@ -@using Fuyu.Backend.Core.DTO.Accounts -@using Fuyu.Launcher.Core.Services; +@using Fuyu.Backend.Core.DTO.Accounts +@inject ISnackbar Snackbar @inject NavigationManager Navigation @page "/register" - - - - - Username: - - Password: - -
-
- Register - Login -
- -
- -@code -{ - string username = string.Empty; - string password = string.Empty; - - // TODO: - // * notify user of successful registration - // * show error on issue - // -- seionmoya, 2024/09/08 - private void Register_Clicked(EventArgs e) + + + + + + + + + + Register + Choose your username, password and game edition. + + + + + + + +
+ Register +
+
+
+
+
+
+
+ +@code { + private bool _success; + private string[] _errors = []; + private MudForm _form; + + private string _username = string.Empty; + private string _password = string.Empty; + + private string PasswordMatch(string arg) { - var registerStatus = RequestService.RegisterAccount(username, password); + return _password != arg ? "Passwords don't match" : null; + } + + public void Register_Clicked() + { + _form.Validate(); + if (!_form.IsValid) + { + return; + } + + var status = RequestService.RegisterAccount(_username, _password); - switch (registerStatus) + switch (status) { case ERegisterStatus.Success: + Snackbar.Add("Account registered successfully. You can now login!", Severity.Success); + Navigation.NavigateTo("/login"); break; case ERegisterStatus.AlreadyExists: - return; + Snackbar.Add("Account already exists!", Severity.Warning); + break; - case ERegisterStatus.UsernameEmpty: - return; + case ERegisterStatus.PasswordInvalid: + Snackbar.Add("Password doesn't meet the requirements!", Severity.Warning); + break; - case ERegisterStatus.UsernameTooShort: - return; + case ERegisterStatus.PasswordTooLong: + Snackbar.Add("Password too long!", Severity.Warning); + break; + case ERegisterStatus.PasswordTooShort: + Snackbar.Add("Password too short!", Severity.Warning); + break; - case ERegisterStatus.UsernameTooLong: - return; + case ERegisterStatus.PasswordEmpty: + Snackbar.Add("Didn't receive a password!", Severity.Warning); + break; case ERegisterStatus.UsernameInvalid: - return; + Snackbar.Add("Username contains invalid characters!", Severity.Warning); + break; - case ERegisterStatus.PasswordEmpty: - return; + case ERegisterStatus.UsernameTooLong: + Snackbar.Add("Username too long!", Severity.Warning); + break; - case ERegisterStatus.PasswordTooShort: - return; + case ERegisterStatus.UsernameTooShort: + Snackbar.Add("Username too short!", Severity.Warning); + break; - case ERegisterStatus.PasswordTooLong: - return; + case ERegisterStatus.UsernameEmpty: + Snackbar.Add("Didn't receive a username!", Severity.Warning); + break; - case ERegisterStatus.PasswordInvalid: - return; } - - Navigation.NavigateTo("/login"); } } diff --git a/Fuyu.Launcher/Pages/Settings.razor b/Fuyu.Launcher/Pages/Settings.razor index a5c5097..51fb1b4 100644 --- a/Fuyu.Launcher/Pages/Settings.razor +++ b/Fuyu.Launcher/Pages/Settings.razor @@ -1,63 +1,29 @@ -@using Fuyu.Launcher.Core.Services; +@inherits Fluxor.Blazor.Web.Components.FluxorComponent -@page "/settings" - - - - - - Server address: - - - - - - Server address: - - Client location: - - - - - - Server address: - - Client location: - - +@inject IState SessionState - - -@code -{ - string fuyuAddress = SettingsService.FuyuAddress; - string eftAddress = SettingsService.EftAddress; - string arenaAddress = SettingsService.ArenaAddress; - string eftDirectory = SettingsService.EftDirectory; - string arenaDirectory = SettingsService.EftDirectory; - - private void FuyuAddress_TextChanged(ChangeEventArgs e) - { - SettingsService.FuyuAddress = (string)e.Value; - } - - private void EftAddress_TextChanged(ChangeEventArgs e) - { - SettingsService.EftAddress = (string)e.Value; - } - - private void ArenaAddress_TextChanged(ChangeEventArgs e) - { - SettingsService.ArenaAddress = (string)e.Value; - } +@page "/settings" - private void EftDirectory_TextChanged(ChangeEventArgs e) - { - SettingsService.EftDirectory = (string)e.Value; - } + + + + + + @if (SessionState.Value.IsLoggedIn) + { + // TODO: check for existing games associated to fuyu account and display only for available + + + + + + + + + } + + + +@code { - private void ArenaDirectory_TextChanged(ChangeEventArgs e) - { - SettingsService.ArenaDirectory = (string)e.Value; - } } diff --git a/Fuyu.Launcher/Resources/Resources.resx b/Fuyu.Launcher/Resources/Resources.resx index 4fdb1b6..3f8254d 100644 --- a/Fuyu.Launcher/Resources/Resources.resx +++ b/Fuyu.Launcher/Resources/Resources.resx @@ -1,101 +1,101 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/Fuyu.Launcher/Routes.razor b/Fuyu.Launcher/Routes.razor index a056310..5486db7 100644 --- a/Fuyu.Launcher/Routes.razor +++ b/Fuyu.Launcher/Routes.razor @@ -1,11 +1,8 @@ -@namespace Fuyu.Launcher + -@using Microsoft.AspNetCore.Components.Routing -@using Fuyu.Launcher.Layout - - + - + diff --git a/Fuyu.Launcher/Store/ActiveGameUseCase/ActiveGamesState.cs b/Fuyu.Launcher/Store/ActiveGameUseCase/ActiveGamesState.cs new file mode 100644 index 0000000..f18f3fc --- /dev/null +++ b/Fuyu.Launcher/Store/ActiveGameUseCase/ActiveGamesState.cs @@ -0,0 +1,19 @@ +using Fluxor; + +namespace Fuyu.Launcher.Store.ActiveGameUseCase +{ + [FeatureState] + public class ActiveGameState + { + public string GameId { get; } = string.Empty; + + public ActiveGameState() + { + } + + public ActiveGameState(string gameId) + { + GameId = gameId; + } + } +} diff --git a/Fuyu.Launcher/Store/ActiveGameUseCase/Reducers.cs b/Fuyu.Launcher/Store/ActiveGameUseCase/Reducers.cs new file mode 100644 index 0000000..8a595db --- /dev/null +++ b/Fuyu.Launcher/Store/ActiveGameUseCase/Reducers.cs @@ -0,0 +1,13 @@ +using Fluxor; + +namespace Fuyu.Launcher.Store.ActiveGameUseCase +{ + public static class Reducers + { + [ReducerMethod] + public static ActiveGameState ReduceGetGamesAction(ActiveGameState state, SetActiveGameAction action) + { + return new ActiveGameState(action.GameId); + } + } +} diff --git a/Fuyu.Launcher/Store/AddGameAction.cs b/Fuyu.Launcher/Store/AddGameAction.cs new file mode 100644 index 0000000..dc8af47 --- /dev/null +++ b/Fuyu.Launcher/Store/AddGameAction.cs @@ -0,0 +1,14 @@ +namespace Fuyu.Launcher.Store +{ + public class AddGameAction + { + public readonly string GameId; + public readonly int AccountId; + + public AddGameAction(string gameId, int accountId) + { + GameId = gameId; + AccountId = accountId; + } + } +} diff --git a/Fuyu.Launcher/Store/GamesUseCase/Effects.cs b/Fuyu.Launcher/Store/GamesUseCase/Effects.cs new file mode 100644 index 0000000..ef319da --- /dev/null +++ b/Fuyu.Launcher/Store/GamesUseCase/Effects.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Fuyu.Launcher.Core.Services; +using Fluxor; + +namespace Fuyu.Launcher.Store.GamesUseCase +{ + public class Effects + { + [EffectMethod] + public async Task HandleGetGamesAction(GetGamesAction action, IDispatcher dispatcher) + { + var games = RequestService.GetGames(); + dispatcher.Dispatch(new GetGamesResultAction(games)); + } + } +} diff --git a/Fuyu.Launcher/Store/GamesUseCase/GamesState.cs b/Fuyu.Launcher/Store/GamesUseCase/GamesState.cs new file mode 100644 index 0000000..edcddb1 --- /dev/null +++ b/Fuyu.Launcher/Store/GamesUseCase/GamesState.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Fluxor; + +namespace Fuyu.Launcher.Store.GamesUseCase +{ + [FeatureState] + public class GamesState + { + public bool IsLoading { get; } = true; + public Dictionary Games { get; } = new(); + + public GamesState() + { + } + + public GamesState(bool isLoading, Dictionary games) + { + IsLoading = isLoading; + Games = games; + } + } +} diff --git a/Fuyu.Launcher/Store/GamesUseCase/Reducers.cs b/Fuyu.Launcher/Store/GamesUseCase/Reducers.cs new file mode 100644 index 0000000..8da6a55 --- /dev/null +++ b/Fuyu.Launcher/Store/GamesUseCase/Reducers.cs @@ -0,0 +1,27 @@ +using Fluxor; + +namespace Fuyu.Launcher.Store.GamesUseCase +{ + public static class Reducers + { + [ReducerMethod] + public static GamesState ReduceGetGamesAction(GamesState state, GetGamesAction action) + { + return new GamesState(true, new()); + } + + [ReducerMethod] + public static GamesState ReduceGetGamesResultAction(GamesState state, GetGamesResultAction action) + { + return new GamesState(false, action.Games); + } + + [ReducerMethod] + public static GamesState ReduceAddGameAction(GamesState state, AddGameAction action) + { + state.Games.Add(action.GameId, action.AccountId); + + return new GamesState(false, state.Games); + } + } +} diff --git a/Fuyu.Launcher/Store/GetGamesAction.cs b/Fuyu.Launcher/Store/GetGamesAction.cs new file mode 100644 index 0000000..6c6eb17 --- /dev/null +++ b/Fuyu.Launcher/Store/GetGamesAction.cs @@ -0,0 +1,6 @@ +namespace Fuyu.Launcher.Store +{ + public class GetGamesAction + { + } +} diff --git a/Fuyu.Launcher/Store/GetGamesResultAction.cs b/Fuyu.Launcher/Store/GetGamesResultAction.cs new file mode 100644 index 0000000..1216f72 --- /dev/null +++ b/Fuyu.Launcher/Store/GetGamesResultAction.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Fuyu.Launcher.Store +{ + public class GetGamesResultAction + { + public Dictionary Games { get; } + + public GetGamesResultAction(Dictionary games) + { + Games = games; + } + } +} diff --git a/Fuyu.Launcher/Store/LoginSessionAction.cs b/Fuyu.Launcher/Store/LoginSessionAction.cs new file mode 100644 index 0000000..0b3b1f1 --- /dev/null +++ b/Fuyu.Launcher/Store/LoginSessionAction.cs @@ -0,0 +1,14 @@ +namespace Fuyu.Launcher.Store +{ + public class LoginSessionAction + { + public readonly string UserName; + + public readonly bool IsLoggedIn = true; + + public LoginSessionAction(string userName) + { + UserName = userName; + } + } +} diff --git a/Fuyu.Launcher/Store/LogoutSessionAction.cs b/Fuyu.Launcher/Store/LogoutSessionAction.cs new file mode 100644 index 0000000..2bfc407 --- /dev/null +++ b/Fuyu.Launcher/Store/LogoutSessionAction.cs @@ -0,0 +1,9 @@ +namespace Fuyu.Launcher.Store +{ + public class LogoutSessionAction + { + public readonly string UserName = string.Empty; + + public readonly bool IsLoggedIn = false; + } +} diff --git a/Fuyu.Launcher/Store/SessionUseCase/Reducers.cs b/Fuyu.Launcher/Store/SessionUseCase/Reducers.cs new file mode 100644 index 0000000..8096594 --- /dev/null +++ b/Fuyu.Launcher/Store/SessionUseCase/Reducers.cs @@ -0,0 +1,19 @@ +using Fluxor; + +namespace Fuyu.Launcher.Store.SessionUseCase +{ + public static class Reducers + { + [ReducerMethod] + public static SessionState ReduceLoginSessionAction(SessionState state, LoginSessionAction action) + { + return new SessionState(action.UserName, action.IsLoggedIn); + } + + [ReducerMethod] + public static SessionState ReduceLogoutSessionAction(SessionState state, LogoutSessionAction action) + { + return new SessionState(action.UserName, action.IsLoggedIn); + } + } +} diff --git a/Fuyu.Launcher/Store/SessionUseCase/SessionState.cs b/Fuyu.Launcher/Store/SessionUseCase/SessionState.cs new file mode 100644 index 0000000..63b1907 --- /dev/null +++ b/Fuyu.Launcher/Store/SessionUseCase/SessionState.cs @@ -0,0 +1,22 @@ +using Fluxor; + +namespace Fuyu.Launcher.Store.SessionUseCase +{ + [FeatureState] + public class SessionState + { + public string UserName { get; } + + public bool IsLoggedIn { get; } + + public SessionState() + { + } + + public SessionState(string username, bool isLoggedIn) + { + UserName = username; + IsLoggedIn = isLoggedIn; + } + } +} diff --git a/Fuyu.Launcher/Store/SetActiveGameAction.cs b/Fuyu.Launcher/Store/SetActiveGameAction.cs new file mode 100644 index 0000000..44d2a4f --- /dev/null +++ b/Fuyu.Launcher/Store/SetActiveGameAction.cs @@ -0,0 +1,12 @@ +namespace Fuyu.Launcher.Store +{ + public class SetActiveGameAction + { + public readonly string GameId; + + public SetActiveGameAction(string gameId) + { + GameId = gameId; + } + } +} diff --git a/Fuyu.Launcher/_Imports.razor b/Fuyu.Launcher/_Imports.razor index b386ec4..97f4128 100644 --- a/Fuyu.Launcher/_Imports.razor +++ b/Fuyu.Launcher/_Imports.razor @@ -1,3 +1,17 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web -@using Microsoft.FluentUI.AspNetCore.Components +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using MudBlazor +@using MudBlazor.Services +@using Fuyu.Launcher.Components @using Fuyu.Launcher.Core.Services +@using Fluxor +@using Fuyu.Launcher.Store +@using Fuyu.Launcher.Store.SessionUseCase +@using Fuyu.Launcher.Store.GamesUseCase +@using Fuyu.Launcher.Store.ActiveGameUseCase diff --git a/Fuyu.Launcher/wwwroot/css/app.css b/Fuyu.Launcher/wwwroot/css/app.css index 5e2dea8..c0077ab 100644 --- a/Fuyu.Launcher/wwwroot/css/app.css +++ b/Fuyu.Launcher/wwwroot/css/app.css @@ -1,7 +1,4 @@ -.height-100vh { - height: 100vh; -} - -.nav-bg { - background-image: url("../img/nav-bg.png"); -} +* { + user-select: none; + -webkit-user-drag: none; +} \ No newline at end of file diff --git a/Fuyu.Launcher/wwwroot/img/bg.png b/Fuyu.Launcher/wwwroot/img/bg.png new file mode 100644 index 0000000..1392ac8 Binary files /dev/null and b/Fuyu.Launcher/wwwroot/img/bg.png differ diff --git a/Fuyu.Launcher/wwwroot/img/icon.png b/Fuyu.Launcher/wwwroot/img/icon.png new file mode 100644 index 0000000..155ba6a Binary files /dev/null and b/Fuyu.Launcher/wwwroot/img/icon.png differ diff --git a/Fuyu.Launcher/wwwroot/img/nav-bg.png b/Fuyu.Launcher/wwwroot/img/nav-bg.png deleted file mode 100644 index cec6b1f..0000000 Binary files a/Fuyu.Launcher/wwwroot/img/nav-bg.png and /dev/null differ diff --git a/Fuyu.Launcher/wwwroot/index.html b/Fuyu.Launcher/wwwroot/index.html index 3bbc3a5..b2707bf 100644 --- a/Fuyu.Launcher/wwwroot/index.html +++ b/Fuyu.Launcher/wwwroot/index.html @@ -1,20 +1,21 @@ + + + + Fuyu Launcher + + + + + + - - - - Fuyu.Launcher - - - - - - - -
Loading...
- - - + +
+
+ + + diff --git a/Fuyu.Tests.Backend.EFT/EndToEnd/BackendTest.cs b/Fuyu.Tests.Backend.EFT/EndToEnd/BackendTest.cs index a367505..eec33d3 100644 --- a/Fuyu.Tests.Backend.EFT/EndToEnd/BackendTest.cs +++ b/Fuyu.Tests.Backend.EFT/EndToEnd/BackendTest.cs @@ -44,11 +44,11 @@ private static string CreateFuyuAccount(string username, string password) private static int CreateGameAccount(string sessionId, string game, string edition) { - var registerStatus = AccountService.RegisterGame(sessionId, game, edition); + var response = AccountService.RegisterGame(sessionId, game, edition); - if (registerStatus != ERegisterStatus.Success) + if (response.Status != ERegisterStatus.Success) { - throw new Exception(registerStatus.ToString()); + throw new Exception(response.Status.ToString()); } var gameAccountId = CoreOrm.GetAccount(sessionId).Games[game].Value;