From 3b2eeb84a7076e1274477b6b0501ffd35efa33c1 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Fri, 14 Jun 2024 01:09:32 +0800 Subject: [PATCH 1/7] add profile picture for each game role --- .../Snap.Hutao/Control/Theme/Converter.xaml | 1 + ...942_UserGameRoleProfilePicture.Designer.cs | 654 ++++++++++++++++++ ...240613144942_UserGameRoleProfilePicture.cs | 38 + .../Migrations/AppDbContextModelSnapshot.cs | 29 +- .../Model/Entity/Database/AppDbContext.cs | 2 + .../Entity/UserGameRoleProfilePicture.cs | 41 ++ .../Model/Metadata/Avatar/ProfilePicture.cs | 2 +- .../Converter/AvatarIconCircleConverter.cs | 29 + ...aDictionaryAvatarIdProfilePictureSource.cs | 11 + ...DictionaryCostumeIdProfilePictureSource.cs | 11 + ...etadataDictionaryIdProfilePictureSource.cs | 11 + .../MetadataServiceContextExtension.cs | 15 + .../Service/Metadata/MetadataFileNames.cs | 1 + .../MetadataServiceDictionaryExtension.cs | 15 + .../Service/User/IUserGameRoleDbService.cs | 18 + .../User/IUserInitializationService.cs | 4 + .../Service/User/IUserMetadataContext.cs | 11 + .../Snap.Hutao/Service/User/IUserService.cs | 2 + .../Service/User/UserGameRoleDbService.cs | 37 + .../Service/User/UserInitializationService.cs | 98 +++ .../Service/User/UserMetadataContext.cs | 16 + .../Snap.Hutao/Service/User/UserService.cs | 6 + src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 75 +- .../ViewModel/User/UserViewModel.cs | 7 + .../Snap.Hutao/Web/Enka/EnkaClient.cs | 12 + .../Web/Enka/Model/ProfilePicture.cs | 8 +- .../Hoyolab/Takumi/Binding/UserGameRole.cs | 2 + .../Snap.Hutao/Web/HutaoEndpoints.cs | 6 + 28 files changed, 1151 insertions(+), 11 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconCircleConverter.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml index beef99ac..d9b8a21e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/Converter.xaml @@ -10,6 +10,7 @@ + diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs new file mode 100644 index 00000000..3e6db24b --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs @@ -0,0 +1,654 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Model.Entity.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20240613144942_UserGameRoleProfilePicture")] + partial class UserGameRoleProfilePicture + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalculatorRefreshTime") + .HasColumnType("TEXT"); + + b.Property("GameRecordRefreshTime") + .HasColumnType("TEXT"); + + b.Property("Info") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ShowcaseRefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("cultivate_entries"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("AvatarLevelTo") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("TEXT"); + + b.Property("SkillALevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillALevelTo") + .HasColumnType("INTEGER"); + + b.Property("SkillELevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillELevelTo") + .HasColumnType("INTEGER"); + + b.Property("SkillQLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("SkillQLevelTo") + .HasColumnType("INTEGER"); + + b.Property("WeaponLevelFrom") + .HasColumnType("INTEGER"); + + b.Property("WeaponLevelTo") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("EntryId") + .IsUnique(); + + b.ToTable("cultivate_entry_level_informations"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("TEXT"); + + b.Property("IsFinished") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("EntryId"); + + b.ToTable("cultivate_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachedUid") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("cultivate_projects"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachUid") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("inventory_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AppendPropIdList") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("MainPropId") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("inventory_reliquaries"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("INTEGER"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("PromoteLevel") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.HasIndex("ProjectId"); + + b.ToTable("inventory_weapons"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("ExpireTime") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("object_cache"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ScheduleId") + .HasColumnType("INTEGER"); + + b.Property("SpiralAbyss") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("spiral_abysses"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Aid") + .HasColumnType("TEXT"); + + b.Property("CookieToken") + .HasColumnType("TEXT"); + + b.Property("CookieTokenLastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("Fingerprint") + .HasColumnType("TEXT"); + + b.Property("FingerprintLastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("Index") + .HasColumnType("INTEGER"); + + b.Property("IsOversea") + .HasColumnType("INTEGER"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("LToken") + .HasColumnType("TEXT") + .HasColumnName("Ltoken"); + + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("PreferredUid") + .HasColumnType("TEXT"); + + b.Property("SToken") + .HasColumnType("TEXT") + .HasColumnName("Stoken"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("LastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("profile_pictures"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry") + .WithOne("LevelInformation") + .HasForeignKey("Snap.Hutao.Model.Entity.CultivateEntryLevelInformation", "EntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry") + .WithMany() + .HasForeignKey("EntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b => + { + b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project") + .WithMany() + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b => + { + b.Navigation("LevelInformation"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs new file mode 100644 index 00000000..84e5271f --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs @@ -0,0 +1,38 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + /// + public partial class UserGameRoleProfilePicture : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "profile_pictures", + columns: table => new + { + InnerId = table.Column(type: "TEXT", nullable: false), + Uid = table.Column(type: "TEXT", nullable: false), + ProfilePictureId = table.Column(type: "INTEGER", nullable: false), + AvatarId = table.Column(type: "INTEGER", nullable: false), + CostumeId = table.Column(type: "INTEGER", nullable: false), + LastUpdateTime = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_profile_pictures", x => x.InnerId); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "profile_pictures"); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index fd2b7d28..3be71058 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "8.0.2"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { @@ -515,6 +515,33 @@ namespace Snap.Hutao.Migrations b.ToTable("users"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("LastUpdateTime") + .HasColumnType("TEXT"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("profile_pictures"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index d03ac2d8..8e6f5a16 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -65,6 +65,8 @@ internal sealed class AppDbContext : DbContext public DbSet SpiralAbysses { get; set; } = default!; + public DbSet UserGameRoleProfilePictures { get; set; } = default!; + public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString) { DbContextOptions options = new DbContextOptionsBuilder() diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs new file mode 100644 index 00000000..21bc9372 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs @@ -0,0 +1,41 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.Abstraction; +using Snap.Hutao.Web.Enka.Model; +using Snap.Hutao.Web.Hoyolab; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Snap.Hutao.Model.Entity; + +[Table("profile_pictures")] +[SuppressMessage("", "SH002")] +internal sealed class UserGameRoleProfilePicture : IMappingFrom +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid InnerId { get; set; } + + public string Uid { get; set; } = default!; + + public uint ProfilePictureId { get; set; } + + public uint AvatarId { get; set; } + + public uint CostumeId { get; set; } + + public DateTimeOffset LastUpdateTime { get; set; } + + public static UserGameRoleProfilePicture From(PlayerUid uid, ProfilePicture profilePicture) + { + return new() + { + Uid = uid.ToString(), + ProfilePictureId = profilePicture.ProfilePictureId, + AvatarId = profilePicture.AvatarId, + CostumeId = profilePicture.CostumeId, + LastUpdateTime = DateTimeOffset.Now, + }; + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProfilePicture.cs index 7aad4ef0..0231bdfb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/ProfilePicture.cs @@ -26,4 +26,4 @@ internal sealed class ProfilePicture /// -> /// public uint UnlockParameter { get; set; } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconCircleConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconCircleConverter.cs new file mode 100644 index 00000000..97a04a65 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarIconCircleConverter.cs @@ -0,0 +1,29 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Control; + +namespace Snap.Hutao.Model.Metadata.Converter; + +/// +/// 玩家头像转换器 +/// +[HighQuality] +internal sealed class AvatarIconCircleConverter : ValueConverter +{ + /// + /// 名称转Uri + /// + /// 名称 + /// 链接 + public static Uri IconNameToUri(string name) + { + return Web.HutaoEndpoints.StaticRaw("AvatarIconCircle", $"{name}.png").ToUri(); + } + + /// + public override Uri Convert(string from) + { + return IconNameToUri(from); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs new file mode 100644 index 00000000..fe755b48 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Service.Metadata.ContextAbstraction; + +internal interface IMetadataDictionaryAvatarIdProfilePictureSource +{ + public Dictionary AvatarIdProfilePictureMap { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs new file mode 100644 index 00000000..655d2cd6 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Service.Metadata.ContextAbstraction; + +internal interface IMetadataDictionaryCostumeIdProfilePictureSource +{ + public Dictionary CostumeIdProfilePictureMap { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs new file mode 100644 index 00000000..66605c17 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Service.Metadata.ContextAbstraction; + +internal interface IMetadataDictionaryIdProfilePictureSource +{ + public Dictionary IdProfilePictureMap { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs index fd17f81d..53a5d504 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs @@ -66,6 +66,21 @@ internal static class MetadataServiceContextExtension dictionaryIdMaterialSource.IdMaterialMap = await metadataService.GetIdToMaterialMapAsync(token).ConfigureAwait(false); } + if (context is IMetadataDictionaryIdProfilePictureSource dictionaryIdProfilePictureSource) + { + dictionaryIdProfilePictureSource.IdProfilePictureMap = await metadataService.GetIdToProfilePictureMapAsync(token).ConfigureAwait(false); + } + + if (context is IMetadataDictionaryAvatarIdProfilePictureSource dictionaryAvatarIdProfilePictureSource) + { + dictionaryAvatarIdProfilePictureSource.AvatarIdProfilePictureMap = await metadataService.GetAvatarIdToProfilePictureMapAsync(token).ConfigureAwait(false); + } + + if (context is IMetadataDictionaryCostumeIdProfilePictureSource dictionaryCostumeIdProfilePictureSource) + { + dictionaryCostumeIdProfilePictureSource.CostumeIdProfilePictureMap = await metadataService.GetCostumeIdToProfilePictureMapAsync(token).ConfigureAwait(false); + } + if (context is IMetadataDictionaryIdReliquarySource dictionaryIdReliquarySource) { dictionaryIdReliquarySource.IdReliquaryMap = await metadataService.GetIdToReliquaryMapAsync(token).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataFileNames.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataFileNames.cs index 6a2e2b5c..0080a6cd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataFileNames.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataFileNames.cs @@ -16,6 +16,7 @@ internal static class MetadataFileNames public const string FileNameMaterial = "Material"; public const string FileNameMonster = "Monster"; public const string FileNameMonsterCurve = "MonsterCurve"; + public const string FileNameProfilePicture = "ProfilePicture"; public const string FileNameReliquary = "Reliquary"; public const string FileNameReliquaryAffixWeight = "ReliquaryAffixWeight"; public const string FileNameReliquaryMainAffix = "ReliquaryMainAffix"; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs index 364808a5..3313145a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs @@ -71,6 +71,21 @@ internal static class MetadataServiceDictionaryExtension return metadataService.FromCacheAsDictionaryAsync(FileNameMaterial, a => a.Id, token); } + public static ValueTask> GetIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) + { + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.Id, token); + } + + public static ValueTask> GetAvatarIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) + { + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.AvatarId, token); + } + + public static ValueTask> GetCostumeIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) + { + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.CostumeId, token); + } + public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default) { return metadataService.FromCacheAsDictionaryAsync(FileNameReliquary, (List list) => list.SelectMany(r => r.Ids, (r, i) => (Index: i, Reliquary: r)), token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs new file mode 100644 index 00000000..217af9a2 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs @@ -0,0 +1,18 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; + +namespace Snap.Hutao.Service.User; + +internal interface IUserGameRoleDbService : IAppDbService +{ + ValueTask ContainsUidAsync(string uid, CancellationToken token = default); + + ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); + + ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default); + + ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs index 442bd68b..5509609e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs @@ -1,6 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; + namespace Snap.Hutao.Service.User; internal interface IUserInitializationService @@ -8,4 +10,6 @@ internal interface IUserInitializationService ValueTask CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default(CancellationToken)); ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); + + ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs new file mode 100644 index 00000000..4b64ae97 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Service.Metadata.ContextAbstraction; + +namespace Snap.Hutao.Service.User; + +internal interface IUserMetadataContext : IMetadataContext, + IMetadataDictionaryIdProfilePictureSource, + IMetadataDictionaryAvatarIdProfilePictureSource, + IMetadataDictionaryCostumeIdProfilePictureSource; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index 011b0f40..08e1fd87 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -53,4 +53,6 @@ internal interface IUserService /// 待移除的用户 /// 任务 ValueTask RemoveUserAsync(BindingUser user); + + ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs new file mode 100644 index 00000000..09ef97f4 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs @@ -0,0 +1,37 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; + +namespace Snap.Hutao.Service.User; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IUserGameRoleDbService))] +internal sealed partial class UserGameRoleDbService : IUserGameRoleDbService +{ + private readonly IServiceProvider serviceProvider; + + public IServiceProvider ServiceProvider { get => serviceProvider; } + + public ValueTask ContainsUidAsync(string uid, CancellationToken token = default) + { + return this.QueryAsync(query => query.AnyAsync(n => n.Uid == uid)); + } + + public ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) + { + return this.QueryAsync(query => query.FirstAsync(n => n.Uid == uid)); + } + + public async ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default) + { + await this.UpdateAsync(profilePicture, token).ConfigureAwait(false); + } + + public async ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) + { + await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index 18af3acd..e0bbe03a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -2,12 +2,18 @@ // 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.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; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; +using MetadataProfilePicture = Snap.Hutao.Model.Metadata.Avatar.ProfilePicture; namespace Snap.Hutao.Service.User; @@ -16,6 +22,7 @@ namespace Snap.Hutao.Service.User; internal sealed partial class UserInitializationService : IUserInitializationService { private readonly IUserFingerprintService userFingerprintService; + private readonly IUserGameRoleDbService userGameRoleDbService; private readonly IServiceProvider serviceProvider; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) @@ -55,6 +62,30 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } + public async ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) + { + EnkaResponse? enkaResponse; + using (IServiceScope scope = serviceProvider.CreateScope()) + { + EnkaClient enkaClient = scope.ServiceProvider + .GetRequiredService(); + + enkaResponse = await enkaClient + .GetForwardPlayerInfoAsync(userGameRole, token) + .ConfigureAwait(false); + } + + if (enkaResponse is { PlayerInfo: { } playerInfo }) + { + UserGameRoleProfilePicture profilePicture = UserGameRoleProfilePicture.From(userGameRole, playerInfo.ProfilePicture); + + await userGameRoleDbService.DeleteUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); + await userGameRoleDbService.UpdateUserGameRoleProfilePictureAsync(profilePicture, token).ConfigureAwait(false); + + await SetUserGameRolesProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); + } + } + private async ValueTask InitializeUserAsync(ViewModel.User.User user, CancellationToken token = default) { if (user.IsInitialized) @@ -89,6 +120,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer return false; } + TrySetUserUserGameRolesProfilePictureAsync(user, token).SafeForget(); + await userFingerprintService.TryInitializeAsync(user, token).ConfigureAwait(false); return user.IsInitialized = true; @@ -216,4 +249,69 @@ 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 userGameRoleDbService.ContainsUidAsync(userGameRole.GameUid, token).ConfigureAwait(false)) + { + UserGameRoleProfilePicture savedProfilePicture = await userGameRoleDbService + .GetUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token) + .ConfigureAwait(false); + + if (await SetUserGameRolesProfilePictureCoreAsync(userGameRole, savedProfilePicture, token).ConfigureAwait(false)) + { + continue; + } + } + + await RefreshUserGameRolesProfilePictureAsync(userGameRole, token).ConfigureAwait(false); + } + } + + private async ValueTask SetUserGameRolesProfilePictureCoreAsync(UserGameRole userGameRole, UserGameRoleProfilePicture profilePicture, CancellationToken token = default) + { + if (profilePicture.LastUpdateTime.AddDays(15) < DateTimeOffset.Now) + { + return false; + } + + UserMetadataContext context; + using (IServiceScope scope = serviceProvider.CreateScope()) + { + IMetadataService metadataService = scope.ServiceProvider + .GetRequiredService(); + + if (!await metadataService.InitializeAsync().ConfigureAwait(false)) + { + return false; + } + + context = await scope.ServiceProvider + .GetRequiredService() + .GetContextAsync(token) + .ConfigureAwait(false); + } + + if (context.IdProfilePictureMap.TryGetValue(profilePicture.ProfilePictureId, out MetadataProfilePicture? metadataProfilePicture)) + { + userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + return true; + } + + if (context.CostumeIdProfilePictureMap.TryGetValue(profilePicture.CostumeId, out metadataProfilePicture)) + { + userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + return true; + } + + if (context.AvatarIdProfilePictureMap.TryGetValue(profilePicture.AvatarId, out metadataProfilePicture)) + { + userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + return true; + } + + return false; + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs new file mode 100644 index 00000000..03b60b6e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs @@ -0,0 +1,16 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Metadata.Avatar; +using Snap.Hutao.Model.Primitive; + +namespace Snap.Hutao.Service.User; + +internal class UserMetadataContext : IUserMetadataContext +{ + public Dictionary IdProfilePictureMap { get; set; } = default!; + + public Dictionary AvatarIdProfilePictureMap { get; set; } = default!; + + public Dictionary CostumeIdProfilePictureMap { get; set; } = default!; +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 24b46f0c..6766ad1e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -21,6 +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 IUserCollectionService userCollectionService; private readonly IServiceProvider serviceProvider; private readonly IUserDbService userDbService; @@ -121,4 +122,9 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe return true; } + + public async ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole) + { + await userInitializationService.RefreshUserGameRolesProfilePictureAsync(userGameRole).ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index e659bf97..022e4bfe 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -19,14 +19,73 @@ - - - - + + + + + + + + + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 3c3847ae..fc8d7b59 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -17,6 +17,7 @@ using Snap.Hutao.View.Dialog; using Snap.Hutao.View.Page; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Passport; +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; using System.Text; using EntityUser = Snap.Hutao.Model.Entity.User; @@ -297,4 +298,10 @@ internal sealed partial class UserViewModel : ObservableObject FlyoutBase.ShowAttachedFlyout(appBarButton); infoBarService.Warning(message); } + + [Command("RefreshUserGameRoleProfilePictureCommand")] + private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) + { + userService.RefreshUserGameRoleProfilePictureAsync(userGameRole).SafeForget(); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs index c02c196c..77e8814b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -21,11 +21,23 @@ namespace Snap.Hutao.Web.Enka; internal sealed partial class EnkaClient { private const string EnkaAPI = "https://enka.network/api/uid/{0}"; + private const string EnkaInfoAPI = "https://enka.network/api/uid/{0}?info"; private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory; private readonly JsonSerializerOptions options; private readonly HttpClient httpClient; + public ValueTask GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) + { + // TODO + return TryGetEnkaResponseCoreAsync($"https://enka-api.hut.ao/{playerUid}?info", token); + } + + public ValueTask GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) + { + return TryGetEnkaResponseCoreAsync(string.Format(CultureInfo.CurrentCulture, EnkaInfoAPI, playerUid), token); + } + public ValueTask GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default) { return TryGetEnkaResponseCoreAsync(HutaoEndpoints.Enka(playerUid), token); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs index e927d380..2bf0aafc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/Model/ProfilePicture.cs @@ -12,5 +12,11 @@ namespace Snap.Hutao.Web.Enka.Model; internal sealed class ProfilePicture { [JsonPropertyName("id")] - public ProfilePictureId Id { get; set; } + public ProfilePictureId ProfilePictureId { get; set; } + + [JsonPropertyName("avatarId")] + public AvatarId AvatarId { get; set; } + + [JsonPropertyName("costumeId")] + public CostumeId CostumeId { get; set; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs index e3322d57..1ad7a81b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs @@ -57,6 +57,8 @@ internal sealed class UserGameRole [JsonPropertyName("is_official")] public bool IsOfficial { get; set; } = default!; + public string ProfilePictureIcon { get; set; } = default!; + /// /// 玩家服务器与等级简述 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index 093c38e8..b2d2dc9c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -212,6 +212,11 @@ internal static class HutaoEndpoints return $"{ApiSnapGenshinEnka}/{uid}"; } + public static string EnkaPlayerInfo(in PlayerUid uid) + { + return $"{ApiSnapGenshinEnka}/{uid}/info"; + } + public const string Ip = $"{ApiSnapGenshin}/ip"; #region Metadata @@ -292,4 +297,5 @@ internal static class HutaoEndpoints private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip"; private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka"; private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; + private const string EnkaHutao = "https://enka-api.hut.ao"; } \ No newline at end of file From 73825d391eb1ccd8baa6ed7bfcffe28c70ca8407 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sat, 15 Jun 2024 00:05:26 +0800 Subject: [PATCH 2/7] fix build --- .../Service/Metadata/MetadataServiceDictionaryExtension.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs index 3313145a..f1e4ca7c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs @@ -78,12 +78,12 @@ internal static class MetadataServiceDictionaryExtension public static ValueTask> GetAvatarIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.AvatarId, token); + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Avatar).Select(p => ((AvatarId)p.UnlockParameter, p)), token); } public static ValueTask> GetCostumeIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.CostumeId, token); + return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Costume).Select(p => ((CostumeId)p.UnlockParameter, p)), token); } public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default) From 16ac52e71d4afa356a919d2699f88d570c424e58 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 01:19:56 +0800 Subject: [PATCH 3/7] use list instead of mappppps --- ...aDictionaryAvatarIdProfilePictureSource.cs | 11 ---------- ...DictionaryCostumeIdProfilePictureSource.cs | 11 ---------- ...etadataDictionaryIdProfilePictureSource.cs | 11 ---------- .../IMetadataListProfilePictureSource.cs | 11 ++++++++++ .../MetadataServiceContextExtension.cs | 20 +++++------------ .../MetadataServiceDictionaryExtension.cs | 15 ------------- .../Metadata/MetadataServiceListExtension.cs | 5 +++++ .../Service/User/IUserMetadataContext.cs | 4 +--- .../Service/User/UserInitializationService.cs | 22 ++++++++++++++----- .../Service/User/UserMetadataContext.cs | 7 +----- .../Snap.Hutao/Web/Enka/EnkaClient.cs | 3 +-- .../Snap.Hutao/Web/HutaoEndpoints.cs | 1 - 12 files changed, 40 insertions(+), 81 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs deleted file mode 100644 index fe755b48..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryAvatarIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryAvatarIdProfilePictureSource -{ - public Dictionary AvatarIdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs deleted file mode 100644 index 655d2cd6..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryCostumeIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryCostumeIdProfilePictureSource -{ - public Dictionary CostumeIdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs deleted file mode 100644 index 66605c17..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataDictionaryIdProfilePictureSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Primitive; - -namespace Snap.Hutao.Service.Metadata.ContextAbstraction; - -internal interface IMetadataDictionaryIdProfilePictureSource -{ - public Dictionary IdProfilePictureMap { get; set; } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs new file mode 100644 index 00000000..34536889 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/IMetadataListProfilePictureSource.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Model.Metadata.Avatar; + +namespace Snap.Hutao.Service.Metadata.ContextAbstraction; + +internal interface IMetadataListProfilePictureSource +{ + public List ProfilePictures { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs index 53a5d504..11fe4b5b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/ContextAbstraction/MetadataServiceContextExtension.cs @@ -38,6 +38,11 @@ internal static class MetadataServiceContextExtension listMaterialSource.Materials = await metadataService.GetMaterialListAsync(token).ConfigureAwait(false); } + if (context is IMetadataListProfilePictureSource dictionaryIdProfilePictureSource) + { + dictionaryIdProfilePictureSource.ProfilePictures = await metadataService.GetProfilePictureListAsync(token).ConfigureAwait(false); + } + if (context is IMetadataListReliquaryMainAffixLevelSource listReliquaryMainAffixLevelSource) { listReliquaryMainAffixLevelSource.ReliquaryMainAffixLevels = await metadataService.GetReliquaryMainAffixLevelListAsync(token).ConfigureAwait(false); @@ -66,21 +71,6 @@ internal static class MetadataServiceContextExtension dictionaryIdMaterialSource.IdMaterialMap = await metadataService.GetIdToMaterialMapAsync(token).ConfigureAwait(false); } - if (context is IMetadataDictionaryIdProfilePictureSource dictionaryIdProfilePictureSource) - { - dictionaryIdProfilePictureSource.IdProfilePictureMap = await metadataService.GetIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - - if (context is IMetadataDictionaryAvatarIdProfilePictureSource dictionaryAvatarIdProfilePictureSource) - { - dictionaryAvatarIdProfilePictureSource.AvatarIdProfilePictureMap = await metadataService.GetAvatarIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - - if (context is IMetadataDictionaryCostumeIdProfilePictureSource dictionaryCostumeIdProfilePictureSource) - { - dictionaryCostumeIdProfilePictureSource.CostumeIdProfilePictureMap = await metadataService.GetCostumeIdToProfilePictureMapAsync(token).ConfigureAwait(false); - } - if (context is IMetadataDictionaryIdReliquarySource dictionaryIdReliquarySource) { dictionaryIdReliquarySource.IdReliquaryMap = await metadataService.GetIdToReliquaryMapAsync(token).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs index f1e4ca7c..364808a5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceDictionaryExtension.cs @@ -71,21 +71,6 @@ internal static class MetadataServiceDictionaryExtension return metadataService.FromCacheAsDictionaryAsync(FileNameMaterial, a => a.Id, token); } - public static ValueTask> GetIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, p => p.Id, token); - } - - public static ValueTask> GetAvatarIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Avatar).Select(p => ((AvatarId)p.UnlockParameter, p)), token); - } - - public static ValueTask> GetCostumeIdToProfilePictureMapAsync(this IMetadataService metadataService, CancellationToken token = default) - { - return metadataService.FromCacheAsDictionaryAsync(FileNameProfilePicture, (List list) => list.Where(p => p.UnlockType is ProfilePictureUnlockType.Costume).Select(p => ((CostumeId)p.UnlockParameter, p)), token); - } - public static ValueTask> GetIdToReliquaryMapAsync(this IMetadataService metadataService, CancellationToken token = default) { return metadataService.FromCacheAsDictionaryAsync(FileNameReliquary, (List list) => list.SelectMany(r => r.Ids, (r, i) => (Index: i, Reliquary: r)), token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs index 1532e245..720147cd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataServiceListExtension.cs @@ -70,6 +70,11 @@ internal static class MetadataServiceListExtension return metadataService.FromCacheOrFileAsync>(FileNameMonsterCurve, token); } + public static ValueTask> GetProfilePictureListAsync(this IMetadataService metadataService, CancellationToken token = default) + { + return metadataService.FromCacheOrFileAsync>(FileNameProfilePicture, token); + } + public static ValueTask> GetReliquaryListAsync(this IMetadataService metadataService, CancellationToken token = default) { return metadataService.FromCacheOrFileAsync>(FileNameReliquary, token); diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs index 4b64ae97..ac828068 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserMetadataContext.cs @@ -6,6 +6,4 @@ using Snap.Hutao.Service.Metadata.ContextAbstraction; namespace Snap.Hutao.Service.User; internal interface IUserMetadataContext : IMetadataContext, - IMetadataDictionaryIdProfilePictureSource, - IMetadataDictionaryAvatarIdProfilePictureSource, - IMetadataDictionaryCostumeIdProfilePictureSource; + IMetadataListProfilePictureSource; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index e0bbe03a..eb2fd95a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -4,6 +4,7 @@ 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; @@ -294,21 +295,30 @@ internal sealed partial class UserInitializationService : IUserInitializationSer .ConfigureAwait(false); } - if (context.IdProfilePictureMap.TryGetValue(profilePicture.ProfilePictureId, out MetadataProfilePicture? metadataProfilePicture)) + if (profilePicture.ProfilePictureId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.Id == profilePicture.ProfilePictureId) + .Icon; + return true; } - if (context.CostumeIdProfilePictureMap.TryGetValue(profilePicture.CostumeId, out metadataProfilePicture)) + if (profilePicture.CostumeId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Costume && p.UnlockParameter == profilePicture.CostumeId) + .Icon; + return true; } - if (context.AvatarIdProfilePictureMap.TryGetValue(profilePicture.AvatarId, out metadataProfilePicture)) + if (profilePicture.AvatarId is not 0U) { - userGameRole.ProfilePictureIcon = metadataProfilePicture.Icon; + userGameRole.ProfilePictureIcon = context.ProfilePictures + .Single(p => p.UnlockType is ProfilePictureUnlockType.Avatar && p.UnlockParameter == profilePicture.AvatarId) + .Icon; + return true; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs index 03b60b6e..977f5547 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserMetadataContext.cs @@ -2,15 +2,10 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Metadata.Avatar; -using Snap.Hutao.Model.Primitive; namespace Snap.Hutao.Service.User; internal class UserMetadataContext : IUserMetadataContext { - public Dictionary IdProfilePictureMap { get; set; } = default!; - - public Dictionary AvatarIdProfilePictureMap { get; set; } = default!; - - public Dictionary CostumeIdProfilePictureMap { get; set; } = default!; + public List ProfilePictures { get; set; } = default!; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs index 77e8814b..f4fdbcc1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -29,8 +29,7 @@ internal sealed partial class EnkaClient public ValueTask GetForwardPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) { - // TODO - return TryGetEnkaResponseCoreAsync($"https://enka-api.hut.ao/{playerUid}?info", token); + return TryGetEnkaResponseCoreAsync(HutaoEndpoints.EnkaPlayerInfo(playerUid), token); } public ValueTask GetPlayerInfoAsync(in PlayerUid playerUid, CancellationToken token = default) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index b2d2dc9c..c314c07d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -297,5 +297,4 @@ internal static class HutaoEndpoints private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip"; private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka"; private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; - private const string EnkaHutao = "https://enka-api.hut.ao"; } \ No newline at end of file From d05c196b7cc71cd4ebdddf73be0c51df435d9b76 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 19:04:51 +0800 Subject: [PATCH 4/7] apply changes --- ...40616104646_UidProfilePicture.Designer.cs} | 58 +++++++++---------- ...cs => 20240616104646_UidProfilePicture.cs} | 11 ++-- .../Migrations/AppDbContextModelSnapshot.cs | 54 ++++++++--------- .../Model/Entity/Database/AppDbContext.cs | 4 +- ...ProfilePicture.cs => UidProfilePicture.cs} | 10 ++-- .../User/IUidProfilePictureDbService.cs | 16 +++++ .../Service/User/IUserGameRoleDbService.cs | 18 ------ .../User/IUserInitializationService.cs | 2 +- .../Snap.Hutao/Service/User/IUserService.cs | 2 +- .../User/UidProfilePictureDbService.cs | 32 ++++++++++ .../Service/User/UserGameRoleDbService.cs | 37 ------------ .../Service/User/UserInitializationService.cs | 26 ++++----- .../Snap.Hutao/Service/User/UserService.cs | 4 +- .../ViewModel/User/UserViewModel.cs | 2 +- 14 files changed, 133 insertions(+), 143 deletions(-) rename src/Snap.Hutao/Snap.Hutao/Migrations/{20240613144942_UserGameRoleProfilePicture.Designer.cs => 20240616104646_UidProfilePicture.Designer.cs} (98%) rename src/Snap.Hutao/Snap.Hutao/Migrations/{20240613144942_UserGameRoleProfilePicture.cs => 20240616104646_UidProfilePicture.cs} (75%) rename src/Snap.Hutao/Snap.Hutao/Model/Entity/{UserGameRoleProfilePicture.cs => UidProfilePicture.cs} (71%) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUidProfilePictureDbService.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs similarity index 98% rename from src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs rename to src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs index 3e6db24b..26aa2963 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.Designer.cs @@ -11,8 +11,8 @@ using Snap.Hutao.Model.Entity.Database; namespace Snap.Hutao.Migrations { [DbContext(typeof(AppDbContext))] - [Migration("20240613144942_UserGameRoleProfilePicture")] - partial class UserGameRoleProfilePicture + [Migration("20240616104646_UidProfilePicture")] + partial class UidProfilePicture { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -469,6 +469,33 @@ namespace Snap.Hutao.Migrations b.ToTable("spiral_abysses"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("uid_profile_pictures"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => { b.Property("InnerId") @@ -518,33 +545,6 @@ namespace Snap.Hutao.Migrations b.ToTable("users"); }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => - { - b.Property("InnerId") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AvatarId") - .HasColumnType("INTEGER"); - - b.Property("CostumeId") - .HasColumnType("INTEGER"); - - b.Property("LastUpdateTime") - .HasColumnType("TEXT"); - - b.Property("ProfilePictureId") - .HasColumnType("INTEGER"); - - b.Property("Uid") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("InnerId"); - - b.ToTable("profile_pictures"); - }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs similarity index 75% rename from src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs rename to src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs index 84e5271f..0c323d59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/20240613144942_UserGameRoleProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20240616104646_UidProfilePicture.cs @@ -1,4 +1,5 @@ // +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -6,13 +7,13 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Snap.Hutao.Migrations { /// - public partial class UserGameRoleProfilePicture : Migration + public partial class UidProfilePicture : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "profile_pictures", + name: "uid_profile_pictures", columns: table => new { InnerId = table.Column(type: "TEXT", nullable: false), @@ -20,11 +21,11 @@ namespace Snap.Hutao.Migrations ProfilePictureId = table.Column(type: "INTEGER", nullable: false), AvatarId = table.Column(type: "INTEGER", nullable: false), CostumeId = table.Column(type: "INTEGER", nullable: false), - LastUpdateTime = table.Column(type: "TEXT", nullable: false) + RefreshTime = table.Column(type: "TEXT", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_profile_pictures", x => x.InnerId); + table.PrimaryKey("PK_uid_profile_pictures", x => x.InnerId); }); } @@ -32,7 +33,7 @@ namespace Snap.Hutao.Migrations protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "profile_pictures"); + name: "uid_profile_pictures"); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index 3be71058..03413381 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -466,6 +466,33 @@ namespace Snap.Hutao.Migrations b.ToTable("spiral_abysses"); }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.UidProfilePicture", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CostumeId") + .HasColumnType("INTEGER"); + + b.Property("ProfilePictureId") + .HasColumnType("INTEGER"); + + b.Property("RefreshTime") + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("uid_profile_pictures"); + }); + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => { b.Property("InnerId") @@ -515,33 +542,6 @@ namespace Snap.Hutao.Migrations b.ToTable("users"); }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.UserGameRoleProfilePicture", b => - { - b.Property("InnerId") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AvatarId") - .HasColumnType("INTEGER"); - - b.Property("CostumeId") - .HasColumnType("INTEGER"); - - b.Property("LastUpdateTime") - .HasColumnType("TEXT"); - - b.Property("ProfilePictureId") - .HasColumnType("INTEGER"); - - b.Property("Uid") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("InnerId"); - - b.ToTable("profile_pictures"); - }); - modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index 8e6f5a16..47cfeb54 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -25,7 +25,7 @@ internal sealed class AppDbContext : DbContext public AppDbContext(DbContextOptions options) : base(options) { - logger = this.GetService>(); + //logger = this.GetService>(); logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); } @@ -65,7 +65,7 @@ internal sealed class AppDbContext : DbContext public DbSet SpiralAbysses { get; set; } = default!; - public DbSet UserGameRoleProfilePictures { get; set; } = default!; + public DbSet UidProfilePictures { get; set; } = default!; public static AppDbContext Create(IServiceProvider serviceProvider, string sqlConnectionString) { diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs similarity index 71% rename from src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs rename to src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs index 21bc9372..729df91c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UserGameRoleProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs @@ -9,9 +9,9 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Snap.Hutao.Model.Entity; -[Table("profile_pictures")] +[Table("uid_profile_pictures")] [SuppressMessage("", "SH002")] -internal sealed class UserGameRoleProfilePicture : IMappingFrom +internal sealed class UidProfilePicture : IMappingFrom { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -25,9 +25,9 @@ internal sealed class UserGameRoleProfilePicture : IMappingFrom +{ + ValueTask SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default); + + ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default); + + ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default); +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs deleted file mode 100644 index 217af9a2..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserGameRoleDbService.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Service.Abstraction; - -namespace Snap.Hutao.Service.User; - -internal interface IUserGameRoleDbService : IAppDbService -{ - ValueTask ContainsUidAsync(string uid, CancellationToken token = default); - - ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); - - ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default); - - ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default); -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs index 5509609e..edbc98af 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs @@ -11,5 +11,5 @@ internal interface IUserInitializationService ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); + ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index 08e1fd87..9a11eb7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -54,5 +54,5 @@ internal interface IUserService /// 任务 ValueTask RemoveUserAsync(BindingUser user); - ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole); + ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs new file mode 100644 index 00000000..5622be01 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs @@ -0,0 +1,32 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.EntityFrameworkCore; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Service.Abstraction; + +namespace Snap.Hutao.Service.User; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IUidProfilePictureDbService))] +internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbService +{ + private readonly IServiceProvider serviceProvider; + + public IServiceProvider ServiceProvider { get => serviceProvider; } + + public ValueTask SingleUidProfilePictureOrDefaultByUidAsync(string uid, CancellationToken token = default) + { + return this.QueryAsync(query => query.SingleOrDefaultAsync(n => n.Uid == uid)); + } + + public async ValueTask UpdateUidProfilePictureAsync(UidProfilePicture profilePicture, CancellationToken token = default) + { + await this.UpdateAsync(profilePicture, token).ConfigureAwait(false); + } + + public async ValueTask DeleteUidProfilePictureByUidAsync(string uid, CancellationToken token = default) + { + await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs deleted file mode 100644 index 09ef97f4..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserGameRoleDbService.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.EntityFrameworkCore; -using Snap.Hutao.Model.Entity; -using Snap.Hutao.Service.Abstraction; - -namespace Snap.Hutao.Service.User; - -[ConstructorGenerated] -[Injection(InjectAs.Singleton, typeof(IUserGameRoleDbService))] -internal sealed partial class UserGameRoleDbService : IUserGameRoleDbService -{ - private readonly IServiceProvider serviceProvider; - - public IServiceProvider ServiceProvider { get => serviceProvider; } - - public ValueTask ContainsUidAsync(string uid, CancellationToken token = default) - { - return this.QueryAsync(query => query.AnyAsync(n => n.Uid == uid)); - } - - public ValueTask GetUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) - { - return this.QueryAsync(query => query.FirstAsync(n => n.Uid == uid)); - } - - public async ValueTask UpdateUserGameRoleProfilePictureAsync(UserGameRoleProfilePicture profilePicture, CancellationToken token = default) - { - await this.UpdateAsync(profilePicture, token).ConfigureAwait(false); - } - - public async ValueTask DeleteUserGameRoleProfilePictureByUidAsync(string uid, CancellationToken token = default) - { - await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); - } -} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index eb2fd95a..e2868bf1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -23,7 +23,7 @@ namespace Snap.Hutao.Service.User; internal sealed partial class UserInitializationService : IUserInitializationService { private readonly IUserFingerprintService userFingerprintService; - private readonly IUserGameRoleDbService userGameRoleDbService; + private readonly IUidProfilePictureDbService uidProfilePictureDbService; private readonly IServiceProvider serviceProvider; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) @@ -63,7 +63,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - public async ValueTask RefreshUserGameRolesProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) + public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) { EnkaResponse? enkaResponse; using (IServiceScope scope = serviceProvider.CreateScope()) @@ -78,12 +78,12 @@ internal sealed partial class UserInitializationService : IUserInitializationSer if (enkaResponse is { PlayerInfo: { } playerInfo }) { - UserGameRoleProfilePicture profilePicture = UserGameRoleProfilePicture.From(userGameRole, playerInfo.ProfilePicture); + UidProfilePicture profilePicture = UidProfilePicture.From(userGameRole, playerInfo.ProfilePicture); - await userGameRoleDbService.DeleteUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); - await userGameRoleDbService.UpdateUserGameRoleProfilePictureAsync(profilePicture, token).ConfigureAwait(false); + await uidProfilePictureDbService.DeleteUidProfilePictureByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false); + await uidProfilePictureDbService.UpdateUidProfilePictureAsync(profilePicture, token).ConfigureAwait(false); - await SetUserGameRolesProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); + await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false); } } @@ -255,25 +255,21 @@ internal sealed partial class UserInitializationService : IUserInitializationSer { foreach (UserGameRole userGameRole in user.UserGameRoles) { - if (await userGameRoleDbService.ContainsUidAsync(userGameRole.GameUid, token).ConfigureAwait(false)) + if (await uidProfilePictureDbService.SingleUidProfilePictureOrDefaultByUidAsync(userGameRole.GameUid, token).ConfigureAwait(false) is { } profilePicture) { - UserGameRoleProfilePicture savedProfilePicture = await userGameRoleDbService - .GetUserGameRoleProfilePictureByUidAsync(userGameRole.GameUid, token) - .ConfigureAwait(false); - - if (await SetUserGameRolesProfilePictureCoreAsync(userGameRole, savedProfilePicture, token).ConfigureAwait(false)) + if (await SetUserGameRoleProfilePictureCoreAsync(userGameRole, profilePicture, token).ConfigureAwait(false)) { continue; } } - await RefreshUserGameRolesProfilePictureAsync(userGameRole, token).ConfigureAwait(false); + await RefreshUidProfilePictureAsync(userGameRole, token).ConfigureAwait(false); } } - private async ValueTask SetUserGameRolesProfilePictureCoreAsync(UserGameRole userGameRole, UserGameRoleProfilePicture profilePicture, CancellationToken token = default) + private async ValueTask SetUserGameRoleProfilePictureCoreAsync(UserGameRole userGameRole, UidProfilePicture profilePicture, CancellationToken token = default) { - if (profilePicture.LastUpdateTime.AddDays(15) < DateTimeOffset.Now) + if (profilePicture.RefreshTime.AddDays(15) < DateTimeOffset.Now) { return false; } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 6766ad1e..4daf87ae 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -123,8 +123,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe return true; } - public async ValueTask RefreshUserGameRoleProfilePictureAsync(UserGameRole userGameRole) + public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole) { - await userInitializationService.RefreshUserGameRolesProfilePictureAsync(userGameRole).ConfigureAwait(false); + await userInitializationService.RefreshUidProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index fc8d7b59..33ee5655 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -302,6 +302,6 @@ internal sealed partial class UserViewModel : ObservableObject [Command("RefreshUserGameRoleProfilePictureCommand")] private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) { - userService.RefreshUserGameRoleProfilePictureAsync(userGameRole).SafeForget(); + userService.RefreshUidProfilePictureAsync(userGameRole).SafeForget(); } } \ No newline at end of file From 558551c8ad46f9a7a8d703426011446de3f9b8f3 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 19:14:33 +0800 Subject: [PATCH 5/7] rename --- .../Snap.Hutao/Model/Entity/Database/AppDbContext.cs | 2 +- .../Snap.Hutao/Service/User/IUserInitializationService.cs | 2 +- src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs | 2 +- .../Snap.Hutao/Service/User/UserInitializationService.cs | 5 ++--- src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs | 4 ++-- src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 2 +- src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs | 6 +++--- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs index 47cfeb54..44be285d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Database/AppDbContext.cs @@ -25,7 +25,7 @@ internal sealed class AppDbContext : DbContext public AppDbContext(DbContextOptions options) : base(options) { - //logger = this.GetService>(); + logger = this.GetService>(); logger?.LogColorizedInformation("{Name}[{Id}] {Action}", nameof(AppDbContext), (ContextId, ConsoleColor.DarkCyan), ("created", ConsoleColor.Green)); } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs index edbc98af..a4ff1728 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs @@ -11,5 +11,5 @@ internal interface IUserInitializationService ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); + ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index 9a11eb7e..8c9fe4d9 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -54,5 +54,5 @@ internal interface IUserService /// 任务 ValueTask RemoveUserAsync(BindingUser user); - ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole); + ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index e2868bf1..a7922055 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -14,7 +14,6 @@ using Snap.Hutao.Web.Hoyolab.Bbs.User; using Snap.Hutao.Web.Hoyolab.Passport; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; -using MetadataProfilePicture = Snap.Hutao.Model.Metadata.Avatar.ProfilePicture; namespace Snap.Hutao.Service.User; @@ -63,7 +62,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) + public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default) { EnkaResponse? enkaResponse; using (IServiceScope scope = serviceProvider.CreateScope()) @@ -263,7 +262,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer } } - await RefreshUidProfilePictureAsync(userGameRole, token).ConfigureAwait(false); + await RefreshProfilePictureAsync(userGameRole, token).ConfigureAwait(false); } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 4daf87ae..116342dc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -123,8 +123,8 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe return true; } - public async ValueTask RefreshUidProfilePictureAsync(UserGameRole userGameRole) + public async ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole) { - await userInitializationService.RefreshUidProfilePictureAsync(userGameRole).ConfigureAwait(false); + await userInitializationService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index 022e4bfe..c3fb9c27 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -40,7 +40,7 @@ Background="Transparent" BorderBrush="Transparent" BorderThickness="0" - Command="{Binding DataContext.RefreshUserGameRoleProfilePictureCommand, Source={StaticResource ViewModelBindingProxy}}" + Command="{Binding DataContext.RefreshProfilePictureCommand, Source={StaticResource ViewModelBindingProxy}}" CommandParameter="{Binding}" CornerRadius="8"> diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 33ee5655..3c6a3d42 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -299,9 +299,9 @@ internal sealed partial class UserViewModel : ObservableObject infoBarService.Warning(message); } - [Command("RefreshUserGameRoleProfilePictureCommand")] - private void RefreshUserGameRoleProfilePicture(UserGameRole userGameRole) + [Command("RefreshProfilePictureCommand")] + private void RefreshProfilePicture(UserGameRole userGameRole) { - userService.RefreshUidProfilePictureAsync(userGameRole).SafeForget(); + userService.RefreshProfilePictureAsync(userGameRole).SafeForget(); } } \ No newline at end of file From 95d64c289510c699bef93a2b05bed4ba173d2521 Mon Sep 17 00:00:00 2001 From: qhy040404 Date: Sun, 16 Jun 2024 20:41:19 +0800 Subject: [PATCH 6/7] make UserGameRole observable --- .../Service/User/UserInitializationService.cs | 8 +++++--- .../Snap.Hutao/ViewModel/User/UserViewModel.cs | 4 ++-- .../Web/Hoyolab/Takumi/Binding/UserGameRole.cs | 16 +++++++++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index a7922055..85e31f00 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -24,6 +24,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer private readonly IUserFingerprintService userFingerprintService; private readonly IUidProfilePictureDbService uidProfilePictureDbService; private readonly IServiceProvider serviceProvider; + private readonly ITaskContext taskContext; public async ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default) { @@ -70,9 +71,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer EnkaClient enkaClient = scope.ServiceProvider .GetRequiredService(); - enkaResponse = await enkaClient - .GetForwardPlayerInfoAsync(userGameRole, token) - .ConfigureAwait(false); + enkaResponse = await enkaClient.GetForwardPlayerInfoAsync(userGameRole, token).ConfigureAwait(false) + ?? await enkaClient.GetPlayerInfoAsync(userGameRole, token).ConfigureAwait(false); } if (enkaResponse is { PlayerInfo: { } playerInfo }) @@ -290,6 +290,8 @@ internal sealed partial class UserInitializationService : IUserInitializationSer .ConfigureAwait(false); } + await taskContext.SwitchToMainThreadAsync(); + if (profilePicture.ProfilePictureId is not 0U) { userGameRole.ProfilePictureIcon = context.ProfilePictures diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs index 3c6a3d42..2e572c3a 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs @@ -300,8 +300,8 @@ internal sealed partial class UserViewModel : ObservableObject } [Command("RefreshProfilePictureCommand")] - private void RefreshProfilePicture(UserGameRole userGameRole) + private async Task RefreshProfilePictureAsync(UserGameRole userGameRole) { - userService.RefreshProfilePictureAsync(userGameRole).SafeForget(); + await userService.RefreshProfilePictureAsync(userGameRole).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs index 1ad7a81b..748957bb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/UserGameRole.cs @@ -1,14 +1,19 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using CommunityToolkit.Mvvm.ComponentModel; +using Snap.Hutao.Model.Metadata.Converter; + namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding; /// /// 用户游戏角色 /// [HighQuality] -internal sealed class UserGameRole +internal sealed class UserGameRole : ObservableObject { + private string? profilePictureIcon; + /// /// hk4e_cn for Genshin Impact /// @@ -57,8 +62,6 @@ internal sealed class UserGameRole [JsonPropertyName("is_official")] public bool IsOfficial { get; set; } = default!; - public string ProfilePictureIcon { get; set; } = default!; - /// /// 玩家服务器与等级简述 /// @@ -67,6 +70,13 @@ internal sealed class UserGameRole get => $"{RegionName} | Lv.{Level}"; } + [JsonIgnore] + public string? ProfilePictureIcon + { + get => profilePictureIcon; + set => SetProperty(ref profilePictureIcon, value); + } + public static implicit operator PlayerUid(UserGameRole userGameRole) { return new PlayerUid(userGameRole.GameUid, userGameRole.Region); From ff9b553a19be8e17361ac7c30346bb3f9d226aea Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sun, 16 Jun 2024 22:46:24 +0800 Subject: [PATCH 7/7] code style --- .../Control/Theme/CornerRadius.xaml | 1 + .../Model/Entity/UidProfilePicture.cs | 4 +- .../Service/User/IProfilePictureService.cs | 14 +++ .../User/IUserInitializationService.cs | 2 - .../Service/User/ProfilePictureService.cs | 109 ++++++++++++++++++ .../User/UidProfilePictureDbService.cs | 2 +- .../Service/User/UserInitializationService.cs | 107 +---------------- .../Snap.Hutao/Service/User/UserService.cs | 4 +- .../Snap.Hutao/View/Guide/GuideView.xaml | 2 +- src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 5 +- .../ViewModel/Guide/StaticResource.cs | 2 + .../ViewModel/User/UserViewModel.cs | 6 - .../Snap.Hutao/Web/Enka/EnkaClient.cs | 20 +++- .../Web/Enka/Model/ProfilePicture.cs | 2 +- .../Hoyolab/Takumi/Binding/UserGameRole.cs | 15 ++- 15 files changed, 167 insertions(+), 128 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/IProfilePictureService.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml index 4dcfd7d4..741bf85e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Theme/CornerRadius.xaml @@ -2,4 +2,5 @@ 4,4,0,0 0,0,4,4 0,4,0,4 + 16 diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs index 729df91c..c19bcb59 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/UidProfilePicture.cs @@ -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 { [Key] @@ -27,12 +26,13 @@ internal sealed class UidProfilePicture : IMappingFrom CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default(CancellationToken)); ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken)); - - ValueTask RefreshProfilePictureAsync(UserGameRole userGameRole, CancellationToken token = default); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs new file mode 100644 index 00000000..2d6438ef --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/ProfilePictureService.cs @@ -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(); + + 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 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(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; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs index 5622be01..da6887f2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UidProfilePictureDbService.cs @@ -29,4 +29,4 @@ internal sealed partial class UidProfilePictureDbService : IUidProfilePictureDbS { await this.DeleteAsync(profilePicture => profilePicture.Uid == uid, token).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs index 85e31f00..a310752c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs @@ -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 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(); - - 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 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 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(); - - if (!await metadataService.InitializeAsync().ConfigureAwait(false)) - { - return false; - } - - context = await scope.ServiceProvider - .GetRequiredService() - .GetContextAsync(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; - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 116342dc..eaf87c88 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -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); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml b/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml index b6e79770..8f28ee39 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Guide/GuideView.xaml @@ -301,7 +301,7 @@