Refactor launcher authentication, working import from depot
This commit is contained in:
parent
dc4a49d71a
commit
cbe363ac4f
30 changed files with 505 additions and 356 deletions
|
|
@ -149,5 +149,6 @@ namespace LANCommander.Launcher.Data
|
|||
|
||||
public DbSet<Media>? Media { get; set; }
|
||||
public DbSet<Library>? Libraries { get; set; }
|
||||
public DbSet<User>? Users { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||
namespace LANCommander.Launcher.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20250119055819_AddLibraries")]
|
||||
partial class AddLibraries
|
||||
[Migration("20250119103515_AddUserLibraries")]
|
||||
partial class AddUserLibraries
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
|
@ -532,10 +532,9 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("AvatarId")
|
||||
b.Property<Guid?>("AvatarId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
|
|
@ -545,14 +544,13 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AvatarId");
|
||||
|
||||
b.ToTable("User");
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LibraryGame", b =>
|
||||
|
|
@ -760,9 +758,7 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Media", "Avatar")
|
||||
.WithMany()
|
||||
.HasForeignKey("AvatarId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("AvatarId");
|
||||
|
||||
b.Navigation("Avatar");
|
||||
});
|
||||
|
|
@ -805,8 +801,7 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Library")
|
||||
.IsRequired();
|
||||
b.Navigation("Library");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
|
@ -6,31 +6,30 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||
namespace LANCommander.Launcher.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddLibraries : Migration
|
||||
public partial class AddUserLibraries : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "User",
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
UserName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Alias = table.Column<string>(type: "TEXT", nullable: false),
|
||||
AvatarId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
UserName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Alias = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AvatarId = table.Column<Guid>(type: "TEXT", nullable: true),
|
||||
CreatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_User", x => x.Id);
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_User_Media_AvatarId",
|
||||
name: "FK_Users_Media_AvatarId",
|
||||
column: x => x.AvatarId,
|
||||
principalTable: "Media",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
|
|
@ -46,9 +45,9 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
{
|
||||
table.PrimaryKey("PK_Libraries", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Libraries_User_UserId",
|
||||
name: "FK_Libraries_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "User",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
|
@ -89,8 +88,8 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
column: "LibraryId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_User_AvatarId",
|
||||
table: "User",
|
||||
name: "IX_Users_AvatarId",
|
||||
table: "Users",
|
||||
column: "AvatarId");
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +103,7 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
name: "Libraries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "User");
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -529,10 +529,9 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("AvatarId")
|
||||
b.Property<Guid?>("AvatarId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedOn")
|
||||
|
|
@ -542,14 +541,13 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AvatarId");
|
||||
|
||||
b.ToTable("User");
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LibraryGame", b =>
|
||||
|
|
@ -757,9 +755,7 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
{
|
||||
b.HasOne("LANCommander.Launcher.Data.Models.Media", "Avatar")
|
||||
.WithMany()
|
||||
.HasForeignKey("AvatarId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("AvatarId");
|
||||
|
||||
b.Navigation("Avatar");
|
||||
});
|
||||
|
|
@ -802,8 +798,7 @@ namespace LANCommander.Launcher.Data.Migrations
|
|||
|
||||
modelBuilder.Entity("LANCommander.Launcher.Data.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Library")
|
||||
.IsRequired();
|
||||
b.Navigation("Library");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace LANCommander.Launcher.Data.Models;
|
||||
|
||||
[Table("Users")]
|
||||
public class User : BaseModel
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public Media Avatar { get; set; }
|
||||
public Library Library { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Alias { get; set; }
|
||||
public Media? Avatar { get; set; }
|
||||
public Library? Library { get; set; }
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ namespace LANCommander.Launcher.Models;
|
|||
|
||||
public class ConnectionState
|
||||
{
|
||||
public bool ValidCredentials { get; set; }
|
||||
public bool IsConnected { get; set; }
|
||||
public bool OfflineModeEnabled { get; set; }
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ namespace LANCommander.Launcher.Models
|
|||
public AuthenticationSettings Authentication { get; set; } = new AuthenticationSettings();
|
||||
public GameSettings Games { get; set; } = new GameSettings();
|
||||
public MediaSettings Media { get; set; } = new MediaSettings();
|
||||
public ProfileSettings Profile { get; set; } = new ProfileSettings();
|
||||
public FilterSettings Filter { get; set; } = new FilterSettings();
|
||||
public DebugSettings Debug { get; set; } = new DebugSettings();
|
||||
public UpdateSettings Updates { get; set; } = new UpdateSettings();
|
||||
|
|
@ -46,13 +45,6 @@ namespace LANCommander.Launcher.Models
|
|||
public string StoragePath { get; set; } = "Media";
|
||||
}
|
||||
|
||||
public class ProfileSettings
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public Guid AvatarId { get; set; }
|
||||
}
|
||||
|
||||
public class FilterSettings
|
||||
{
|
||||
public string? Title { get; set; }
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ namespace LANCommander.Launcher.Services;
|
|||
|
||||
public class AuthenticationService : BaseService
|
||||
{
|
||||
private readonly ProfileService ProfileService;
|
||||
|
||||
private Settings Settings;
|
||||
|
||||
public event EventHandler OnLogin;
|
||||
|
|
@ -22,11 +20,9 @@ public class AuthenticationService : BaseService
|
|||
|
||||
public AuthenticationService(
|
||||
Client client,
|
||||
ILogger<AuthenticationService> logger,
|
||||
ProfileService profileService) : base(client, logger)
|
||||
ILogger<AuthenticationService> logger) : base(client, logger)
|
||||
{
|
||||
Settings = SettingService.GetSettings();
|
||||
ProfileService = profileService;
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
|
|
@ -83,8 +79,6 @@ public class AuthenticationService : BaseService
|
|||
SettingService.SaveSettings(Settings);
|
||||
|
||||
OnLogin?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
await ProfileService.DownloadProfileInfoAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,8 +111,16 @@ public class AuthenticationService : BaseService
|
|||
SettingService.SaveSettings(Settings);
|
||||
|
||||
OnRegister?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
await ProfileService.DownloadProfileInfoAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateConnectionAsync()
|
||||
{
|
||||
return await Client.ValidateTokenAsync();
|
||||
}
|
||||
|
||||
public bool OfflineModeEnabled()
|
||||
{
|
||||
return Settings.Authentication.OfflineMode;
|
||||
}
|
||||
|
||||
public void SetOfflineMode(bool state)
|
||||
|
|
@ -140,8 +142,7 @@ public class AuthenticationService : BaseService
|
|||
await Client.LogoutAsync();
|
||||
|
||||
Settings = SettingService.GetSettings();
|
||||
|
||||
Settings.Profile = new ProfileSettings();
|
||||
|
||||
Settings.Authentication = new AuthenticationSettings();
|
||||
|
||||
SettingService.SaveSettings(Settings);
|
||||
|
|
@ -156,12 +157,24 @@ public class AuthenticationService : BaseService
|
|||
if (decodedToken == null)
|
||||
return Guid.Empty;
|
||||
|
||||
if (Guid.TryParse(decodedToken.Id, out Guid id))
|
||||
var claim = decodedToken.Claims.First(c => c.Type == ClaimTypes.NameIdentifier);
|
||||
|
||||
if (Guid.TryParse(claim.Value, out Guid id))
|
||||
return id;
|
||||
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
public string GetCurrentUserName()
|
||||
{
|
||||
var decodedToken = DecodeToken();
|
||||
|
||||
if (decodedToken == null)
|
||||
return String.Empty;
|
||||
|
||||
return decodedToken.Claims.First(claim => claim.Type == ClaimTypes.Name).Value;
|
||||
}
|
||||
|
||||
public JwtSecurityToken DecodeToken()
|
||||
{
|
||||
if (Settings.Authentication.AccessToken == null)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ namespace LANCommander.Launcher.Services
|
|||
{
|
||||
public class CommandLineService : BaseService
|
||||
{
|
||||
private readonly AuthenticationService AuthenticationService;
|
||||
private readonly UserService UserService;
|
||||
private readonly GameService GameService;
|
||||
private readonly InstallService InstallService;
|
||||
private readonly ImportService ImportService;
|
||||
|
|
@ -22,11 +24,15 @@ namespace LANCommander.Launcher.Services
|
|||
public CommandLineService(
|
||||
SDK.Client client,
|
||||
ILogger<CommandLineService> logger,
|
||||
AuthenticationService authenticationService,
|
||||
UserService userService,
|
||||
GameService gameService,
|
||||
InstallService installService,
|
||||
ImportService importService,
|
||||
ProfileService profileService) : base(client, logger)
|
||||
{
|
||||
AuthenticationService = authenticationService;
|
||||
UserService = userService;
|
||||
GameService = gameService;
|
||||
InstallService = installService;
|
||||
ImportService = importService;
|
||||
|
|
@ -301,9 +307,11 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
private async Task ChangeAlias(ChangeAliasCommandLineOptions options)
|
||||
{
|
||||
var currentUser = await UserService.GetAsync(AuthenticationService.GetUserId());
|
||||
|
||||
await ProfileService.ChangeAlias(options.Alias);
|
||||
|
||||
Logger.LogInformation($"Changed current user's alias from {Settings.Profile.Alias} to {options.Alias}");
|
||||
Logger.LogInformation($"Changed current user's alias from {currentUser.Alias} to {options.Alias}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,16 +31,11 @@ namespace LANCommander.Launcher.Services
|
|||
{
|
||||
public class ImportService : BaseService
|
||||
{
|
||||
private readonly AuthenticationService AuthenticationService;
|
||||
private readonly MediaService MediaService;
|
||||
private readonly CollectionService CollectionService;
|
||||
private readonly CompanyService CompanyService;
|
||||
private readonly EngineService EngineService;
|
||||
private readonly GameService GameService;
|
||||
private readonly GenreService GenreService;
|
||||
private readonly PlatformService PlatformService;
|
||||
private readonly MultiplayerModeService MultiplayerModeService;
|
||||
private readonly RedistributableService RedistributableService;
|
||||
private readonly TagService TagService;
|
||||
private readonly LibraryService LibraryService;
|
||||
private readonly MessageBusService MessageBusService;
|
||||
private readonly Settings Settings;
|
||||
private readonly DatabaseContext DatabaseContext;
|
||||
|
|
@ -65,29 +60,19 @@ namespace LANCommander.Launcher.Services
|
|||
public ImportService(
|
||||
SDK.Client client,
|
||||
ILogger<ImportService> logger,
|
||||
AuthenticationService authenticationService,
|
||||
LibraryService libraryService,
|
||||
MediaService mediaService,
|
||||
CollectionService collectionService,
|
||||
CompanyService companyService,
|
||||
EngineService engineService,
|
||||
GameService gameService,
|
||||
GenreService genreService,
|
||||
PlatformService platformService,
|
||||
MultiplayerModeService multiplayerModeService,
|
||||
RedistributableService redistributableService,
|
||||
TagService tagService,
|
||||
MessageBusService messageBusService,
|
||||
DatabaseContext databaseContext) : base(client, logger)
|
||||
{
|
||||
AuthenticationService = authenticationService;
|
||||
LibraryService = libraryService;
|
||||
MediaService = mediaService;
|
||||
CollectionService = collectionService;
|
||||
CompanyService = companyService;
|
||||
EngineService = engineService;
|
||||
GameService = gameService;
|
||||
GenreService = genreService;
|
||||
PlatformService = platformService;
|
||||
MultiplayerModeService = multiplayerModeService;
|
||||
RedistributableService = redistributableService;
|
||||
TagService = tagService;
|
||||
MessageBusService = messageBusService;
|
||||
DatabaseContext = databaseContext;
|
||||
|
||||
|
|
@ -103,8 +88,7 @@ namespace LANCommander.Launcher.Services
|
|||
{
|
||||
try
|
||||
{
|
||||
await ImportGamesAsync();
|
||||
await ImportRedistributables();
|
||||
await ImportLibraryAsync();
|
||||
|
||||
OnImportComplete?.Invoke();
|
||||
}
|
||||
|
|
@ -113,61 +97,15 @@ namespace LANCommander.Launcher.Services
|
|||
OnImportFailed?.Invoke(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ICollection<TModel>> ImportBulkAsync<TModel, TKeyedModel>(
|
||||
ICollection<TModel> target,
|
||||
IEnumerable<TKeyedModel> source,
|
||||
Action<TModel, TKeyedModel> updateAction)
|
||||
where TModel : BaseModel
|
||||
where TKeyedModel : IKeyedModel
|
||||
{
|
||||
foreach (var sourceItem in source)
|
||||
{
|
||||
var existingItem = await DatabaseContext.Set<TModel>().FirstOrDefaultAsync(i => i.Id == sourceItem.Id);
|
||||
|
||||
if (existingItem != null)
|
||||
{
|
||||
updateAction(existingItem, sourceItem);
|
||||
|
||||
DatabaseContext.Update(existingItem);
|
||||
|
||||
await DatabaseContext.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add
|
||||
var item = (TModel)Activator.CreateInstance(typeof(TModel));
|
||||
|
||||
item.Id = sourceItem.Id;
|
||||
|
||||
updateAction(item, sourceItem);
|
||||
|
||||
var result = await DatabaseContext.Set<TModel>().AddAsync(item);
|
||||
|
||||
await DatabaseContext.SaveChangesAsync();
|
||||
|
||||
target.Add(result.Entity);
|
||||
}
|
||||
}
|
||||
|
||||
var toRemove = target.Where(x => !source.Any(y => y.Id == x.Id)).ToList();
|
||||
|
||||
foreach (var item in toRemove)
|
||||
{
|
||||
target.Remove(item);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
private async Task<Game> ImportGameAsync(Guid id)
|
||||
|
||||
public async Task<Game> ImportGameAsync(Guid id)
|
||||
{
|
||||
var game = await Client.Games.GetAsync(id);
|
||||
|
||||
return await ImportGameAsync(game);
|
||||
}
|
||||
|
||||
private async Task<Game> ImportGameAsync(SDK.Models.Game game)
|
||||
public async Task<Game> ImportGameAsync(SDK.Models.Game game)
|
||||
{
|
||||
using (var op = Logger.BeginOperation("Importing game {GameTitle}", game.Title))
|
||||
{
|
||||
|
|
@ -324,8 +262,6 @@ namespace LANCommander.Launcher.Services
|
|||
})
|
||||
.AsNoRemove()
|
||||
.ImportAsync();
|
||||
|
||||
var mediaStoragePath = MediaService.GetStoragePath();
|
||||
|
||||
var importedMedia = await DatabaseContext.BulkImport<Media, SDK.Models.Media>()
|
||||
.SetTarget(localGame.Media)
|
||||
|
|
@ -401,15 +337,17 @@ namespace LANCommander.Launcher.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task ImportGamesAsync()
|
||||
public async Task ImportLibraryAsync()
|
||||
{
|
||||
var library = await Client.Library.GetAsync();
|
||||
var remoteLibrary = await Client.Library.GetAsync();
|
||||
|
||||
await ImportGamesAsync(library);
|
||||
await ImportLibraryAsync(remoteLibrary);
|
||||
}
|
||||
|
||||
public async Task ImportGamesAsync(IEnumerable<SDK.Models.EntityReference> games)
|
||||
public async Task ImportLibraryAsync(IEnumerable<SDK.Models.EntityReference> games)
|
||||
{
|
||||
var library = await LibraryService.GetByUserAsync(AuthenticationService.GetUserId());
|
||||
|
||||
Logger?.LogInformation("Importing games");
|
||||
|
||||
int i = 1;
|
||||
|
|
@ -428,7 +366,11 @@ namespace LANCommander.Launcher.Services
|
|||
Total = games.Count()
|
||||
});
|
||||
|
||||
await ImportGameAsync(remoteGame);
|
||||
var importedGame = await ImportGameAsync(remoteGame);
|
||||
|
||||
library.Games.Add(importedGame);
|
||||
|
||||
await LibraryService.UpdateAsync(library);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -461,7 +403,7 @@ namespace LANCommander.Launcher.Services
|
|||
toImport.Add(libraryGame);
|
||||
}
|
||||
|
||||
await ImportGamesAsync(toImport);
|
||||
await ImportLibraryAsync(toImport);
|
||||
}
|
||||
|
||||
public async Task ImportRedistributables()
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ namespace LANCommander.Launcher.Services
|
|||
{
|
||||
public class LibraryService : BaseDatabaseService<Library>
|
||||
{
|
||||
private readonly AuthenticationService AuthenticationService;
|
||||
private readonly InstallService InstallService;
|
||||
private readonly GameService GameService;
|
||||
private readonly ImportService ImportService;
|
||||
private readonly UserService UserService;
|
||||
|
||||
public Dictionary<Guid, Process> RunningProcesses = new Dictionary<Guid, Process>();
|
||||
|
||||
|
|
@ -46,16 +47,17 @@ namespace LANCommander.Launcher.Services
|
|||
DatabaseContext databaseContext,
|
||||
SDK.Client client,
|
||||
ILogger<LibraryService> logger,
|
||||
AuthenticationService authenticationService,
|
||||
InstallService installService,
|
||||
GameService gameService,
|
||||
ImportService importService) : base(databaseContext, client, logger)
|
||||
UserService userService) : base(databaseContext, client, logger)
|
||||
{
|
||||
AuthenticationService = authenticationService;
|
||||
InstallService = installService;
|
||||
GameService = gameService;
|
||||
ImportService = importService;
|
||||
UserService = userService;
|
||||
|
||||
InstallService.OnInstallComplete += InstallService_OnInstallComplete;
|
||||
ImportService.OnImportComplete += ImportService_OnImportComplete;
|
||||
Filter.OnChanged += Filter_OnChanged;
|
||||
}
|
||||
|
||||
|
|
@ -65,13 +67,6 @@ namespace LANCommander.Launcher.Services
|
|||
await OnItemsFiltered.Invoke(Filter.ApplyFilter(Items));
|
||||
}
|
||||
|
||||
private async Task ImportService_OnImportComplete()
|
||||
{
|
||||
await RefreshItemsAsync();
|
||||
|
||||
LibraryChanged();
|
||||
}
|
||||
|
||||
private async Task InstallService_OnInstallComplete(Game game)
|
||||
{
|
||||
if (OnLibraryChanged != null)
|
||||
|
|
@ -95,11 +90,42 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
public async Task<Library> GetByUserAsync(Guid userId)
|
||||
{
|
||||
return Context
|
||||
.Libraries
|
||||
.Include(l => l.User)
|
||||
.Include(l => l.Games)
|
||||
.FirstOrDefault(l => l.User.Id == userId);
|
||||
var user = await Context
|
||||
.Users
|
||||
.Include(u => u.Library)
|
||||
.ThenInclude(l => l.Games)
|
||||
.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
|
||||
try
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
user = new User
|
||||
{
|
||||
Id = userId,
|
||||
};
|
||||
|
||||
user = Context.Users.Add(user).Entity;
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (user.Library == null)
|
||||
{
|
||||
user.Library = new Library();
|
||||
|
||||
user = Context.Users.Update(user).Entity;
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
return user.Library;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ListItem>> GetItemsAsync()
|
||||
|
|
@ -108,22 +134,26 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
using (var op = Logger.BeginOperation(LogLevel.Trace, "Loading library items from local database"))
|
||||
{
|
||||
var games = await Context.Games
|
||||
.Include(g => g.Collections)
|
||||
.Include(g => g.Developers)
|
||||
.Include(g => g.Genres)
|
||||
.Include(g => g.Publishers)
|
||||
.Include(g => g.Tags)
|
||||
.Include(g => g.PlaySessions)
|
||||
.Include(g => g.Engine)
|
||||
.Include(g => g.Platforms)
|
||||
.Include(g => g.Media)
|
||||
.Include(g => g.MultiplayerModes)
|
||||
.ToListAsync();
|
||||
var library = await GetByUserAsync(AuthenticationService.GetUserId());
|
||||
library = await Context
|
||||
.Libraries
|
||||
.AsQueryable()
|
||||
.Include(l => l.Games).ThenInclude(g => g.Collections)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Collections)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Developers)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Genres)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Publishers)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Tags)
|
||||
.Include(l => l.Games).ThenInclude(g => g.PlaySessions)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Engine)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Platforms)
|
||||
.Include(l => l.Games).ThenInclude(g => g.Media)
|
||||
.Include(l => l.Games).ThenInclude(g => g.MultiplayerModes)
|
||||
.FirstOrDefaultAsync(l => l.UserId == AuthenticationService.GetUserId());
|
||||
|
||||
Filter.Populate(games);
|
||||
Filter.Populate(library.Games);
|
||||
|
||||
foreach (var item in games.Select(g => new ListItem(g)).OrderByTitle(g => !String.IsNullOrWhiteSpace(g.SortName) ? g.SortName : g.Name))
|
||||
foreach (var item in library.Games.Select(g => new ListItem(g)).OrderByTitle(g => !String.IsNullOrWhiteSpace(g.SortName) ? g.SortName : g.Name))
|
||||
{
|
||||
Items.Add(item);
|
||||
}
|
||||
|
|
@ -174,20 +204,31 @@ namespace LANCommander.Launcher.Services
|
|||
|
||||
public async Task AddToLibraryAsync(Guid id)
|
||||
{
|
||||
await Client.Library.AddToLibrary(id);
|
||||
var localGame = await GameService.GetAsync(id);
|
||||
var library = await GetByUserAsync(AuthenticationService.GetUserId());
|
||||
|
||||
await ImportService.ImportGamesAsync(id);
|
||||
if (localGame != null)
|
||||
{
|
||||
library.Games.Add(localGame);
|
||||
}
|
||||
|
||||
await UpdateAsync(library);
|
||||
|
||||
await Client.Library.AddToLibrary(id);
|
||||
|
||||
await LibraryChanged();
|
||||
}
|
||||
|
||||
public async Task RemoveFromLibraryAsync(Guid id)
|
||||
{
|
||||
await Client.Library.RemoveFromLibrary(id);
|
||||
|
||||
var localGame = await GameService.GetAsync(id);
|
||||
var library = await GetByUserAsync(AuthenticationService.GetUserId());
|
||||
|
||||
await GameService.DeleteAsync(localGame);
|
||||
library.Games.Remove(localGame);
|
||||
|
||||
await UpdateAsync(library);
|
||||
|
||||
await Client.Library.RemoveFromLibrary(id);
|
||||
|
||||
await LibraryChanged();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,36 +13,49 @@ namespace LANCommander.Launcher.Services
|
|||
{
|
||||
public class ProfileService : BaseService
|
||||
{
|
||||
private readonly AuthenticationService AuthenticationService;
|
||||
private readonly MediaService MediaService;
|
||||
private readonly UserService UserService;
|
||||
|
||||
private Settings Settings;
|
||||
|
||||
public ProfileService(
|
||||
SDK.Client client,
|
||||
ILogger<ProfileService> logger,
|
||||
MediaService mediaService) : base(client, logger)
|
||||
AuthenticationService authenticationService,
|
||||
MediaService mediaService,
|
||||
UserService userService) : base(client, logger)
|
||||
{
|
||||
AuthenticationService = authenticationService;
|
||||
MediaService = mediaService;
|
||||
UserService = userService;
|
||||
Settings = SettingService.GetSettings();
|
||||
|
||||
AuthenticationService.OnLogin += async (sender, args) => await DownloadProfileInfoAsync();
|
||||
AuthenticationService.OnRegister += async (sender, args) => await DownloadProfileInfoAsync();
|
||||
}
|
||||
|
||||
public async Task ChangeAlias(string newName)
|
||||
{
|
||||
var currentUserId = AuthenticationService.GetUserId();
|
||||
var currentUser = await UserService.GetAsync(currentUserId);
|
||||
|
||||
await Client.Profile.ChangeAliasAsync(newName);
|
||||
|
||||
Settings = SettingService.GetSettings();
|
||||
|
||||
Settings.Profile.Alias = newName;
|
||||
|
||||
SettingService.SaveSettings(Settings);
|
||||
currentUser.Alias = newName;
|
||||
|
||||
await UserService.UpdateAsync(currentUser);
|
||||
}
|
||||
|
||||
public async Task DownloadProfileInfoAsync()
|
||||
{
|
||||
var remoteProfile = await Client.Profile.GetAsync();
|
||||
|
||||
Settings.Profile.Id = remoteProfile.Id;
|
||||
Settings.Profile.Alias = String.IsNullOrWhiteSpace(remoteProfile.Alias) ? remoteProfile.UserName : remoteProfile.Alias;
|
||||
var localUser = await UserService.AddMissingAsync(u => u.Id == remoteProfile.Id, new User
|
||||
{
|
||||
Id = remoteProfile.Id,
|
||||
UserName = remoteProfile.UserName,
|
||||
Alias = remoteProfile.Alias,
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -65,7 +78,9 @@ namespace LANCommander.Launcher.Services
|
|||
if (File.Exists(tempAvatarPath))
|
||||
File.Move(tempAvatarPath, localPath);
|
||||
|
||||
Settings.Profile.AvatarId = media.Id;
|
||||
localUser.Value.Avatar = media;
|
||||
|
||||
await UserService.UpdateAsync(localUser.Value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,36 @@
|
|||
using LANCommander.Launcher.Data;
|
||||
using LANCommander.Launcher.Data.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace LANCommander.Launcher.Services
|
||||
{
|
||||
public class UserService : BaseDatabaseService<User>
|
||||
{
|
||||
public UserService(DatabaseContext dbContext, SDK.Client client, ILogger<UserService> logger) : base(dbContext, client, logger)
|
||||
private readonly AuthenticationService AuthenticationService;
|
||||
public UserService(
|
||||
DatabaseContext dbContext,
|
||||
SDK.Client client,
|
||||
ILogger<UserService> logger,
|
||||
AuthenticationService authenticationService) : base(dbContext, client, logger)
|
||||
{
|
||||
AuthenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public async Task<User> GetCurrentUser()
|
||||
{
|
||||
return await Context
|
||||
.Users
|
||||
.AsQueryable()
|
||||
.Include(u => u.Avatar)
|
||||
.FirstOrDefaultAsync(u => u.Id == AuthenticationService.GetUserId());
|
||||
}
|
||||
|
||||
public async Task<string> GetAliasAsync(Guid id)
|
||||
{
|
||||
var user = await GetAsync(id);
|
||||
|
||||
return String.IsNullOrWhiteSpace(user.Alias) ? user.UserName : user.Alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,18 @@ app {
|
|||
bottom: 0;
|
||||
}
|
||||
|
||||
// Hack for Depot spinner
|
||||
.pb-custom-window-content > .ant-spin-nested-loading {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
& > div,
|
||||
& > div > .ant-spin-container,
|
||||
& .ant-layout {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-layout {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace LANCommander.Launcher.UI.Authenticate.Components;
|
||||
|
||||
public class AuthenticationFormState
|
||||
{
|
||||
public AuthenticationStage Stage { get; set; }
|
||||
public string ServerAddress { get; set; }
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
namespace LANCommander.Launcher.UI.Authenticate.Components;
|
||||
|
||||
public static class AuthenticationStage
|
||||
public enum AuthenticationStage
|
||||
{
|
||||
public const string Login = "Login";
|
||||
public const string Register = "Register";
|
||||
public const string SelectServer = "SelectServer";
|
||||
Login,
|
||||
Register,
|
||||
SelectServer,
|
||||
}
|
||||
|
|
@ -5,14 +5,14 @@
|
|||
@inject IMessageService MessageService
|
||||
@inject ILogger<LoginForm> Logger
|
||||
|
||||
<PageHeader OnBack="Back">
|
||||
<PageHeader OnBack="OnBack">
|
||||
<TitleTemplate>
|
||||
Sign In
|
||||
</TitleTemplate>
|
||||
</PageHeader>
|
||||
<Form Model="@Model" Loading="@Loading" Layout="FormLayout.Vertical" OnFinish="OnFinish">
|
||||
<FormItem Label="Server Address">
|
||||
<Input @bind-Value="@ServerAddress" Disabled />
|
||||
<Input Value="ServerAddress" Disabled />
|
||||
</FormItem>
|
||||
<FormItem Label="Username">
|
||||
<Input @bind-Value="@context.UserName"/>
|
||||
|
|
@ -25,13 +25,6 @@
|
|||
Login
|
||||
</Button>
|
||||
|
||||
@if (Settings.Profile.Id != Guid.Empty && !String.IsNullOrWhiteSpace(Settings.Profile.Alias))
|
||||
{
|
||||
<Button OnClick="OfflineMode">
|
||||
Offline
|
||||
</Button>
|
||||
}
|
||||
|
||||
<Button Type="ButtonType.Text" OnClick="@(() => NavigationManager.NavigateTo("/Authenticate/Register"))">
|
||||
Register
|
||||
</Button>
|
||||
|
|
@ -68,7 +61,7 @@
|
|||
|
||||
@code {
|
||||
[Parameter] public string ServerAddress { get; set; }
|
||||
[Parameter] public EventCallback<string> ServerAddressChanged { get; set; }
|
||||
[Parameter] public EventCallback OnBack { get; set; }
|
||||
|
||||
List<AuthenticationProvider> AuthenticationProviders = new();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
@inject IMessageService MessageService
|
||||
@inject ILogger<RegistrationForm> Logger
|
||||
|
||||
<PageHeader OnBack="Back">
|
||||
<PageHeader OnBack="OnBack">
|
||||
<TitleTemplate>
|
||||
Register
|
||||
</TitleTemplate>
|
||||
</PageHeader>
|
||||
<Form Model="@Model" Loading="@Loading" Layout="FormLayout.Vertical" OnFinish="OnFinish">
|
||||
<FormItem Label="Server Address">
|
||||
<Input @bind-Value="@ServerAddress" Disabled />
|
||||
<Input Value="ServerAddress" Disabled />
|
||||
</FormItem>
|
||||
<FormItem Label="Username">
|
||||
<Input @bind-Value="@context.UserName"/>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
@code {
|
||||
[Parameter] public string ServerAddress { get; set; }
|
||||
[Parameter] public EventCallback<string> ServerAddressChanged { get; set; }
|
||||
[Parameter] public EventCallback OnBack { get; set; }
|
||||
|
||||
RegistrationRequest Model = new();
|
||||
bool Loading = false;
|
||||
|
|
@ -66,11 +66,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
void Back()
|
||||
{
|
||||
NavigationManager.NavigateTo("/Authenticate/Login");
|
||||
}
|
||||
|
||||
async Task UseToken(AuthToken token)
|
||||
{
|
||||
Settings.Authentication.AccessToken = token.AccessToken;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,6 @@
|
|||
<Flex Gap="FlexGap.Small">
|
||||
<Input @bind-Value="ServerAddress" Placeholder="Server Address"/>
|
||||
<Button Type="ButtonType.Primary" OnClick="() => SelectServer(ServerAddress)">Connect</Button>
|
||||
|
||||
@if (OfflineModeAvailable)
|
||||
{
|
||||
<Button OnClick="() => OfflineMode()">Offline</Button>
|
||||
}
|
||||
</Flex>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
|
@ -50,11 +45,12 @@
|
|||
</AntList>
|
||||
|
||||
@code {
|
||||
[Parameter] public string ServerAddress { get; set; }
|
||||
[Parameter] public EventCallback<string> ServerAddressChanged { get; set; }
|
||||
[Parameter] public EventCallback<string> OnSelected { get; set; }
|
||||
|
||||
bool BeaconActive = false;
|
||||
bool OfflineModeAvailable = false;
|
||||
|
||||
string ServerAddress = String.Empty;
|
||||
|
||||
AuthRequest Model = new();
|
||||
List<DiscoveredServer> DiscoveredServers = new();
|
||||
|
|
@ -70,25 +66,15 @@
|
|||
|
||||
async Task SelectServer(string serverAddress)
|
||||
{
|
||||
ServerAddress = serverAddress;
|
||||
|
||||
Probe.Stop();
|
||||
Probe.Dispose();
|
||||
|
||||
BeaconActive = false;
|
||||
|
||||
if (ServerAddressChanged.HasDelegate)
|
||||
await ServerAddressChanged.InvokeAsync(ServerAddress);
|
||||
if (OnSelected.HasDelegate)
|
||||
await OnSelected.InvokeAsync(serverAddress);
|
||||
|
||||
NavigationManager.NavigateTo("Authenticate/Login");
|
||||
}
|
||||
|
||||
async Task OfflineMode()
|
||||
{
|
||||
Settings.Authentication.OfflineMode = true;
|
||||
SettingService.SaveSettings(Settings);
|
||||
|
||||
NavigationManager.NavigateTo("/");
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
async Task ActivateBeacon()
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
@page "/Authenticate"
|
||||
@page "/Authenticate/{stage}"
|
||||
@using System.Security.Policy
|
||||
@using BeaconLib
|
||||
@using LANCommander.Launcher.Models
|
||||
@using LANCommander.SDK
|
||||
@using LANCommander.SDK.Models
|
||||
@using LANCommander.Launcher.UI.Authenticate.Components
|
||||
@inject ProfileService ProfileService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject SDK.Client Client
|
||||
@inject IMessageService MessageService
|
||||
@inject ILogger<System.Index> Logger
|
||||
|
||||
<Layout Style="background-image: url('/assets/auth-background.jpg'); background-size: cover;">
|
||||
<Content Class="authentication-form">
|
||||
<div class="authentication-logo">
|
||||
<img src="/assets/logo.svg" width="300" />
|
||||
</div>
|
||||
|
||||
<div class="authentication-box">
|
||||
@if (Stage == AuthenticationStage.SelectServer || String.IsNullOrWhiteSpace(Stage))
|
||||
{
|
||||
<ServerSelector @bind-ServerAddress="ServerAddress" />
|
||||
}
|
||||
|
||||
@if (Stage == AuthenticationStage.Login)
|
||||
{
|
||||
<LoginForm @bind-ServerAddress="ServerAddress" />
|
||||
}
|
||||
|
||||
@if (Stage == AuthenticationStage.Register)
|
||||
{
|
||||
<RegistrationForm @bind-ServerAddress="ServerAddress" />
|
||||
}
|
||||
</div>
|
||||
</Content>
|
||||
</Layout>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Stage { get; set; }
|
||||
|
||||
string ServerAddress { get; set; }
|
||||
}
|
||||
27
LANCommander.Launcher/UI/Components/AuthenticatedView.razor
Normal file
27
LANCommander.Launcher/UI/Components/AuthenticatedView.razor
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
@using ConnectionState = LANCommander.Launcher.Models.ConnectionState
|
||||
@inject AuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<CascadingValue Value="ConnectionState">
|
||||
@if (ConnectionState.IsConnected || ConnectionState.OfflineModeEnabled)
|
||||
{
|
||||
@Authenticated
|
||||
}
|
||||
else
|
||||
{
|
||||
@NotAuthenticated
|
||||
}
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment Authenticated { get; set; }
|
||||
[Parameter] public RenderFragment NotAuthenticated { get; set; }
|
||||
|
||||
public ConnectionState ConnectionState = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ConnectionState.IsConnected = await AuthenticationService.ValidateConnectionAsync();
|
||||
ConnectionState.OfflineModeEnabled = AuthenticationService.OfflineModeEnabled();
|
||||
}
|
||||
}
|
||||
57
LANCommander.Launcher/UI/Components/AuthenticationForm.razor
Normal file
57
LANCommander.Launcher/UI/Components/AuthenticationForm.razor
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
@using System.Security.Policy
|
||||
@using BeaconLib
|
||||
@using LANCommander.Launcher.Models
|
||||
@using LANCommander.SDK
|
||||
@using LANCommander.SDK.Models
|
||||
@using LANCommander.Launcher.UI.Authenticate.Components
|
||||
@inject ProfileService ProfileService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject SDK.Client Client
|
||||
@inject IMessageService MessageService
|
||||
@inject ILogger<System.Index> Logger
|
||||
|
||||
<CascadingValue Value="State">
|
||||
<Layout Style="background-image: url('/assets/auth-background.jpg'); background-size: cover;">
|
||||
<Content Class="authentication-form">
|
||||
<div class="authentication-logo">
|
||||
<img src="/assets/logo.svg" width="300" />
|
||||
</div>
|
||||
|
||||
<div class="authentication-box">
|
||||
@if (State.Stage == AuthenticationStage.SelectServer)
|
||||
{
|
||||
<ServerSelector OnSelected="ServerSelected" />
|
||||
}
|
||||
|
||||
@if (State.Stage == AuthenticationStage.Login)
|
||||
{
|
||||
<LoginForm ServerAddress="@State.ServerAddress" OnBack="() => State.Stage = AuthenticationStage.SelectServer" />
|
||||
}
|
||||
|
||||
@if (State.Stage == AuthenticationStage.Register)
|
||||
{
|
||||
<RegistrationForm ServerAddress="@State.ServerAddress" OnBack="() => State.Stage = AuthenticationStage.Login" />
|
||||
}
|
||||
</div>
|
||||
</Content>
|
||||
</Layout>
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
AuthenticationFormState State { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
State = new()
|
||||
{
|
||||
Stage = AuthenticationStage.SelectServer,
|
||||
ServerAddress = String.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
void ServerSelected(string serverAddress)
|
||||
{
|
||||
State.ServerAddress = serverAddress;
|
||||
State.Stage = AuthenticationStage.Login;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
@using System.Diagnostics
|
||||
@using LANCommander.SDK.Helpers
|
||||
@inject GameService GameService
|
||||
@inject UserService UserService
|
||||
@inject LibraryService LibraryService
|
||||
@inject InstallService InstallService
|
||||
@inject ModalService ModalService
|
||||
|
|
@ -262,11 +263,12 @@ else
|
|||
|
||||
async Task RunNameChangeScripts()
|
||||
{
|
||||
var user = await UserService.GetCurrentUser();
|
||||
var manifests = await Client.Games.GetManifestsAsync(Game.InstallDirectory, Game.Id);
|
||||
|
||||
foreach (var manifest in manifests)
|
||||
{
|
||||
await Client.Scripts.RunNameChangeScriptAsync(Game.InstallDirectory, Game.Id, Settings.Profile.Alias);
|
||||
await Client.Scripts.RunNameChangeScriptAsync(Game.InstallDirectory, Game.Id, user.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@using LANCommander.Launcher.Models
|
||||
@using LANCommander.Launcher.Data.Models
|
||||
@using LANCommander.Launcher.Models
|
||||
@inject UserService UserService
|
||||
@inject ProfileService ProfileService
|
||||
@inject AuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
|
@ -25,12 +27,12 @@
|
|||
|
||||
<ChildContent>
|
||||
<Button Type="ButtonType.Primary" Class="appbar-profile-button">
|
||||
@if (Settings.Profile.AvatarId != null && Settings.Profile.AvatarId != Guid.Empty)
|
||||
@if (User != null && User.Avatar != null)
|
||||
{
|
||||
<MediaImage Id="@Settings.Profile.AvatarId" />
|
||||
<MediaImage Id="@User.Avatar.Id" />
|
||||
}
|
||||
|
||||
<span>@Settings.Profile.Alias</span>
|
||||
<span>@User?.Alias</span>
|
||||
</Button>
|
||||
</ChildContent>
|
||||
</Dropdown>
|
||||
|
|
@ -39,15 +41,15 @@
|
|||
[CascadingParameter] public ConnectionState ConnectionState { get; set; }
|
||||
Models.Settings Settings = null;
|
||||
|
||||
User User;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Settings = SettingService.GetSettings();
|
||||
User = await UserService.GetCurrentUser();
|
||||
}
|
||||
|
||||
async Task ChangeAlias()
|
||||
{
|
||||
var settings = SettingService.GetSettings();
|
||||
|
||||
var modalOptions = new ModalOptions()
|
||||
{
|
||||
Title = "Change Name",
|
||||
|
|
@ -57,7 +59,7 @@
|
|||
Centered = true
|
||||
};
|
||||
|
||||
var modalRef = ModalService.CreateModal<ChangeAliasDialog, string, string>(modalOptions, settings.Profile.Alias);
|
||||
var modalRef = ModalService.CreateModal<ChangeAliasDialog, string, string>(modalOptions, User.Alias);
|
||||
|
||||
modalRef.OnOk = async (newName) =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
@inject LibraryService LibraryService
|
||||
@inject GameService GameService
|
||||
@inject InstallService InstallService
|
||||
@inject ImportService ImportService
|
||||
@inject SDK.Client Client
|
||||
@inject ILogger<DepotGameDetails> Logger
|
||||
@inject NavigationManager NavigationManager
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
<SpaceItem>
|
||||
@if (IsInstalled)
|
||||
{
|
||||
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" OnClick="@(() => NavigationManager.NavigateTo($"/{SelectedGame.Id}"))">View in Library</Button>
|
||||
<Button Type="ButtonType.Primary" Size="ButtonSize.Large" OnClick="@(() => NavigationManager.NavigateTo($"{SelectedGame.Id}"))">View in Library</Button>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -248,7 +249,9 @@
|
|||
await Task.Yield();
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
await ImportService.ImportGameAsync(SelectedGame.Id);
|
||||
await LibraryService.AddToLibraryAsync(SelectedGame.Id);
|
||||
await LibraryService.RefreshItemsAsync();
|
||||
|
||||
|
|
@ -261,8 +264,7 @@
|
|||
Logger?.LogError(ex, $"{SelectedGame.Title} ({SelectedGame.Id}) could not be added to your library!");
|
||||
MessageService.Error($"{SelectedGame.Title} could not be added to your library!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
AddingToLibrary = false;
|
||||
StateHasChanged();
|
||||
await Task.Yield();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
@inject SDK.Client Client
|
||||
@inject DepotService DepotService
|
||||
@inject LibraryService LibraryService
|
||||
@inject ImportService ImportService
|
||||
@inject IMessageService MessageService
|
||||
@inject ILogger<DepotGame> Logger
|
||||
|
||||
|
|
@ -39,7 +40,9 @@
|
|||
StateHasChanged();
|
||||
await Task.Yield();
|
||||
|
||||
await ImportService.ImportGameAsync(Item.Key);
|
||||
await LibraryService.AddToLibraryAsync(Item.Key);
|
||||
await LibraryService.RefreshItemsAsync();
|
||||
|
||||
Importing = false;
|
||||
StateHasChanged();
|
||||
|
|
|
|||
|
|
@ -1,34 +1,28 @@
|
|||
@page "/Depot"
|
||||
@page "/Depot/{id:guid}"
|
||||
@using LANCommander.Launcher.Data.Models
|
||||
@using LANCommander.Launcher.Models.Enums
|
||||
@using LANCommander.Launcher.Models
|
||||
@using System.Diagnostics
|
||||
@using LANCommander.Launcher.UI.Depot.Components
|
||||
@using LANCommander.SDK.Helpers
|
||||
@inject SDK.Client Client
|
||||
@implements IDisposable
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject DepotService DepotService
|
||||
@inject InstallService InstallService
|
||||
@inject GameService GameService
|
||||
@inject ModalService ModalService
|
||||
@inject ConfirmService ConfirmService
|
||||
@inject ImportService ImportService
|
||||
@inject KeepAliveService KeepAliveService
|
||||
|
||||
<Layout>
|
||||
<Content Class="depot">
|
||||
<DepotFilter Items="@ListItems" />
|
||||
<Spin Spinning="Disabled">
|
||||
<Layout>
|
||||
<Content Class="depot">
|
||||
<DepotFilter Items="@ListItems" />
|
||||
|
||||
<DepotList Items="@ListItems" OnItemSelected="@((item) => NavigationManager.NavigateTo($"/Depot/{item.Key}"))" />
|
||||
</Content>
|
||||
</Layout>
|
||||
<DepotList Items="@ListItems" OnItemSelected="@((item) => NavigationManager.NavigateTo($"/Depot/{item.Key}"))" />
|
||||
</Content>
|
||||
</Layout>
|
||||
|
||||
<DepotGameDetails @ref="DepotGameDetails" ItemId="@(SelectedItem?.Key ?? Guid.Empty)" OnClose="@(() => NavigationManager.NavigateTo("/Depot"))" />
|
||||
</Spin>
|
||||
|
||||
<div class="logo">
|
||||
<img src="assets/logo-cut.svg" />
|
||||
</div>
|
||||
|
||||
<DepotGameDetails @ref="DepotGameDetails" ItemId="@(SelectedItem?.Key ?? Guid.Empty)" OnClose="@(() => NavigationManager.NavigateTo("/Depot"))" />
|
||||
|
||||
<LANCommander.Launcher.UI.Components.Footer />
|
||||
|
||||
@code {
|
||||
|
|
@ -42,13 +36,36 @@
|
|||
|
||||
Settings Settings = SettingService.GetSettings();
|
||||
|
||||
bool Disabled = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
DepotService.OnItemsFiltered += LoadFilteredItems;
|
||||
|
||||
KeepAliveService.ConnectionSevered += KeepAliveServiceOnConnectionSevered;
|
||||
KeepAliveService.ConnectionEstablished += KeepAliveServiceOnConnectionEstablished;
|
||||
KeepAliveService.ConnectionLostPermanently += KeepAliveServiceOnConnectionLostPermanently;
|
||||
|
||||
await DepotService.RefreshItemsAsync();
|
||||
}
|
||||
|
||||
private void KeepAliveServiceOnConnectionLostPermanently(object? sender, EventArgs e)
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
private async void KeepAliveServiceOnConnectionEstablished(object? sender, EventArgs e)
|
||||
{
|
||||
Disabled = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async void KeepAliveServiceOnConnectionSevered(object? sender, EventArgs e)
|
||||
{
|
||||
Disabled = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SelectedItem = DepotService.Items.FirstOrDefault(i => i.Key == Id);
|
||||
|
|
@ -65,4 +82,12 @@
|
|||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
KeepAliveService.ConnectionEstablished -= KeepAliveServiceOnConnectionEstablished;
|
||||
KeepAliveService.ConnectionSevered -= KeepAliveServiceOnConnectionSevered;
|
||||
KeepAliveService.ConnectionLostPermanently -= KeepAliveServiceOnConnectionLostPermanently;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@
|
|||
InstallService.OnInstallFail += OnInstallFail;
|
||||
InstallService.OnInstallComplete += OnInstallComplete;
|
||||
GameService.OnUninstallComplete += OnUninstallComplete;
|
||||
|
||||
|
||||
await LoadData();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@
|
|||
@inject LANCommander.SDK.Client LANCommander
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<CascadingValue Value="ConnectionState">
|
||||
<CustomWindow HeaderHeight="37">
|
||||
<HeaderExtraControlsLayout>
|
||||
<Space Direction="SpaceDirection.Horizontal">
|
||||
<ConnectionStateView>
|
||||
<Online>
|
||||
<AuthenticatedView>
|
||||
<Authenticated>
|
||||
<SpaceItem>
|
||||
<Popover Placement="Placement.BottomRight" IsButton OnClick="Import" Trigger="new[] { Trigger.Hover }">
|
||||
<ChildContent>
|
||||
|
|
@ -29,63 +28,49 @@
|
|||
</ContentTemplate>
|
||||
</Popover>
|
||||
</SpaceItem>
|
||||
</Online>
|
||||
<Offline>
|
||||
|
||||
<SpaceItem>
|
||||
<ProfileButton/>
|
||||
</SpaceItem>
|
||||
</Authenticated>
|
||||
|
||||
<NotAuthenticated>
|
||||
<SpaceItem>
|
||||
<Button Type="@ButtonType.Text" Icon="@IconType.Outline.CloudSync" OnClick="Connect" Loading="@Connecting" Danger/>
|
||||
</SpaceItem>
|
||||
</Offline>
|
||||
</ConnectionStateView>
|
||||
|
||||
@if (Settings != null && Settings.Profile != null && ProfileService.IsAuthenticated())
|
||||
{
|
||||
<SpaceItem>
|
||||
<ProfileButton />
|
||||
</SpaceItem>
|
||||
}
|
||||
</NotAuthenticated>
|
||||
</AuthenticatedView>
|
||||
</Space>
|
||||
</HeaderExtraControlsLayout>
|
||||
<WindowContent>
|
||||
<ErrorBoundary>
|
||||
<ChildContent>
|
||||
@Body
|
||||
<ErrorHandler>
|
||||
<AuthenticatedView>
|
||||
<Authenticated>
|
||||
@Body
|
||||
|
||||
@if (Settings.Debug.EnableScriptDebugging)
|
||||
{
|
||||
<PowerShellConsole />
|
||||
}
|
||||
@if (Settings.Debug.EnableScriptDebugging)
|
||||
{
|
||||
<PowerShellConsole/>
|
||||
}
|
||||
|
||||
<UpdateChecker />
|
||||
<AntContainer />
|
||||
<KeepAliveContainer />
|
||||
<RedirectToLogin />
|
||||
</ChildContent>
|
||||
<ErrorContent>
|
||||
<Result Status="ResultStatus.Error"
|
||||
Title="Launcher Crashed"
|
||||
SubTitle="@RandomQuip"
|
||||
Class="crash-error">
|
||||
<Extra>
|
||||
<Button Type="ButtonType.Primary" OnClick="@(() => NavigationManager.NavigateTo("/", true))">View Library</Button>
|
||||
<Button OnClick="() => CopyError(context)">Copy Error</Button>
|
||||
</Extra>
|
||||
<ChildContent>
|
||||
<code>
|
||||
@context.StackTrace
|
||||
</code>
|
||||
</ChildContent>
|
||||
</Result>
|
||||
</ErrorContent>
|
||||
</ErrorBoundary>
|
||||
<KeepAliveContainer />
|
||||
</Authenticated>
|
||||
<NotAuthenticated>
|
||||
<AuthenticationForm />
|
||||
</NotAuthenticated>
|
||||
</AuthenticatedView>
|
||||
|
||||
<UpdateChecker />
|
||||
<AntContainer />
|
||||
</ErrorHandler>
|
||||
</WindowContent>
|
||||
</CustomWindow>
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
Models.Settings Settings = null;
|
||||
|
||||
public bool Importing = false;
|
||||
public bool Connecting = false;
|
||||
public bool Importing;
|
||||
public bool Connecting;
|
||||
|
||||
public ConnectionState ConnectionState = new();
|
||||
|
||||
|
|
@ -140,16 +125,10 @@
|
|||
ImportService.OnImportUpdated += OnImportUpdated;
|
||||
|
||||
AuthenticationService.OnOfflineModeChanged += OnOfflineModeChanged;
|
||||
|
||||
|
||||
var randIndex = new Random().Next(0, CrashQuips.Length - 1);
|
||||
|
||||
RandomQuip = CrashQuips[randIndex];
|
||||
|
||||
ConnectionState.IsConnected = await LANCommander.ValidateTokenAsync();
|
||||
ConnectionState.OfflineModeEnabled = Settings.Authentication.OfflineMode;
|
||||
|
||||
if (!ConnectionState.IsConnected && !ConnectionState.OfflineModeEnabled)
|
||||
NavigationManager.NavigateTo("/Authenticate");
|
||||
}
|
||||
|
||||
async Task OnImportComplete()
|
||||
|
|
|
|||
75
LANCommander.UI/Components/ErrorHandler.razor
Normal file
75
LANCommander.UI/Components/ErrorHandler.razor
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
@using Microsoft.JSInterop
|
||||
@inject IJSRuntime JS
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<ErrorBoundary>
|
||||
<ChildContent>
|
||||
@ChildContent
|
||||
</ChildContent>
|
||||
<ErrorContent>
|
||||
<Result Status="ResultStatus.Error"
|
||||
Title="Launcher Crashed"
|
||||
SubTitle="@RandomQuip"
|
||||
Class="crash-error">
|
||||
<Extra>
|
||||
<Button Type="ButtonType.Primary" OnClick="@(() => NavigationManager.NavigateTo("/", true))">View Library</Button>
|
||||
<Button OnClick="() => CopyError(context)">Copy Error</Button>
|
||||
</Extra>
|
||||
<ChildContent>
|
||||
<code>
|
||||
@context.Message
|
||||
@context.StackTrace
|
||||
</code>
|
||||
</ChildContent>
|
||||
</Result>
|
||||
</ErrorContent>
|
||||
</ErrorBoundary>
|
||||
|
||||
@code {
|
||||
[Parameter] public RenderFragment ChildContent { get; set; }
|
||||
|
||||
string RandomQuip = "";
|
||||
|
||||
string[] CrashQuips = new[]
|
||||
{
|
||||
"You Died.",
|
||||
"Snake? SNAAAAAAKE!",
|
||||
"WASTED",
|
||||
"Major fracture detected",
|
||||
"The past is a gaping hole. You try to run from it, but the more you run, the deeper, the darker, the bigger it gets.",
|
||||
"Your town center has been destroyed",
|
||||
"Your forces are under attack!",
|
||||
"You have lost the lead",
|
||||
"Terrorists Win",
|
||||
"War... War never changes.",
|
||||
"You have died of dysentery",
|
||||
"You have failed to restore the books. The Ages are lost.",
|
||||
"Player was splattered by a demon",
|
||||
"Sure, blame it on your ISP",
|
||||
"Baba is no more",
|
||||
"Guests are complaining they are lost",
|
||||
"The darkness has overcome you",
|
||||
"Subject: Gordon Freeman. Status: Terminated",
|
||||
"Mission failed: You were spotted.",
|
||||
"Critical damage! Eject, eject!",
|
||||
"Your minions are unhappy. They are leaving.",
|
||||
"The Empire has triumphed",
|
||||
"Your quest has ended in failure",
|
||||
"You have been eaten by a grue",
|
||||
"You no mess with Lo Wang!",
|
||||
"Sam was killed. Serious carnage ensues.",
|
||||
"Damn, those alien bastards are gonna pay for shooting up my ride"
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var randIndex = new Random().Next(0, CrashQuips.Length - 1);
|
||||
|
||||
RandomQuip = CrashQuips[randIndex];
|
||||
}
|
||||
|
||||
async Task CopyError(Exception ex)
|
||||
{
|
||||
await JS.InvokeVoidAsync("navigator.clipboard.writeText", ex.Message + "\n" + ex.StackTrace);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue