From d02172377bf4ea2b6dfa21a8e6e46ab7798809ff Mon Sep 17 00:00:00 2001 From: Holden <122419606+midozen@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:35:58 -0500 Subject: [PATCH] Add platform info to profile DTO with setting-based visibility --- .../Mapping/ApplicationMappingProfile.cs | 6 +++- src/RecNet.Application/Profiles/ProfileDto.cs | 8 +++++ .../Profiles/ProfileService.cs | 36 ++++++++++++++++--- .../Profiles/IProfileRepository.cs | 1 + .../Repositories/ProfileRepository.cs | 29 +++++++++++---- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/RecNet.Application/Common/Mapping/ApplicationMappingProfile.cs b/src/RecNet.Application/Common/Mapping/ApplicationMappingProfile.cs index 57d9866..1f57cc3 100644 --- a/src/RecNet.Application/Common/Mapping/ApplicationMappingProfile.cs +++ b/src/RecNet.Application/Common/Mapping/ApplicationMappingProfile.cs @@ -3,6 +3,7 @@ using RecNet.Application.Profiles; using RecNet.Application.Profiles.Avatar; using RecNet.Application.Profiles.Relationships; using RecNet.Application.Profiles.Settings; +using RecNet.Domain.Common; namespace RecNet.Application.Common.Mapping; @@ -10,7 +11,10 @@ public class ApplicationMappingProfile : Profile { public ApplicationMappingProfile() { - CreateMap(); + CreateMap() + .ForMember( + x => x.PlatformId, + opt => opt.MapFrom(x => x.Platform == PlatformType.Steamworks ? x.PlatformId : null)); CreateMap(); CreateMap(); CreateMap(); diff --git a/src/RecNet.Application/Profiles/ProfileDto.cs b/src/RecNet.Application/Profiles/ProfileDto.cs index 0c762c7..ba8e088 100644 --- a/src/RecNet.Application/Profiles/ProfileDto.cs +++ b/src/RecNet.Application/Profiles/ProfileDto.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using RecNet.Domain.Common; namespace RecNet.Application.Profiles; @@ -12,4 +13,11 @@ public class ProfileDTO [JsonPropertyName("Developer")] public bool IsDeveloper { get; init; } + + [JsonPropertyName("Platform")] + public PlatformType Platform { get; init; } + + [JsonPropertyName("PlatformId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? PlatformId { get; init; } } diff --git a/src/RecNet.Application/Profiles/ProfileService.cs b/src/RecNet.Application/Profiles/ProfileService.cs index 2d1aadf..67fe256 100644 --- a/src/RecNet.Application/Profiles/ProfileService.cs +++ b/src/RecNet.Application/Profiles/ProfileService.cs @@ -21,13 +21,15 @@ public class ProfileService( ITokenService tokenService, IMapper mapper) : IProfileService { + private const string ShowPlatformPreferenceKey = "SHOW_PLATFORM_PREF"; + public async Task GetProfileAsync(Guid profileId, CancellationToken ct = default) { var profile = await profileRepository.GetByIdAsync(profileId, ct); return profile is null ? null - : mapper.Map(profile); + : await MapProfileAsync(profile, ct); } public async Task> GetProfilesAsync( @@ -36,7 +38,11 @@ public class ProfileService( { var profiles = await profileRepository.GetByIdsAsync(profileIds, ct); - return mapper.Map>(profiles); + var results = new List(profiles.Count); + foreach (var profile in profiles) + results.Add(await MapProfileAsync(profile, ct)); + + return results; } public async Task GetAvatarAsync(Guid profileId, CancellationToken ct = default) @@ -116,7 +122,7 @@ public class ProfileService( var isValidGameVersion = await gameVersionRepository.IsValidAsync(command.AppVersion, ct); if (!isValidGameVersion) - return LoginProfileResult.Fail("Invalid app version."); + return LoginProfileResult.Fail("A new update is available! Go to https://recroom.baby to download the latest update."); // 2. Validate Platform Authentication var validator = validators.FirstOrDefault(x => x.PlatformType == command.PlatformType); @@ -166,9 +172,29 @@ public class ProfileService( var token = tokenService.GenerateProfileToken(profile); return LoginProfileResult.Success( - profile: mapper.Map(profile), + profile: await MapProfileAsync(profile, ct), accessToken: token.AccessToken, expiresIn: token.ExpiresIn ); } -} \ No newline at end of file + + private async Task MapProfileAsync(ProfileEntity profile, CancellationToken ct) + { + var showPlatformPreference = profile.Platform == PlatformType.Steamworks && + await profileRepository.GetSettingByProfileId( + profile.ProfileId, + ShowPlatformPreferenceKey, + ct); + + return new ProfileDTO + { + ProfileId = profile.ProfileId, + Name = profile.Name, + IsDeveloper = profile.IsDeveloper, + Platform = profile.Platform, + PlatformId = profile.Platform == PlatformType.Steamworks && showPlatformPreference + ? profile.PlatformId + : null + }; + } +} diff --git a/src/RecNet.Domain/Profiles/IProfileRepository.cs b/src/RecNet.Domain/Profiles/IProfileRepository.cs index b2f16bb..5c5d0a0 100644 --- a/src/RecNet.Domain/Profiles/IProfileRepository.cs +++ b/src/RecNet.Domain/Profiles/IProfileRepository.cs @@ -7,6 +7,7 @@ public interface IProfileRepository Task GetByIdAsync(Guid profileId, CancellationToken ct = default); Task GetByIdWithSettingsAsync(Guid profileId, CancellationToken ct = default); Task GetAvatarByProfileIdAsync(Guid profileId, CancellationToken ct = default); + Task GetSettingByProfileId(Guid profileId, string key, CancellationToken ct = default); Task> GetSettingsByProfileIdAsync(Guid profileId, CancellationToken ct = default); Task> GetByIdsAsync(List profileIds, CancellationToken ct = default); diff --git a/src/RecNet.Infrastructure/Persistence/Repositories/ProfileRepository.cs b/src/RecNet.Infrastructure/Persistence/Repositories/ProfileRepository.cs index 40e6960..ed395ba 100644 --- a/src/RecNet.Infrastructure/Persistence/Repositories/ProfileRepository.cs +++ b/src/RecNet.Infrastructure/Persistence/Repositories/ProfileRepository.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using Microsoft.EntityFrameworkCore; using RecNet.Domain.Common; using RecNet.Domain.Profiles; @@ -7,7 +8,7 @@ namespace RecNet.Infrastructure.Persistence.Repositories; public class ProfileRepository(DatabaseContext dbContext) : IProfileRepository { public Task GetByIdAsync( - Guid profileId, + Guid profileId, CancellationToken ct = default) => dbContext.Profiles .FirstOrDefaultAsync(x => x.ProfileId == profileId, ct); @@ -28,6 +29,20 @@ public class ProfileRepository(DatabaseContext dbContext) : IProfileRepository .Select(x => x.Avatar) .FirstOrDefaultAsync(ct); + public async Task GetSettingByProfileId(Guid profileId, string key, CancellationToken ct = default) + { + var setting = await dbContext.PlayerSettings + .AsNoTracking() + .Where(x => + x.UserId == profileId && + x.Key == key) + .FirstOrDefaultAsync(ct); + if (setting == null) + return default; + + return JsonSerializer.Deserialize(setting.Value); + } + public async Task> GetSettingsByProfileIdAsync( Guid profileId, CancellationToken ct = default) @@ -44,13 +59,13 @@ public class ProfileRepository(DatabaseContext dbContext) : IProfileRepository .ToListAsync(ct); public Task GetByPlatform( - PlatformType platform, - string platformId, + PlatformType platform, + string platformId, CancellationToken ct = default) => dbContext.Profiles .FirstOrDefaultAsync(x => - x.Platform == platform && - x.PlatformId == platformId, + x.Platform == platform && + x.PlatformId == platformId, ct); public Task GetProfileTokenVersion( @@ -63,10 +78,10 @@ public class ProfileRepository(DatabaseContext dbContext) : IProfileRepository .FirstOrDefaultAsync(ct); public async Task AddAsync( - Profile profile, + Profile profile, CancellationToken ct = default) => await dbContext.Profiles.AddAsync(profile, ct); public Task SaveChangesAsync(CancellationToken ct = default) => dbContext.SaveChangesAsync(ct); -} +} \ No newline at end of file