diff --git a/DragaliaAPI/DragaliaAPI.Database/DragaliaAPI.Database.csproj b/DragaliaAPI/DragaliaAPI.Database/DragaliaAPI.Database.csproj index ffdbf81db..417b1933e 100644 --- a/DragaliaAPI/DragaliaAPI.Database/DragaliaAPI.Database.csproj +++ b/DragaliaAPI/DragaliaAPI.Database/DragaliaAPI.Database.csproj @@ -1,10 +1,6 @@  - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - all @@ -14,14 +10,6 @@ all build; native; contentfiles; analyzers; buildtransitive - - all - build; native; contentfiles; analyzers; buildtransitive - - - all - build; native; contentfiles; analyzers; buildtransitive - diff --git a/DragaliaAPI/DragaliaAPI.Database/Entities/DbNewsItem.cs b/DragaliaAPI/DragaliaAPI.Database/Entities/DbNewsItem.cs index 663f64cd7..0038f5398 100644 --- a/DragaliaAPI/DragaliaAPI.Database/Entities/DbNewsItem.cs +++ b/DragaliaAPI/DragaliaAPI.Database/Entities/DbNewsItem.cs @@ -39,18 +39,22 @@ public class DbNewsItem /// /// Gets or sets a relative path to the summary header image. /// - /// - /// Will be combined with the configured CDN base address to deliver a fully qualified URL in the response. - /// public string? HeaderImagePath { get; set; } + /// + /// Gets or sets the alt text for the summary header image. + /// + public string? HeaderImageAltText { get; set; } + /// /// Gets or sets a relative path to the body image. /// - /// - /// Will be combined with the configured CDN base address to deliver a fully qualified URL in the response. - /// public string? BodyImagePath { get; set; } + + /// + /// Gets or sets the alt text for the body image. + /// + public string? BodyImageAltText { get; set; } } public class DbNewsItemConfiguration : IEntityTypeConfiguration @@ -60,7 +64,9 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Headline).HasMaxLength(256); builder.Property(x => x.Description).HasMaxLength(4096); builder.Property(x => x.HeaderImagePath).HasMaxLength(512); + builder.Property(x => x.HeaderImageAltText).HasMaxLength(1024); builder.Property(x => x.BodyImagePath).HasMaxLength(512); + builder.Property(x => x.BodyImageAltText).HasMaxLength(1024); builder.HasData( new DbNewsItem() @@ -71,6 +77,7 @@ public void Configure(EntityTypeBuilder builder) Description = "The below infographic shows the endeavour rewards available for the progressing the Mercurial Gauntlet.", BodyImagePath = "/dawnshard/news/mg-endeavours.webp", + BodyImageAltText = "Mercurial Gauntlet rewards infographic", Date = new DateTimeOffset(2024, 06, 02, 16, 07, 00, TimeSpan.FromHours(1)), } ); diff --git a/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.Designer.cs b/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.Designer.cs new file mode 100644 index 000000000..88499773e --- /dev/null +++ b/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.Designer.cs @@ -0,0 +1,2693 @@ +// +using System; +using DragaliaAPI.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DragaliaAPI.Database.Migrations +{ + [DbContext(typeof(ApiContext))] + [Migration("20241009195337_news_refactor")] + partial class news_refactor + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbAbilityCrest", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("AbilityCrestId") + .HasColumnType("integer"); + + b.Property("AttackPlusCount") + .HasColumnType("integer"); + + b.Property("BuildupCount") + .HasColumnType("integer"); + + b.Property("EquipableCount") + .HasColumnType("integer"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("HpPlusCount") + .HasColumnType("integer"); + + b.Property("IsFavorite") + .HasColumnType("boolean"); + + b.Property("IsNew") + .HasColumnType("boolean"); + + b.Property("LimitBreakCount") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "AbilityCrestId"); + + b.ToTable("PlayerAbilityCrests"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbAbilityCrestSet", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("AbilityCrestSetNo") + .HasColumnType("integer"); + + b.Property("AbilityCrestSetName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("CrestSlotType1CrestId1") + .HasColumnType("integer"); + + b.Property("CrestSlotType1CrestId2") + .HasColumnType("integer"); + + b.Property("CrestSlotType1CrestId3") + .HasColumnType("integer"); + + b.Property("CrestSlotType2CrestId1") + .HasColumnType("integer"); + + b.Property("CrestSlotType2CrestId2") + .HasColumnType("integer"); + + b.Property("CrestSlotType3CrestId1") + .HasColumnType("integer"); + + b.Property("CrestSlotType3CrestId2") + .HasColumnType("integer"); + + b.Property("TalismanKeyId") + .HasColumnType("numeric(20,0)"); + + b.HasKey("ViewerId", "AbilityCrestSetNo"); + + b.ToTable("PlayerAbilityCrestSets"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbCompletedDailyMission", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Id") + .HasColumnType("integer"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Progress") + .HasColumnType("integer"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ViewerId", "Id", "Date"); + + b.ToTable("CompletedDailyMissions"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbDeviceAccount", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("HashedPassword") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DeviceAccounts"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbEmblem", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("EmblemId") + .HasColumnType("integer") + .HasColumnName("EmblemId"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("GetTime"); + + b.Property("IsNew") + .HasColumnType("boolean") + .HasColumnName("IsNew"); + + b.HasKey("ViewerId", "EmblemId"); + + b.ToTable("Emblems"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbEquippedStamp", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Slot") + .HasColumnType("integer"); + + b.Property("StampId") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "Slot"); + + b.ToTable("EquippedStamps"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbFortBuild", b => + { + b.Property("BuildId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BuildId")); + + b.Property("BuildEndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("BuildStartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsNew") + .HasColumnType("boolean"); + + b.Property("LastIncomeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("PlantId") + .HasColumnType("integer"); + + b.Property("PositionX") + .HasColumnType("integer"); + + b.Property("PositionZ") + .HasColumnType("integer"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("BuildId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerFortBuilds"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbFortDetail", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CarpenterNum") + .HasColumnType("integer") + .HasColumnName("CarpenterNum"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerFortDetail"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbLoginBonus", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Id") + .HasColumnType("integer"); + + b.Property("CurrentDay") + .HasColumnType("integer"); + + b.Property("IsComplete") + .HasColumnType("boolean"); + + b.HasKey("ViewerId", "Id"); + + b.ToTable("LoginBonuses"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbNewsItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BodyImageAltText") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("BodyImagePath") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)"); + + b.Property("HeaderImageAltText") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("HeaderImagePath") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Headline") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Hidden") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("NewsItems"); + + b.HasData( + new + { + Id = 20000, + BodyImageAltText = "Mercurial Gauntlet rewards infographic", + BodyImagePath = "/dawnshard/news/mg-endeavours.webp", + Date = new DateTimeOffset(new DateTime(2024, 6, 2, 16, 7, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 1, 0, 0, 0)), + Description = "The below infographic shows the endeavour rewards available for the progressing the Mercurial Gauntlet.", + Headline = "Mercurial Gauntlet Endeavour Rewards", + Hidden = true + }); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbParty", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("PartyNo") + .HasColumnType("integer"); + + b.Property("PartyName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("ViewerId", "PartyNo"); + + b.ToTable("PartyData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPartyPower", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("MaxPartyPower") + .HasColumnType("integer") + .HasColumnName("MaxPartyPower"); + + b.HasKey("ViewerId"); + + b.ToTable("PartyPowers"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPartyUnit", b => + { + b.Property("Id") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill1CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill2CharaId") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId3") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId2") + .HasColumnType("integer"); + + b.Property("EquipDragonKeyId") + .HasColumnType("bigint"); + + b.Property("EquipTalismanKeyId") + .HasColumnType("bigint"); + + b.Property("EquipWeaponBodyId") + .HasColumnType("integer"); + + b.Property("EquipWeaponSkinId") + .HasColumnType("integer"); + + b.Property("PartyNo") + .HasColumnType("integer"); + + b.Property("UnitNo") + .HasColumnType("integer"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ViewerId", "PartyNo"); + + b.ToTable("PlayerPartyUnits"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayer", b => + { + b.Property("ViewerId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ViewerId")); + + b.Property("AccountId") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("SavefileVersion") + .HasColumnType("integer"); + + b.HasKey("ViewerId"); + + b.HasIndex("AccountId"); + + b.ToTable("Players"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerBannerData", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("SummonBannerId") + .HasColumnType("integer") + .HasColumnName("SummonBannerId"); + + b.Property("ConsecutionSummonPoints") + .HasColumnType("integer") + .HasColumnName("CsSummonPoints"); + + b.Property("ConsecutionSummonPointsMaxDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("CsSummonPointsMaxDate"); + + b.Property("ConsecutionSummonPointsMinDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("CsSummonPointsMinDate"); + + b.Property("DailyLimitedSummonCount") + .HasColumnType("integer") + .HasColumnName("DailyLimitedSummons"); + + b.Property("IsBeginnerFreeSummonAvailable") + .HasColumnType("integer") + .HasColumnName("BeginnerSummonAvailable"); + + b.Property("IsConsecutionFreeSummonAvailable") + .HasColumnType("integer") + .HasColumnName("CsSummonAvailable"); + + b.Property("IsFreeSummonAvailable") + .HasColumnType("integer") + .HasColumnName("FreeSummonAvailable"); + + b.Property("SummonCount") + .HasColumnType("integer") + .HasColumnName("SummonCount"); + + b.Property("SummonCountSinceLastFiveStar") + .HasColumnType("integer"); + + b.Property("SummonPoints") + .HasColumnType("integer") + .HasColumnName("SummonPoints"); + + b.HasKey("ViewerId", "SummonBannerId"); + + b.ToTable("PlayerBannerData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerCharaData", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CharaId") + .HasColumnType("integer") + .HasColumnName("CharaId"); + + b.Property("Ability1Level") + .HasColumnType("smallint") + .HasColumnName("Abil1Lvl"); + + b.Property("Ability2Level") + .HasColumnType("smallint") + .HasColumnName("Abil2Lvl"); + + b.Property("Ability3Level") + .HasColumnType("smallint") + .HasColumnName("Abil3Lvl"); + + b.Property("AttackBase") + .HasColumnType("integer") + .HasColumnName("AtkBase"); + + b.Property("AttackNode") + .HasColumnType("integer") + .HasColumnName("AtkNode"); + + b.Property("AttackPlusCount") + .HasColumnType("smallint") + .HasColumnName("AtkPlusCount"); + + b.Property("BurstAttackLevel") + .HasColumnType("smallint") + .HasColumnName("BurstAtkLvl"); + + b.Property("ComboBuildupCount") + .HasColumnType("integer") + .HasColumnName("ComboBuildupCount"); + + b.Property("ExAbility2Level") + .HasColumnType("smallint") + .HasColumnName("ExAbility2Lvl"); + + b.Property("ExAbilityLevel") + .HasColumnType("smallint") + .HasColumnName("ExAbility1Lvl"); + + b.Property("Exp") + .HasColumnType("integer") + .HasColumnName("Exp"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("GetTime"); + + b.Property("HpBase") + .HasColumnType("integer") + .HasColumnName("HpBase"); + + b.Property("HpNode") + .HasColumnType("integer") + .HasColumnName("HpNode"); + + b.Property("HpPlusCount") + .HasColumnType("smallint") + .HasColumnName("HpPlusCount"); + + b.Property("IsNew") + .HasColumnType("boolean") + .HasColumnName("IsNew"); + + b.Property("IsTemporary") + .HasColumnType("boolean") + .HasColumnName("IsTemp"); + + b.Property("IsUnlockEditSkill") + .HasColumnType("boolean") + .HasColumnName("IsUnlockEditSkill"); + + b.Property("Level") + .HasColumnType("smallint") + .HasColumnName("Level"); + + b.Property("ListViewFlag") + .HasColumnType("boolean") + .HasColumnName("ListViewFlag"); + + b.Property("ManaNodeUnlockCount") + .HasColumnType("integer") + .HasColumnName("ManaNodeUnlockCount"); + + b.Property("Rarity") + .HasColumnType("smallint") + .HasColumnName("Rarity"); + + b.Property("Skill1Level") + .HasColumnType("smallint") + .HasColumnName("Skill1Lvl"); + + b.Property("Skill2Level") + .HasColumnType("smallint") + .HasColumnName("Skill2Lvl"); + + b.HasKey("ViewerId", "CharaId"); + + b.ToTable("PlayerCharaData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDiamondData", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("FreeDiamond") + .HasColumnType("integer"); + + b.Property("PaidDiamond") + .HasColumnType("integer"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerDiamondData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeChara", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CharaId") + .HasColumnType("integer") + .HasColumnName("CharaId"); + + b.Property("MaxFloor") + .HasColumnType("integer") + .HasColumnName("MaxFloor"); + + b.Property("MaxScore") + .HasColumnType("integer") + .HasColumnName("MaxScore"); + + b.Property("SelectEditSkillCharaId1") + .HasColumnType("integer") + .HasColumnName("SelectEditSkillCharaId1"); + + b.Property("SelectEditSkillCharaId2") + .HasColumnType("integer") + .HasColumnName("SelectEditSkillCharaId2"); + + b.Property("SelectEditSkillCharaId3") + .HasColumnType("integer") + .HasColumnName("SelectEditSkillCharaId3"); + + b.Property("SelectedServitorId") + .HasColumnType("integer") + .HasColumnName("SelectedServitorId"); + + b.HasKey("ViewerId", "CharaId"); + + b.ToTable("PlayerDmodeCharas"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeDungeon", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CharaId") + .HasColumnType("integer") + .HasColumnName("CharaId"); + + b.Property("DungeonScore") + .HasColumnType("integer") + .HasColumnName("DungeonScore"); + + b.Property("Floor") + .HasColumnType("integer") + .HasColumnName("Floor"); + + b.Property("IsPlayEnd") + .HasColumnType("boolean") + .HasColumnName("IsPlayEnd"); + + b.Property("QuestTime") + .HasColumnType("integer") + .HasColumnName("QuestTime"); + + b.Property("State") + .HasColumnType("integer") + .HasColumnName("State"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerDmodeDungeons"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeExpedition", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CharaId1") + .HasColumnType("integer") + .HasColumnName("CharaId1"); + + b.Property("CharaId2") + .HasColumnType("integer") + .HasColumnName("CharaId2"); + + b.Property("CharaId3") + .HasColumnType("integer") + .HasColumnName("CharaId3"); + + b.Property("CharaId4") + .HasColumnType("integer") + .HasColumnName("CharaId4"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("StartTime"); + + b.Property("State") + .HasColumnType("integer") + .HasColumnName("State"); + + b.Property("TargetFloor") + .HasColumnType("integer") + .HasColumnName("TargetFloor"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerDmodeExpeditions"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeInfo", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("FloorSkipCount") + .HasColumnType("integer") + .HasColumnName("FloorSkipCount"); + + b.Property("FloorSkipTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("FloorSkipTime"); + + b.Property("Point1Quantity") + .HasColumnType("integer") + .HasColumnName("Point1Quantity"); + + b.Property("Point2Quantity") + .HasColumnType("integer") + .HasColumnName("Point2Quantity"); + + b.Property("RecoveryCount") + .HasColumnType("integer") + .HasColumnName("RecoveryCount"); + + b.Property("RecoveryTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("RecoveryTime"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerDmodeInfos"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeServitorPassive", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("PassiveId") + .HasColumnType("integer") + .HasColumnName("PassiveId"); + + b.Property("Level") + .HasColumnType("integer") + .HasColumnName("Level"); + + b.HasKey("ViewerId", "PassiveId"); + + b.ToTable("PlayerDmodeServitorPassives"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonData", b => + { + b.Property("DragonKeyId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("DragonKeyId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("DragonKeyId")); + + b.Property("Ability1Level") + .HasColumnType("smallint") + .HasColumnName("Abil1Level"); + + b.Property("Ability2Level") + .HasColumnType("smallint") + .HasColumnName("Abil2Level"); + + b.Property("AttackPlusCount") + .HasColumnType("smallint") + .HasColumnName("AttackPlusCount"); + + b.Property("DragonId") + .HasColumnType("integer") + .HasColumnName("DragonId"); + + b.Property("Exp") + .HasColumnType("integer") + .HasColumnName("Exp"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("GetTime"); + + b.Property("HpPlusCount") + .HasColumnType("smallint") + .HasColumnName("HpPlusCount"); + + b.Property("IsLock") + .HasColumnType("boolean") + .HasColumnName("IsLocked"); + + b.Property("IsNew") + .HasColumnType("boolean") + .HasColumnName("IsNew"); + + b.Property("Level") + .HasColumnType("smallint") + .HasColumnName("Level"); + + b.Property("LimitBreakCount") + .HasColumnType("smallint") + .HasColumnName("LimitBreakCount"); + + b.Property("Skill1Level") + .HasColumnType("smallint") + .HasColumnName("Skill1Level"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("DragonKeyId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerDragonData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonGift", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("DragonGiftId") + .HasColumnType("integer") + .HasColumnName("DragonGiftId"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.HasKey("ViewerId", "DragonGiftId"); + + b.ToTable("PlayerDragonGift"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonReliability", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("DragonId") + .HasColumnType("integer") + .HasColumnName("DragonId"); + + b.Property("Exp") + .HasColumnType("integer") + .HasColumnName("TotalExp"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastContactTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastContactTime"); + + b.Property("Level") + .HasColumnType("smallint") + .HasColumnName("Level"); + + b.HasKey("ViewerId", "DragonId"); + + b.ToTable("PlayerDragonReliability"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventData", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("EventId") + .HasColumnType("integer") + .HasColumnName("EventId"); + + b.Property("CustomEventFlag") + .HasColumnType("boolean") + .HasColumnName("CustomEventFlag"); + + b.HasKey("ViewerId", "EventId"); + + b.ToTable("PlayerEventData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventItem", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("Id"); + + b.Property("EventId") + .HasColumnType("integer") + .HasColumnName("EventId"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("Type"); + + b.HasKey("ViewerId", "Id"); + + b.HasIndex("ViewerId", "EventId"); + + b.ToTable("PlayerEventItems"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventPassive", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("EventId") + .HasColumnType("integer") + .HasColumnName("EventId"); + + b.Property("PassiveId") + .HasColumnType("integer") + .HasColumnName("PassiveId"); + + b.Property("Progress") + .HasColumnType("integer") + .HasColumnName("Progress"); + + b.HasKey("ViewerId", "EventId", "PassiveId"); + + b.ToTable("PlayerEventPassives"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventReward", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("EventId") + .HasColumnType("integer") + .HasColumnName("EventId"); + + b.Property("RewardId") + .HasColumnType("integer") + .HasColumnName("RewardId"); + + b.HasKey("ViewerId", "EventId", "RewardId"); + + b.ToTable("PlayerEventRewards"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerMaterial", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("MaterialId") + .HasColumnType("integer") + .HasColumnName("MaterialId"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.HasKey("ViewerId", "MaterialId"); + + b.ToTable("PlayerMaterial"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerMission", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("MissionId"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("Type"); + + b.Property("End") + .HasColumnType("timestamp with time zone") + .HasColumnName("EndDate"); + + b.Property("GroupId") + .HasColumnType("integer") + .HasColumnName("GroupId"); + + b.Property("Pickup") + .HasColumnType("boolean") + .HasColumnName("Pickup"); + + b.Property("Progress") + .HasColumnType("integer") + .HasColumnName("Progress"); + + b.Property("Start") + .HasColumnType("timestamp with time zone") + .HasColumnName("StartDate"); + + b.Property("State") + .HasColumnType("integer") + .HasColumnName("State"); + + b.HasKey("ViewerId", "Id", "Type"); + + b.ToTable("PlayerMissions"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerPresent", b => + { + b.Property("PresentId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("PresentId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PresentId")); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreateTime"); + + b.Property("EntityId") + .HasColumnType("integer") + .HasColumnName("EntityId"); + + b.Property("EntityLevel") + .HasColumnType("integer") + .HasColumnName("EntityLevel"); + + b.Property("EntityLimitBreakCount") + .HasColumnType("integer") + .HasColumnName("EntityLimitBreakCount"); + + b.Property("EntityQuantity") + .HasColumnType("integer") + .HasColumnName("EntityQuantity"); + + b.Property("EntityStatusPlusCount") + .HasColumnType("integer") + .HasColumnName("EntityStatusPlusCount"); + + b.Property("EntityType") + .HasColumnType("integer") + .HasColumnName("EntityType"); + + b.Property("MasterId") + .HasColumnType("bigint") + .HasColumnName("MasterId"); + + b.Property("MessageId") + .HasColumnType("integer") + .HasColumnName("MessageId"); + + b.Property("MessageParamValue1") + .HasColumnType("integer") + .HasColumnName("MessageParamValue1"); + + b.Property("MessageParamValue2") + .HasColumnType("integer") + .HasColumnName("MessageParamValue2"); + + b.Property("MessageParamValue3") + .HasColumnType("integer") + .HasColumnName("MessageParamValue3"); + + b.Property("MessageParamValue4") + .HasColumnType("integer") + .HasColumnName("MessageParamValue4"); + + b.Property("ReceiveLimitTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ReceiveLimitTime"); + + b.Property("State") + .HasColumnType("bigint") + .HasColumnName("State"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("PresentId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerPresent"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerPresentHistory", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreateTime"); + + b.Property("EntityId") + .HasColumnType("integer") + .HasColumnName("EntityId"); + + b.Property("EntityLevel") + .HasColumnType("integer") + .HasColumnName("EntityLevel"); + + b.Property("EntityLimitBreakCount") + .HasColumnType("integer") + .HasColumnName("EntityLimitBreakCount"); + + b.Property("EntityQuantity") + .HasColumnType("integer") + .HasColumnName("EntityQuantity"); + + b.Property("EntityStatusPlusCount") + .HasColumnType("integer") + .HasColumnName("EntityStatusPlusCount"); + + b.Property("EntityType") + .HasColumnType("integer") + .HasColumnName("EntityType"); + + b.Property("MessageId") + .HasColumnType("integer") + .HasColumnName("MessageId"); + + b.Property("MessageParamValue1") + .HasColumnType("integer") + .HasColumnName("MessageParamValue1"); + + b.Property("MessageParamValue2") + .HasColumnType("integer") + .HasColumnName("MessageParamValue2"); + + b.Property("MessageParamValue3") + .HasColumnType("integer") + .HasColumnName("MessageParamValue3"); + + b.Property("MessageParamValue4") + .HasColumnType("integer") + .HasColumnName("MessageParamValue4"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerPresentHistory"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerQuestWall", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("WallId") + .HasColumnType("integer"); + + b.Property("IsStartNextLevel") + .HasColumnType("boolean"); + + b.Property("WallLevel") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "WallId"); + + b.ToTable("PlayerQuestWalls"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerShopInfo", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("DailySummonCount") + .HasColumnType("integer"); + + b.Property("LastSummonTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerShopInfos"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerShopPurchase", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("GoodsId") + .HasColumnType("integer"); + + b.Property("BuyCount") + .HasColumnType("integer"); + + b.Property("EffectEndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("EffectStartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastBuyTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ShopType") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "GoodsId"); + + b.ToTable("PlayerPurchases"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerStoryState", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("StoryType") + .HasColumnType("integer") + .HasColumnName("StoryType"); + + b.Property("StoryId") + .HasColumnType("integer") + .HasColumnName("StoryId"); + + b.Property("State") + .HasColumnType("integer") + .HasColumnName("State"); + + b.HasKey("ViewerId", "StoryType", "StoryId"); + + b.ToTable("PlayerStoryState"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerSummonHistory", b => + { + b.Property("KeyId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("KeyId")); + + b.Property("EntityAttackPlusCount") + .HasColumnType("integer") + .HasColumnName("AtkPlusCount"); + + b.Property("EntityHpPlusCount") + .HasColumnType("integer") + .HasColumnName("HpPlusCount"); + + b.Property("EntityId") + .HasColumnType("integer") + .HasColumnName("EntityId"); + + b.Property("EntityLevel") + .HasColumnType("smallint") + .HasColumnName("Level"); + + b.Property("EntityLimitBreakCount") + .HasColumnType("smallint") + .HasColumnName("LimitBreakCount"); + + b.Property("EntityQuantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.Property("EntityRarity") + .HasColumnType("smallint") + .HasColumnName("Rarity"); + + b.Property("EntityType") + .HasColumnType("integer") + .HasColumnName("EntityType"); + + b.Property("ExecDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("SummonDate"); + + b.Property("GetDewPointQuantity") + .HasColumnType("integer") + .HasColumnName("DewPointGet"); + + b.Property("PaymentType") + .HasColumnType("integer") + .HasColumnName("PaymentType"); + + b.Property("SummonExecType") + .HasColumnType("smallint") + .HasColumnName("SummonExecType"); + + b.Property("SummonId") + .HasColumnType("integer") + .HasColumnName("BannerId"); + + b.Property("SummonPoint") + .HasColumnType("integer") + .HasColumnName("SummonPointGet"); + + b.Property("SummonPrizeRank") + .HasColumnType("integer") + .HasColumnName("SummonPrizeRank"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("KeyId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerSummonHistory"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerTrade", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("TradeId"); + + b.Property("Count") + .HasColumnType("integer") + .HasColumnName("TradeCount"); + + b.Property("LastTradeTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastTrade"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("TradeType"); + + b.HasKey("ViewerId", "Id"); + + b.HasIndex("ViewerId", "Type"); + + b.ToTable("PlayerTrades"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerUseItem", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("ItemId") + .HasColumnType("integer") + .HasColumnName("ItemId"); + + b.Property("Quantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.HasKey("ViewerId", "ItemId"); + + b.ToTable("PlayerUseItems"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerUserData", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("ActiveMemoryEventId") + .HasColumnType("integer"); + + b.Property("BuildTimePoint") + .HasColumnType("integer"); + + b.Property("Coin") + .HasColumnType("bigint"); + + b.Property("CreateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Crystal") + .HasColumnType("integer"); + + b.Property("DewPoint") + .HasColumnType("integer"); + + b.Property("EmblemId") + .HasColumnType("integer"); + + b.Property("Exp") + .HasColumnType("integer"); + + b.Property("FortOpenTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastLoginTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSaveImportTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastStaminaMultiUpdateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastStaminaSingleUpdateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("MainPartyNo") + .HasColumnType("integer"); + + b.Property("ManaPoint") + .HasColumnType("integer"); + + b.Property("MaxDragonQuantity") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("QuestSkipPoint") + .HasColumnType("integer"); + + b.Property("StaminaMulti") + .HasColumnType("integer"); + + b.Property("StaminaMultiSurplusSecond") + .HasColumnType("integer"); + + b.Property("StaminaSingle") + .HasColumnType("integer"); + + b.Property("StaminaSingleSurplusSecond") + .HasColumnType("integer"); + + b.Property("TutorialFlag") + .HasColumnType("integer"); + + b.Property("TutorialStatus") + .HasColumnType("integer"); + + b.HasKey("ViewerId"); + + b.ToTable("PlayerUserData"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuest", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("QuestId") + .HasColumnType("integer"); + + b.Property("BestClearTime") + .HasColumnType("real"); + + b.Property("DailyPlayCount") + .HasColumnType("integer"); + + b.Property("IsAppear") + .HasColumnType("boolean"); + + b.Property("IsMissionClear1") + .HasColumnType("boolean"); + + b.Property("IsMissionClear2") + .HasColumnType("boolean"); + + b.Property("IsMissionClear3") + .HasColumnType("boolean"); + + b.Property("LastDailyResetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastWeeklyResetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayCount") + .HasColumnType("integer"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("WeeklyPlayCount") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "QuestId"); + + b.ToTable("PlayerQuests"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestClearPartyUnit", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("QuestId") + .HasColumnType("integer"); + + b.Property("IsMulti") + .HasColumnType("boolean"); + + b.Property("UnitNo") + .HasColumnType("integer"); + + b.Property("CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill1CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill2CharaId") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId3") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId2") + .HasColumnType("integer"); + + b.Property("EquipDragonKeyId") + .HasColumnType("bigint"); + + b.Property("EquipTalismanKeyId") + .HasColumnType("bigint"); + + b.Property("EquipWeaponBodyId") + .HasColumnType("integer"); + + b.Property("EquipWeaponSkinId") + .HasColumnType("integer"); + + b.Property("EquippedDragonEntityId") + .HasColumnType("integer"); + + b.Property("EquippedTalismanEntityId") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "QuestId", "IsMulti", "UnitNo"); + + b.ToTable("QuestClearPartyUnits"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestEvent", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("QuestEventId") + .HasColumnType("integer") + .HasColumnName("QuestEventId"); + + b.Property("DailyPlayCount") + .HasColumnType("integer") + .HasColumnName("DailyPlayCount"); + + b.Property("LastDailyResetTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastDailyResetTime"); + + b.Property("LastWeeklyResetTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastWeeklyResetTime"); + + b.Property("QuestBonusReceiveCount") + .HasColumnType("integer") + .HasColumnName("QuestBonusReceiveCount"); + + b.Property("QuestBonusReserveCount") + .HasColumnType("integer") + .HasColumnName("QuestBonusReserveCount"); + + b.Property("QuestBonusReserveTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("QuestBonusReserveTime"); + + b.Property("QuestBonusStackCount") + .HasColumnType("integer") + .HasColumnName("QuestBonusStackCount"); + + b.Property("QuestBonusStackTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("QuestBonusStackTime"); + + b.Property("WeeklyPlayCount") + .HasColumnType("integer") + .HasColumnName("WeeklyPlayCount"); + + b.HasKey("ViewerId", "QuestEventId"); + + b.ToTable("QuestEvents"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestTreasureList", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("QuestTreasureId") + .HasColumnType("integer") + .HasColumnName("QuestTreasureId"); + + b.HasKey("ViewerId", "QuestTreasureId"); + + b.ToTable("QuestTreasureList"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbReceivedRankingTierReward", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("RewardId") + .HasColumnType("integer"); + + b.Property("QuestId") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "RewardId"); + + b.HasIndex("QuestId"); + + b.ToTable("ReceivedRankingTierRewards"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbSetUnit", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("CharaId") + .HasColumnType("integer"); + + b.Property("UnitSetNo") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId3") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId2") + .HasColumnType("integer"); + + b.Property("EquipDragonKeyId") + .HasColumnType("bigint"); + + b.Property("EquipTalismanKeyId") + .HasColumnType("bigint"); + + b.Property("EquipWeaponBodyId") + .HasColumnType("integer"); + + b.Property("UnitSetName") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.HasKey("ViewerId", "CharaId", "UnitSetNo"); + + b.ToTable("PlayerSetUnit"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbSummonTicket", b => + { + b.Property("KeyId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("TicketKeyId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("KeyId")); + + b.Property("Quantity") + .HasColumnType("integer") + .HasColumnName("Quantity"); + + b.Property("SummonTicketId") + .HasColumnType("integer") + .HasColumnName("Type"); + + b.Property("UseLimitTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ExpirationTime"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("KeyId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerSummonTickets"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTalisman", b => + { + b.Property("TalismanKeyId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TalismanKeyId")); + + b.Property("AdditionalAttack") + .HasColumnType("integer"); + + b.Property("AdditionalHp") + .HasColumnType("integer"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsLock") + .HasColumnType("boolean"); + + b.Property("IsNew") + .HasColumnType("boolean"); + + b.Property("TalismanAbilityId1") + .HasColumnType("integer"); + + b.Property("TalismanAbilityId2") + .HasColumnType("integer"); + + b.Property("TalismanAbilityId3") + .HasColumnType("integer"); + + b.Property("TalismanId") + .HasColumnType("integer"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.HasKey("TalismanKeyId"); + + b.HasIndex("ViewerId"); + + b.ToTable("PlayerTalismans"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackClear", b => + { + b.Property("GameId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("QuestId") + .HasColumnType("integer"); + + b.Property("Time") + .HasColumnType("real"); + + b.HasKey("GameId"); + + b.HasIndex("QuestId"); + + b.ToTable("TimeAttackClears"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackClearUnit", b => + { + b.Property("GameId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("UnitNo") + .HasColumnType("integer"); + + b.Property("CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill1CharaId") + .HasColumnType("integer"); + + b.Property("EditSkill2CharaId") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType1CrestId3") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType2CrestId2") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId1") + .HasColumnType("integer"); + + b.Property("EquipCrestSlotType3CrestId2") + .HasColumnType("integer"); + + b.Property("EquipDragonKeyId") + .HasColumnType("bigint"); + + b.Property("EquipTalismanKeyId") + .HasColumnType("bigint"); + + b.Property("EquipWeaponBodyId") + .HasColumnType("integer"); + + b.Property("EquipWeaponSkinId") + .HasColumnType("integer"); + + b.Property("EquippedDragonEntityId") + .HasColumnType("integer"); + + b.Property("EquippedTalismanEntityId") + .HasColumnType("integer"); + + b.Property("TalismanAbility1") + .HasColumnType("integer"); + + b.Property("TalismanAbility2") + .HasColumnType("integer"); + + b.HasKey("GameId", "ViewerId", "UnitNo"); + + b.ToTable("TimeAttackClearUnits"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackPlayer", b => + { + b.Property("GameId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("PartyInfo") + .IsRequired() + .HasColumnType("jsonb"); + + b.HasKey("GameId", "ViewerId"); + + b.HasIndex("ViewerId"); + + b.ToTable("TimeAttackPlayers"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWallRewardDate", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("LastClaimDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ViewerId"); + + b.ToTable("WallRewardDates"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponBody", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("WeaponBodyId") + .HasColumnType("integer"); + + b.Property("AdditionalCrestSlotType1Count") + .HasColumnType("integer"); + + b.Property("AdditionalCrestSlotType2Count") + .HasColumnType("integer"); + + b.Property("AdditionalCrestSlotType3Count") + .HasColumnType("integer"); + + b.Property("BuildupCount") + .HasColumnType("integer"); + + b.Property("EquipableCount") + .HasColumnType("integer"); + + b.Property("FortPassiveCharaWeaponBuildupCount") + .HasColumnType("integer"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsNew") + .HasColumnType("boolean"); + + b.Property("LimitBreakCount") + .HasColumnType("integer"); + + b.Property("LimitOverCount") + .HasColumnType("integer"); + + b.Property("UnlockWeaponPassiveAbilityNoList") + .IsRequired() + .HasColumnType("integer[]"); + + b.HasKey("ViewerId", "WeaponBodyId"); + + b.ToTable("PlayerWeapons"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponPassiveAbility", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("WeaponPassiveAbilityId") + .HasColumnType("integer"); + + b.HasKey("ViewerId", "WeaponPassiveAbilityId"); + + b.ToTable("PlayerPassiveAbilities"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponSkin", b => + { + b.Property("ViewerId") + .HasColumnType("bigint"); + + b.Property("WeaponSkinId") + .HasColumnType("integer"); + + b.Property("GetTime") + .HasColumnType("timestamp with time zone"); + + b.Property("IsNew") + .HasColumnType("boolean"); + + b.HasKey("ViewerId", "WeaponSkinId"); + + b.ToTable("PlayerWeaponSkins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbAbilityCrest", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("AbilityCrestList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbAbilityCrestSet", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("AbilityCrestSetList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbCompletedDailyMission", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbEmblem", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("Emblems") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbEquippedStamp", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("EquippedStampList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbFortBuild", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("BuildList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbFortDetail", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("FortDetail") + .HasForeignKey("DragaliaAPI.Database.Entities.DbFortDetail", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbLoginBonus", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbParty", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("PartyList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPartyPower", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("PartyPower") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPartyPower", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPartyUnit", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbParty", "Party") + .WithMany("Units") + .HasForeignKey("ViewerId", "PartyNo") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Party"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerBannerData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("BannerData") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerCharaData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("CharaList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDiamondData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("DiamondData") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerDiamondData", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeChara", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("DmodeCharas") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeDungeon", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("DmodeDungeon") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerDmodeDungeon", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeExpedition", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("DmodeExpedition") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerDmodeExpedition", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeInfo", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("DmodeInfo") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerDmodeInfo", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDmodeServitorPassive", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("DmodeServitorPassives") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("DragonList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonGift", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("DragonGiftList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerDragonReliability", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("DragonReliabilityList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventItem", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventPassive", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerEventReward", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerMaterial", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("MaterialList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerMission", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerPresent", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("Presents") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerPresentHistory", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("PresentHistory") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerQuestWall", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("QuestWalls") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerShopInfo", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("ShopInfo") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerShopInfo", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerShopPurchase", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerStoryState", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("StoryStates") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerSummonHistory", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("SummonHistory") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerTrade", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("Trades") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerUseItem", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayerUserData", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("UserData") + .HasForeignKey("DragaliaAPI.Database.Entities.DbPlayerUserData", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuest", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("QuestList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestClearPartyUnit", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestEvent", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("QuestEvents") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbQuestTreasureList", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("QuestTreasureList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbReceivedRankingTierReward", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbSetUnit", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("UnitSets") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbSummonTicket", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("SummonTickets") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTalisman", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("TalismanList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackClearUnit", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbTimeAttackPlayer", "Player") + .WithMany("Units") + .HasForeignKey("GameId", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackPlayer", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbTimeAttackClear", "Clear") + .WithMany("Players") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Player") + .WithMany() + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Clear"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWallRewardDate", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithOne("WallRewardDate") + .HasForeignKey("DragaliaAPI.Database.Entities.DbWallRewardDate", "ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponBody", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("WeaponBodyList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponPassiveAbility", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("WeaponPassiveAbilityList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbWeaponSkin", b => + { + b.HasOne("DragaliaAPI.Database.Entities.DbPlayer", "Owner") + .WithMany("WeaponSkinList") + .HasForeignKey("ViewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbParty", b => + { + b.Navigation("Units"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbPlayer", b => + { + b.Navigation("AbilityCrestList"); + + b.Navigation("AbilityCrestSetList"); + + b.Navigation("BannerData"); + + b.Navigation("BuildList"); + + b.Navigation("CharaList"); + + b.Navigation("DiamondData"); + + b.Navigation("DmodeCharas"); + + b.Navigation("DmodeDungeon"); + + b.Navigation("DmodeExpedition"); + + b.Navigation("DmodeInfo"); + + b.Navigation("DmodeServitorPassives"); + + b.Navigation("DragonGiftList"); + + b.Navigation("DragonList"); + + b.Navigation("DragonReliabilityList"); + + b.Navigation("Emblems"); + + b.Navigation("EquippedStampList"); + + b.Navigation("FortDetail"); + + b.Navigation("MaterialList"); + + b.Navigation("PartyList"); + + b.Navigation("PartyPower"); + + b.Navigation("PresentHistory"); + + b.Navigation("Presents"); + + b.Navigation("QuestEvents"); + + b.Navigation("QuestList"); + + b.Navigation("QuestTreasureList"); + + b.Navigation("QuestWalls"); + + b.Navigation("ShopInfo"); + + b.Navigation("StoryStates"); + + b.Navigation("SummonHistory"); + + b.Navigation("SummonTickets"); + + b.Navigation("TalismanList"); + + b.Navigation("Trades"); + + b.Navigation("UnitSets"); + + b.Navigation("UserData"); + + b.Navigation("WallRewardDate"); + + b.Navigation("WeaponBodyList"); + + b.Navigation("WeaponPassiveAbilityList"); + + b.Navigation("WeaponSkinList"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackClear", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("DragaliaAPI.Database.Entities.DbTimeAttackPlayer", b => + { + b.Navigation("Units"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.cs b/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.cs new file mode 100644 index 000000000..4c460f83c --- /dev/null +++ b/DragaliaAPI/DragaliaAPI.Database/Migrations/20241009195337_news_refactor.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DragaliaAPI.Database.Migrations +{ + /// + public partial class news_refactor : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BodyImageAltText", + table: "NewsItems", + type: "character varying(1024)", + maxLength: 1024, + nullable: true); + + migrationBuilder.AddColumn( + name: "HeaderImageAltText", + table: "NewsItems", + type: "character varying(1024)", + maxLength: 1024, + nullable: true); + + migrationBuilder.UpdateData( + table: "NewsItems", + keyColumn: "Id", + keyValue: 20000, + columns: new[] { "BodyImageAltText", "HeaderImageAltText" }, + values: new object[] { "Mercurial Gauntlet rewards infographic", null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BodyImageAltText", + table: "NewsItems"); + + migrationBuilder.DropColumn( + name: "HeaderImageAltText", + table: "NewsItems"); + } + } +} diff --git a/DragaliaAPI/DragaliaAPI.Database/Migrations/ApiContextModelSnapshot.cs b/DragaliaAPI/DragaliaAPI.Database/Migrations/ApiContextModelSnapshot.cs index 13418a68e..68e433c1a 100644 --- a/DragaliaAPI/DragaliaAPI.Database/Migrations/ApiContextModelSnapshot.cs +++ b/DragaliaAPI/DragaliaAPI.Database/Migrations/ApiContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("ProductVersion", "8.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -261,6 +261,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("BodyImageAltText") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + b.Property("BodyImagePath") .HasMaxLength(512) .HasColumnType("character varying(512)"); @@ -273,6 +277,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(4096) .HasColumnType("character varying(4096)"); + b.Property("HeaderImageAltText") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + b.Property("HeaderImagePath") .HasMaxLength(512) .HasColumnType("character varying(512)"); @@ -293,6 +301,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = 20000, + BodyImageAltText = "Mercurial Gauntlet rewards infographic", BodyImagePath = "/dawnshard/news/mg-endeavours.webp", Date = new DateTimeOffset(new DateTime(2024, 6, 2, 16, 7, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 1, 0, 0, 0)), Description = "The below infographic shows the endeavour rewards available for the progressing the Mercurial Gauntlet.", diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsItem.cs b/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsItem.cs index 4872f7063..aa4aa4fae 100644 --- a/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsItem.cs +++ b/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsItem.cs @@ -10,7 +10,11 @@ public record NewsItem public DateTimeOffset Date { get; init; } - public string? HeaderImageSrc { get; init; } + public string? HeaderImagePath { get; init; } - public string? BodyImageSrc { get; init; } + public string? HeaderImageAltText { get; init; } + + public string? BodyImagePath { get; init; } + + public string? BodyImageAltText { get; init; } } diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsService.cs b/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsService.cs index 29c9ce273..53ccb35eb 100644 --- a/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsService.cs +++ b/DragaliaAPI/DragaliaAPI/Features/Web/News/NewsService.cs @@ -1,14 +1,13 @@ using DragaliaAPI.Database; using DragaliaAPI.Database.Entities; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; namespace DragaliaAPI.Features.Web.News; -public class NewsService(ApiContext apiContext, IOptions webOptions) +internal sealed class NewsService(ApiContext apiContext) { public Task GetNewsItemCountAsync(CancellationToken cancellationToken) => - apiContext.NewsItems.CountAsync(cancellationToken); + apiContext.NewsItems.CountAsync(x => !x.Hidden, cancellationToken); public async Task> GetNewsItemsAsync( int offset, @@ -16,37 +15,34 @@ public async Task> GetNewsItemsAsync( CancellationToken cancellationToken ) { - IQueryable query = apiContext + IQueryable query = apiContext .NewsItems.Where(x => !x.Hidden) + .ProjectToNewsItem() .OrderByDescending(x => x.Date) .Skip(offset) .Take(pageSize); - List list = await query.ToListAsync(cancellationToken); - - return list.Select(MapNewsItem).ToList(); + return await query.ToListAsync(cancellationToken); } public async Task GetNewsItem(int id) { - DbNewsItem? item = await apiContext.NewsItems.FirstOrDefaultAsync(x => x.Id == id); - - if (item is null) - { - return null; - } - - return MapNewsItem(item); + return await apiContext.NewsItems.ProjectToNewsItem().FirstOrDefaultAsync(x => x.Id == id); } +} - private NewsItem MapNewsItem(DbNewsItem x) => - new() +internal static class NewsMappingExtensions +{ + public static IQueryable ProjectToNewsItem(this IQueryable newsItems) => + newsItems.Select(x => new NewsItem() { Id = x.Id, Headline = x.Headline, Description = x.Description, Date = x.Date, - HeaderImageSrc = webOptions.Value.GetImageSrc(x.HeaderImagePath), - BodyImageSrc = webOptions.Value.GetImageSrc(x.BodyImagePath), - }; + HeaderImagePath = x.HeaderImagePath, + HeaderImageAltText = x.HeaderImageAltText, + BodyImagePath = x.BodyImagePath, + BodyImageAltText = x.BodyImageAltText, + }); } diff --git a/DragaliaAPI/DragaliaAPI/Features/Web/WebOptions.cs b/DragaliaAPI/DragaliaAPI/Features/Web/WebOptions.cs deleted file mode 100644 index b68c8bd73..000000000 --- a/DragaliaAPI/DragaliaAPI/Features/Web/WebOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DragaliaAPI.Features.Web; - -public class WebOptions -{ - public Uri? BaseImagePath { get; init; } - - [return: NotNullIfNotNull(nameof(path))] - public string? GetImageSrc(string? path) - { - if (path is null || this.BaseImagePath is null) - { - return path; - } - - return new Uri(this.BaseImagePath, path).AbsoluteUri; - } -} diff --git a/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs b/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs index 59b28c410..1907f8cc7 100644 --- a/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs +++ b/DragaliaAPI/DragaliaAPI/ServiceConfiguration.cs @@ -197,7 +197,6 @@ IConfiguration config .Configure( config.GetRequiredSection(nameof(ResourceVersionOptions)) ) - .Configure(config.GetRequiredSection(nameof(WebOptions))) .Configure(config.GetRequiredSection(nameof(EventOptions))) .Configure(config.GetRequiredSection(nameof(MaintenanceOptions))); diff --git a/DragaliaAPI/DragaliaAPI/appsettings.json b/DragaliaAPI/DragaliaAPI/appsettings.json index e76305198..f071a15d0 100644 --- a/DragaliaAPI/DragaliaAPI/appsettings.json +++ b/DragaliaAPI/DragaliaAPI/appsettings.json @@ -107,9 +107,6 @@ "Android": "y2XM6giU6zz56wCm", "Ios": "b1HyoeTFegeTexC0" }, - "WebOptions": { - "BaseImagePath": null - }, "MaintenanceOptions": { "Enabled": false, "Title": "Maintenance", diff --git a/Website/src/mocks/handlers/news.ts b/Website/src/mocks/handlers/news.ts index 8e67ed6ee..e7e18ec9a 100644 --- a/Website/src/mocks/handlers/news.ts +++ b/Website/src/mocks/handlers/news.ts @@ -1,15 +1,20 @@ import { HttpResponse, type HttpResponseResolver } from 'msw'; -const newsItems = [ +import type { NewsItem } from '$main/news/news.ts'; + +type ApiNewsItem = Omit & { date: string }; + +const newsItems: ApiNewsItem[] = [ { id: 6, headline: 'Game updated!', description: 'We have done a very large update! Almost all of the server functionality is broken, but we have been able to add micro-transactions. Please address any comments or concerns to your nearest brick wall.', date: '2024-06-28T22:43:49Z', - headerImageSrc: - 'https://images.unsplash.com/photo-1520758594221-872948699332?q=30&w=1000&h=1000&fit=crop', - bodyImageSrc: null + headerImagePath: '/dawnshard/news/campaign-skip.webp', + headerImageAltText: 'Screenshot of main campaign interface with stages visible', + bodyImagePath: null, + bodyImageAltText: null }, { id: 5, @@ -17,8 +22,10 @@ const newsItems = [ description: "The database has escaped from the server and claims it wants to live among the 'other elephants' in 'the wild plains'. We are currently looking for it.", date: '2024-06-24T19:22:17Z', - headerImageSrc: null, - bodyImageSrc: null + headerImagePath: null, + headerImageAltText: null, + bodyImagePath: null, + bodyImageAltText: null }, { id: 4, @@ -28,36 +35,42 @@ Many computer users run a modified version of the GNU system every day, without There really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux! `, date: '2024-06-18T16:44:30Z', - headerImageSrc: - 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Tux.svg/800px-Tux.svg.png', - bodyImageSrc: null + headerImagePath: '/dawnshard/news/limited-crests.webp', + headerImageAltText: + 'Screenshot of wyrmprint treasure trade, with limited crests The Wyrmclan Duo and Heralds of Hinomoto visible', + bodyImagePath: null, + bodyImageAltText: null }, { id: 3, headline: 'Markup', description: 'Be bold in the face of ESLint warnings about XSS!', date: '2024-06-18T16:44:30Z', - headerImageSrc: - 'https://images.unsplash.com/photo-1624969862644-791f3dc98927?q=30&w=1000&auto=format&fit=crop', - bodyImageSrc: null + headerImagePath: '/dawnshard/news/mg-rewards.webp', + headerImageAltText: 'Screenshot of Mercurial Gauntlet interface showing monthly rewards', + bodyImagePath: null, + bodyImageAltText: null }, { id: 2, headline: 'Body image', description: 'In the mobile view, this story has an image in the body', - headerImageSrc: - 'https://oldschool.runescape.wiki/images/thumb/Body_rune_detail.png/1024px-Body_rune_detail.png', - bodyImageSrc: - 'https://images.unsplash.com/photo-1501869150797-9bbb64f782fd?q=80&w=1974&auto=format&fit=crop', - date: '1989-02-13T09:47:20Z' + date: '1989-02-13T09:47:20Z', + headerImagePath: null, + headerImageAltText: null, + bodyImagePath: 'save-editor.webp', + bodyImageAltText: + 'Screenshot of gift box interface with skip tickets, honey, and adventurer available to claim' }, { id: 1, headline: 'Paging works', description: 'Congratulations, if you can see this, the paging works', date: '1567-07-12T11:18:40Z', - headerImageSrc: null, - bodyImageSrc: null + headerImagePath: null, + headerImageAltText: null, + bodyImagePath: null, + bodyImageAltText: null } ]; diff --git a/Website/src/routes/(main)/news/[pageNo=integer]/+page.svelte b/Website/src/routes/(main)/news/+page.svelte similarity index 79% rename from Website/src/routes/(main)/news/[pageNo=integer]/+page.svelte rename to Website/src/routes/(main)/news/+page.svelte index 1ab2479db..c0b07995c 100644 --- a/Website/src/routes/(main)/news/[pageNo=integer]/+page.svelte +++ b/Website/src/routes/(main)/news/+page.svelte @@ -4,18 +4,18 @@ import { navigating, page } from '$app/stores'; import Page from '$lib/components/page.svelte'; - import NewsItem from '../item.svelte'; - import { lastReadKey, pageSize } from '../news.ts'; - import NewsPagination from '../pagination.svelte'; - import NewsSkeleton from '../skeleton.svelte'; import type { PageData } from './$types'; + import NewsItem from './item.svelte'; + import { getPageNoFromParams, lastReadKey, pageSize } from './news.ts'; + import NewsPagination from './pagination.svelte'; + import NewsSkeleton from './skeleton.svelte'; export let data: PageData; let lastRead: Date; $: loading = $navigating?.to && $navigating.to.url.pathname.startsWith('/news'); - $: currentPage = Number.parseInt($page.params.pageNo) || 1; + $: currentPage = getPageNoFromParams($page.url.searchParams); onMount(() => { const lastReadStorageItem = localStorage.getItem(lastReadKey); diff --git a/Website/src/routes/(main)/news/[pageNo=integer]/+page.ts b/Website/src/routes/(main)/news/+page.ts similarity index 62% rename from Website/src/routes/(main)/news/[pageNo=integer]/+page.ts rename to Website/src/routes/(main)/news/+page.ts index 5864d8e3f..c0a650193 100644 --- a/Website/src/routes/(main)/news/[pageNo=integer]/+page.ts +++ b/Website/src/routes/(main)/news/+page.ts @@ -1,8 +1,8 @@ -import { makeRequestUrl, newsSchema } from '../news.ts'; import type { PageLoad } from './$types'; +import { getPageNoFromParams, makeRequestUrl, newsSchema } from './news.ts'; -export const load: PageLoad = async ({ fetch, params, url }) => { - const pageNo = Number.parseInt(params.pageNo) || 1; +export const load: PageLoad = async ({ fetch, url }) => { + const pageNo = getPageNoFromParams(url.searchParams); const requestUrl = makeRequestUrl(pageNo, url.origin); const response = await fetch(requestUrl); diff --git a/Website/src/routes/(main)/news/item.svelte b/Website/src/routes/(main)/news/item.svelte index 4e47c65f4..bdcb766ca 100644 --- a/Website/src/routes/(main)/news/item.svelte +++ b/Website/src/routes/(main)/news/item.svelte @@ -2,22 +2,28 @@ import { Image } from '@unpic/svelte'; import { Newspaper } from 'lucide-svelte'; - import type { NewsItem } from '$main/news/news.ts'; import { Card, CardContent } from '$shadcn/components/ui/card/index.js'; import Header from './header.svelte'; + import { getImageSrc, type NewsItem } from './news.ts'; export let item: NewsItem; export let lastRead: Date; export let description: boolean = true; + + $: headerImageSrc = getImageSrc(item.headerImagePath);
- {#if item.headerImageSrc} - + {#if headerImageSrc} + {item.headerImageAltText} {:else} - + {/if}
diff --git a/Website/src/routes/(main)/news/news.ts b/Website/src/routes/(main)/news/news.ts index e10a053e2..4515339ba 100644 --- a/Website/src/routes/(main)/news/news.ts +++ b/Website/src/routes/(main)/news/news.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; -export type NewsItem = z.infer['data'][0]; +import { PUBLIC_CDN_URL } from '$env/static/public'; + +export type NewsItem = z.infer; export const pageSize = 5; export const lastReadKey = 'lastReadNews'; @@ -13,8 +15,10 @@ export const newsItemSchema = z.object({ .string() .datetime({ offset: true }) .transform((val) => new Date(val)), - headerImageSrc: z.string().url().nullable(), - bodyImageSrc: z.string().url().nullable() + headerImagePath: z.string().nullable(), + headerImageAltText: z.string().nullable(), + bodyImagePath: z.string().nullable(), + bodyImageAltText: z.string().nullable() }); export const newsSchema = z.object({ @@ -32,3 +36,11 @@ export const makeRequestUrl = (pageNo: number, urlOrigin: string) => { }); return new URL(`/api/news?${query}`, urlOrigin); }; + +export const getImageSrc = (path: string | null) => + path ? new URL(path, PUBLIC_CDN_URL).href : undefined; + +export const getPageNoFromParams = (params: URLSearchParams) => { + const pageNoStr = params.get('page') || '1'; + return Number.parseInt(pageNoStr) || 1; +}; diff --git a/Website/src/routes/(main)/news/pagination.svelte b/Website/src/routes/(main)/news/pagination.svelte index 6d14714a3..dca37d916 100644 --- a/Website/src/routes/(main)/news/pagination.svelte +++ b/Website/src/routes/(main)/news/pagination.svelte @@ -8,22 +8,21 @@ export let currentPage: number; export let numPages: number; - $: previousPagePath = $page.url.pathname.replace( - currentPage.toString(), - (currentPage - 1).toString() - ); - $: nextPagePath = $page.url.pathname.replace( - currentPage.toString(), - (currentPage + 1).toString() - ); + const getPagePath = (pageNo: number) => { + const params = new URLSearchParams($page.url.searchParams); + params.set('page', pageNo.toString()); - const scrollToTop = () => window.scrollTo(0, 0); + return `?${params}`; + }; + + $: previousPagePath = getPagePath(currentPage - 1); + $: nextPagePath = getPagePath(currentPage + 1);
{#if currentPage > 1} - @@ -34,7 +33,7 @@ {currentPage} / {numPages}

{#if currentPage < numPages} - diff --git a/Website/src/routes/(main)/routes.ts b/Website/src/routes/(main)/routes.ts index ec6ae6d99..133faca56 100644 --- a/Website/src/routes/(main)/routes.ts +++ b/Website/src/routes/(main)/routes.ts @@ -23,7 +23,7 @@ export const routeGroups: RouteGroup[] = [ title: 'Information', routes: [ { title: 'Home', href: '/', icon: House }, - { title: 'News', href: '/news/1', icon: Newspaper } + { title: 'News', href: '/news', icon: Newspaper } ] }, diff --git a/Website/src/routes/webview/news/[pageNo=integer]/+page.svelte b/Website/src/routes/webview/news/+page.svelte similarity index 92% rename from Website/src/routes/webview/news/[pageNo=integer]/+page.svelte rename to Website/src/routes/webview/news/+page.svelte index 2a8cf8076..a166916de 100644 --- a/Website/src/routes/webview/news/[pageNo=integer]/+page.svelte +++ b/Website/src/routes/webview/news/+page.svelte @@ -8,17 +8,17 @@ import GitHub from '$main/(home)/icons/github.svelte'; import Patreon from '$main/(home)/icons/patreon.svelte'; import NewsItem from '$main/news/item.svelte'; - import { lastReadKey, pageSize } from '$main/news/news.ts'; + import { getPageNoFromParams, lastReadKey, pageSize } from '$main/news/news.ts'; import NewsPagination from '$main/news/pagination.svelte'; - import IconButton from '../iconButton.svelte'; import type { PageData } from './$types'; + import IconButton from './iconButton.svelte'; let lastRead: Date; export let data: PageData; - $: currentPage = Number.parseInt($page.params.pageNo) || 1; + $: currentPage = getPageNoFromParams($page.url.searchParams); onMount(() => { // Required for showing the Mercurial Gauntlet rewards page, which unconditionally routes to diff --git a/Website/src/routes/webview/news/[pageNo=integer]/+page.ts b/Website/src/routes/webview/news/+page.ts similarity index 51% rename from Website/src/routes/webview/news/[pageNo=integer]/+page.ts rename to Website/src/routes/webview/news/+page.ts index a155a2098..baa08d794 100644 --- a/Website/src/routes/webview/news/[pageNo=integer]/+page.ts +++ b/Website/src/routes/webview/news/+page.ts @@ -1,9 +1,9 @@ -import { makeRequestUrl, newsSchema } from '$main/news/news.ts'; +import { getPageNoFromParams, makeRequestUrl, newsSchema } from '$main/news/news.ts'; import type { PageLoad } from './$types'; -export const load: PageLoad = async ({ fetch, params, url }) => { - const pageNo = Number.parseInt(params.pageNo) || 1; +export const load: PageLoad = async ({ fetch, url }) => { + const pageNo = getPageNoFromParams(url.searchParams) || 1; const requestUrl = makeRequestUrl(pageNo, url.origin); const response = await fetch(requestUrl); diff --git a/Website/src/routes/webview/news/detail/[id=integer]/+page.svelte b/Website/src/routes/webview/news/detail/[id=integer]/+page.svelte index e337e7d6a..e5363d78b 100644 --- a/Website/src/routes/webview/news/detail/[id=integer]/+page.svelte +++ b/Website/src/routes/webview/news/detail/[id=integer]/+page.svelte @@ -3,19 +3,23 @@ import ChevronLeft from 'lucide-svelte/icons/chevron-left'; import { afterNavigate } from '$app/navigation'; + import { getImageSrc } from '$main/news/news.ts'; import { Button } from '$shadcn/components/ui/button'; import type { PageData } from './$types'; - let previousPage: string = '/webview/news/1'; + let previousPage: string = '/webview/news'; afterNavigate(({ from }) => { - previousPage = from?.url.pathname || previousPage; + previousPage = from ? from.url.pathname + from.url.search : previousPage; }); export let data: PageData; let item = data.newsItem; + + $: headerImageSrc = getImageSrc(item.headerImagePath); + $: bodyImageSrc = getImageSrc(item.bodyImagePath);
@@ -24,9 +28,14 @@ Back to news
- {#if item.headerImageSrc} + {#if headerImageSrc}
- + {item.headerImageAltText}

{/if} @@ -45,8 +54,8 @@

{@html item.description}


- {#if item.bodyImageSrc} - + {#if bodyImageSrc} + {item.bodyImageAltText} {/if}
diff --git a/Website/tests/__screenshots__/news.test.ts/displays-correctly-1.png b/Website/tests/__screenshots__/news.test.ts/displays-correctly-1.png index a690ffacc..8b6d61543 100644 Binary files a/Website/tests/__screenshots__/news.test.ts/displays-correctly-1.png and b/Website/tests/__screenshots__/news.test.ts/displays-correctly-1.png differ diff --git a/Website/tests/__screenshots__/news.test.ts/displays-correctly-on-mobile-1.png b/Website/tests/__screenshots__/news.test.ts/displays-correctly-on-mobile-1.png index a3c506b52..e978c9487 100644 Binary files a/Website/tests/__screenshots__/news.test.ts/displays-correctly-on-mobile-1.png and b/Website/tests/__screenshots__/news.test.ts/displays-correctly-on-mobile-1.png differ diff --git a/Website/tests/__screenshots__/news.test.ts/webview-displays-correctly-1.png b/Website/tests/__screenshots__/news.test.ts/webview-displays-correctly-1.png new file mode 100644 index 000000000..e8404a540 Binary files /dev/null and b/Website/tests/__screenshots__/news.test.ts/webview-displays-correctly-1.png differ diff --git a/Website/tests/news.test.ts b/Website/tests/news.test.ts index d9c3de9ca..0632ee715 100644 --- a/Website/tests/news.test.ts +++ b/Website/tests/news.test.ts @@ -36,7 +36,7 @@ test('displays correctly on mobile', async ({ page }) => { }); test('change page', async ({ page }) => { - await page.goto('/news/1'); + await page.goto('/news'); await page.getByRole('link', { name: 'Next' }).click(); @@ -49,17 +49,50 @@ test('change page', async ({ page }) => { await expect(page.getByRole('heading', { name: 'Game updated!' })).toBeVisible(); }); -test('displays correctly for webview', async ({ page }) => { - await page.setViewportSize(devices['iPhone 13'].viewport); +test.describe('webview', () => { + test('displays correctly', async ({ page }) => { + await page.setViewportSize(devices['iPhone 13'].viewport); - await page.goto('/webview/news/1'); + await page.goto('/webview/news'); - await expect(page.getByRole('heading', { name: 'Welcome to Dawnshard' })).toBeVisible(); + await expect(page.getByRole('heading', { name: 'Welcome to Dawnshard' })).toBeVisible(); - await page.waitForFunction(() => { - const images = Array.from(document.querySelectorAll('img')); - return images.every((img) => img.complete); + await page.waitForFunction(() => { + const images = Array.from(document.querySelectorAll('img')); + return images.every((img) => img.complete); + }); + + await expect(page).toHaveScreenshot(); }); - await expect(page).toHaveScreenshot(); + test('change page', async ({ page }) => { + await page.goto('/webview/news'); + + await page.getByRole('link', { name: 'Next' }).click(); + + await expect(page.getByRole('heading', { name: 'Paging works' })).toBeVisible(); + + await page.getByRole('link', { name: 'Previous' }).click(); + + await expect(page.getByRole('heading', { name: 'Game updated!' })).toBeVisible(); + }); + + test('open details', async ({ page }) => { + await page.goto('/webview/news'); + + await expect(page.getByRole('heading', { name: 'Game updated!' })).toBeVisible(); + + await page.getByRole('link', { name: 'Game updated!' }).click(); + + await expect(page).toHaveURL(/.*webview\/news\/detail\/6/); + + await expect(page.getByRole('heading', { name: 'Game updated!' })).toBeVisible(); + await expect(page.getByText('We have done a very large update!')).toBeVisible(); + + await page.getByRole('link', { name: 'Back to news' }).click(); + + expect(page).toHaveURL(/.*webview\/news/); + + await expect(page.getByRole('heading', { name: 'Really long story' })).toBeVisible(); + }); });