mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
code style
This commit is contained in:
@@ -2,4 +2,5 @@
|
||||
<CornerRadius x:Key="ControlCornerRadiusTop">4,4,0,0</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusBottom">0,0,4,4</CornerRadius>
|
||||
<CornerRadius x:Key="ControlCornerRadiusTopRightAndBottomLeft">0,4,0,4</CornerRadius>
|
||||
<CornerRadius x:Key="CornerRadiusAll16">16</CornerRadius>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
namespace Snap.Hutao.Model.Entity;
|
||||
|
||||
[Table("uid_profile_pictures")]
|
||||
[SuppressMessage("", "SH002")]
|
||||
internal sealed class UidProfilePicture : IMappingFrom<UidProfilePicture, PlayerUid, ProfilePicture>
|
||||
{
|
||||
[Key]
|
||||
@@ -27,12 +26,13 @@ internal sealed class UidProfilePicture : IMappingFrom<UidProfilePicture, Player
|
||||
|
||||
public DateTimeOffset RefreshTime { get; set; }
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public static UidProfilePicture From(PlayerUid uid, ProfilePicture profilePicture)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Uid = uid.ToString(),
|
||||
ProfilePictureId = profilePicture.ProfilePictureId,
|
||||
ProfilePictureId = profilePicture.Id,
|
||||
AvatarId = profilePicture.AvatarId,
|
||||
CostumeId = profilePicture.CostumeId,
|
||||
RefreshTime = DateTimeOffset.Now,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
internal interface IProfilePictureService
|
||||
{
|
||||
ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default(CancellationToken));
|
||||
|
||||
ValueTask RefreshUserGameRoleAsync(UserGameRole userGameRole, CancellationToken token = default(CancellationToken));
|
||||
}
|
||||
@@ -10,6 +10,4 @@ internal interface IUserInitializationService
|
||||
ValueTask<ViewModel.User.User?> CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default(CancellationToken));
|
||||
|
||||
ValueTask<ViewModel.User.User> ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken));
|
||||
|
||||
ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default);
|
||||
}
|
||||
109
src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs
Normal file
109
src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Web.Enka;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
|
||||
namespace Snap.Hutao.Service.User;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Singleton, typeof(IProfilePictureService))]
|
||||
internal sealed partial class ProfilePictureService : IProfilePictureService
|
||||
{
|
||||
private readonly IUidProfilePictureDbService uidProfilePictureDbService;
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public async ValueTask TryInitializeAsync(ViewModel.User.User user, CancellationToken token = default)
|
||||
{
|
||||
foreach (UserGameRole userGameRole in user.UserGameRoles)
|
||||
{
|
||||
if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture)
|
||||
{
|
||||
if (await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Force update
|
||||
await RefreshUserGameRoleAsync(userGameRole, token: token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RefreshUserGameRoleAsync(UserGameRole userGameRole, CancellationToken token = default)
|
||||
{
|
||||
EnkaResponse? enkaResponse;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
EnkaClient enkaClient = scope.ServiceProvider
|
||||
.GetRequiredService<EnkaClient>();
|
||||
|
||||
enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false)
|
||||
?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (enkaResponse is { PlayerInfo: { } playerInfo })
|
||||
{
|
||||
UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture);
|
||||
|
||||
await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false);
|
||||
await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false);
|
||||
|
||||
await TryUpdateUserGameRoleAsync(userGameRole, profilePicture, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> TryUpdateUserGameRoleAsync(UserGameRole userGameRole, UidProfilePicture cache, CancellationToken token = default)
|
||||
{
|
||||
if (cache.RefreshTime.AddDays(15) < DateTimeOffset.Now)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UserMetadataContext context = await metadataService.GetContextAsync<UserMetadataContext>(token).ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
// Most common to most rare
|
||||
if (cache.ProfilePictureId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.Id == cache.ProfilePictureId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cache.AvatarId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == cache.AvatarId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cache.CostumeId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == cache.CostumeId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,4 @@ internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbS
|
||||
{
|
||||
await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Abstraction;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.Entity.Extension;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
||||
using Snap.Hutao.Web.Enka;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using Snap.Hutao.Web.Hoyolab.Bbs.User;
|
||||
using Snap.Hutao.Web.Hoyolab.Passport;
|
||||
@@ -22,9 +16,8 @@ namespace Snap.Hutao.Service.User;
|
||||
internal sealed partial class UserInitializationService : IUserInitializationService
|
||||
{
|
||||
private readonly IUserFingerprintService userFingerprintService;
|
||||
private readonly IUidProfilePictureDbService uidProfilePictureDbService;
|
||||
private readonly IProfilePictureService profilePictureService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public async ValueTask<ViewModel.User.User> ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default)
|
||||
{
|
||||
@@ -63,29 +56,6 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default)
|
||||
{
|
||||
EnkaResponse? enkaResponse;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
EnkaClient enkaClient = scope.ServiceProvider
|
||||
.GetRequiredService<EnkaClient>();
|
||||
|
||||
enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false)
|
||||
?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (enkaResponse is { PlayerInfo: { } playerInfo })
|
||||
{
|
||||
UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture);
|
||||
|
||||
await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false);
|
||||
await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false);
|
||||
|
||||
await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> InitializeUserAsync(ViewModel.User.User user, CancellationToken token = default)
|
||||
{
|
||||
if (user.IsInitialized)
|
||||
@@ -120,9 +90,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
return false;
|
||||
}
|
||||
|
||||
TrySetUserUserGameRolesProfilePictureAsync(user, token).SafeForget();
|
||||
|
||||
await userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||
await profilePictureService.TryInitializeAsync(user, token).ConfigureAwait(false);
|
||||
|
||||
return user.IsInitialized = true;
|
||||
}
|
||||
@@ -249,76 +218,4 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask TrySetUserUserGameRolesProfilePictureAsync(ViewModel.User.User user, CancellationToken token = default)
|
||||
{
|
||||
foreach (UserGameRole userGameRole in user.UserGameRoles)
|
||||
{
|
||||
if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture)
|
||||
{
|
||||
if (await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await RefreshProfilePictureAsync(userGameRole, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> SetUserGameRoleProfilePictureCoreAsync(UserGameRole userGameRole, UidProfilePicture profilePicture, CancellationToken token = default)
|
||||
{
|
||||
if (profilePicture.RefreshTime.AddDays(15) < DateTimeOffset.Now)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UserMetadataContext context;
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
IMetadataService metadataService = scope.ServiceProvider
|
||||
.GetRequiredService<IMetadataService>();
|
||||
|
||||
if (!await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
context = await scope.ServiceProvider
|
||||
.GetRequiredService<IMetadataService>()
|
||||
.GetContextAsync<UserMetadataContext>(token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
if (profilePicture.ProfilePictureId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.Id == profilePicture.ProfilePictureId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (profilePicture.CostumeId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == profilePicture.CostumeId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (profilePicture.AvatarId is not 0U)
|
||||
{
|
||||
userGameRole.ProfilePictureIcon = context.ProfilePictures
|
||||
.Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == profilePicture.AvatarId)
|
||||
.Icon;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ namespace Snap.Hutao.Service.User;
|
||||
[Injection(InjectAs.Singleton, typeof(IUserService))]
|
||||
internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
{
|
||||
private readonly IUserInitializationService userInitializationService;
|
||||
private readonly IProfilePictureService profilePictureService;
|
||||
private readonly IUserCollectionService userCollectionService;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly IUserDbService userDbService;
|
||||
@@ -125,6 +125,6 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
|
||||
|
||||
public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole)
|
||||
{
|
||||
await userInitializationService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false);
|
||||
await profilePictureService.RefreshUserGameRoleAsync(userGameRole).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -301,7 +301,7 @@
|
||||
<ScrollViewer>
|
||||
<ItemsControl
|
||||
Margin="0,0,-4,0"
|
||||
HorizontalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
ItemContainerTransitions="{StaticResource ListViewLikeThemeTransitions}"
|
||||
ItemTemplate="{StaticResource DownloadSummaryTemplate}"
|
||||
ItemsPanel="{StaticResource WrapPanelSpacing0Template}"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
Height="32"
|
||||
Margin="2,0"
|
||||
HorizontalAlignment="Left"
|
||||
Background="#FFDAB79B"
|
||||
ProfilePicture="{Binding ProfilePictureIcon, Converter={StaticResource AvatarIconCircleConverter}}"/>
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
@@ -40,9 +41,9 @@
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
Command="{Binding DataContext.RefreshProfilePictureCommand, Source={StaticResource ViewModelBindingProxy}}"
|
||||
Command="{Binding RefreshProfilePictureCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
CornerRadius="8">
|
||||
CornerRadius="{ThemeResource CornerRadiusAll16}">
|
||||
<Button.Resources>
|
||||
<Storyboard x:Key="ShowRefreshIcon">
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RefreshIcon" Storyboard.TargetProperty="Visibility">
|
||||
|
||||
@@ -20,6 +20,7 @@ internal static class StaticResource
|
||||
{ "AchievementIcon", 0 },
|
||||
{ "AvatarCard", 0 },
|
||||
{ "AvatarIcon", 0 },
|
||||
{ "AvatarIconCircle", 0 },
|
||||
{ "Bg", 0 },
|
||||
{ "ChapterIcon", 0 },
|
||||
{ "CodexMonster", 0 },
|
||||
@@ -50,6 +51,7 @@ internal static class StaticResource
|
||||
{ "AchievementIcon", 2 },
|
||||
{ "AvatarCard", 2 },
|
||||
{ "AvatarIcon", 5 },
|
||||
{ "AvatarIconCircle", 1 },
|
||||
{ "Bg", 3 },
|
||||
{ "ChapterIcon", 3 },
|
||||
{ "CodexMonster", 0 },
|
||||
|
||||
@@ -298,10 +298,4 @@ internal sealed partial class UserViewModel : ObservableObject
|
||||
FlyoutBase.ShowAttachedFlyout(appBarButton);
|
||||
infoBarService.Warning(message);
|
||||
}
|
||||
|
||||
[Command("RefreshProfilePictureCommand")]
|
||||
private async Task RefreshProfilePictureAsync(UserGameRole userGameRole)
|
||||
{
|
||||
await userService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -29,25 +29,25 @@ internal sealed partial class EnkaClient
|
||||
|
||||
public ValueTask<EnkaResponse?> GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.EnkaPlayerInfo(playerUid), token);
|
||||
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.EnkaPlayerInfo(playerUid), true, token);
|
||||
}
|
||||
|
||||
public ValueTask<EnkaResponse?> GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaInfoAPI, playerUid), token);
|
||||
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaInfoAPI, playerUid), false, token);
|
||||
}
|
||||
|
||||
public ValueTask<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), token);
|
||||
return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), true, token);
|
||||
}
|
||||
|
||||
public ValueTask<EnkaResponse?> GetDataAsync(in PlayerUid playerUid, CancellationToken token = default)
|
||||
{
|
||||
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaAPI, playerUid), token);
|
||||
return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaAPI, playerUid), false, token);
|
||||
}
|
||||
|
||||
private async ValueTask<EnkaResponse?> TryGetEnkaResponseCoreAsync(string url, CancellationToken token = default)
|
||||
private async ValueTask<EnkaResponse?> TryGetEnkaResponseCoreAsync(string url, bool isForward, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -63,6 +63,16 @@ internal sealed partial class EnkaClient
|
||||
}
|
||||
else
|
||||
{
|
||||
// We want to fallback to original API and retry when requesting our forward api
|
||||
if (isForward)
|
||||
{
|
||||
string content = await response.Content.ReadAsStringAsync(token).ConfigureAwait(false);
|
||||
if (content.Contains("nginx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/yoimiya-kokomi/miao-plugin/pull/441
|
||||
// Additionally, HTTP codes for UID requests:
|
||||
// 400 = wrong UID format
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Snap.Hutao.Web.Enka.Model;
|
||||
internal sealed class ProfilePicture
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public ProfilePictureId ProfilePictureId { get; set; }
|
||||
public ProfilePictureId Id { get; set; }
|
||||
|
||||
[JsonPropertyName("avatarId")]
|
||||
public AvatarId AvatarId { get; set; }
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Snap.Hutao.Service.User;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
|
||||
@@ -13,6 +14,7 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||
internal sealed class UserGameRole : ObservableObject
|
||||
{
|
||||
private string? profilePictureIcon;
|
||||
private ICommand? refreshProfilePictureCommand;
|
||||
|
||||
/// <summary>
|
||||
/// hk4e_cn for Genshin Impact
|
||||
@@ -77,6 +79,11 @@ internal sealed class UserGameRole : ObservableObject
|
||||
set => SetProperty(ref profilePictureIcon, value);
|
||||
}
|
||||
|
||||
public ICommand RefreshProfilePictureCommand
|
||||
{
|
||||
get => refreshProfilePictureCommand ??= new AsyncRelayCommand(RefreshProfilePictureAsync);
|
||||
}
|
||||
|
||||
public static implicit operator PlayerUid(UserGameRole userGameRole)
|
||||
{
|
||||
return new PlayerUid(userGameRole.GameUid, userGameRole.Region);
|
||||
@@ -87,4 +94,10 @@ internal sealed class UserGameRole : ObservableObject
|
||||
{
|
||||
return $"{Nickname} | {RegionName} | Lv.{Level}";
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH003")]
|
||||
private async Task RefreshProfilePictureAsync()
|
||||
{
|
||||
await Ioc.Default.GetRequiredService<IUserService>().RefreshProfilePictureAsync(this).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user