From eaeaa28c606796d0eb81536e0d5092a787bf1bd8 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 4 Dec 2025 00:19:36 +0800 Subject: [PATCH] :sparkles: Sticker icon --- DysonNetwork.Shared/Models/CustomApp.cs | 2 +- DysonNetwork.Shared/Models/FilePool.cs | 2 +- DysonNetwork.Shared/Models/Sticker.cs | 43 +++++-------- .../Migrations/AppDatabaseModelSnapshot.cs | 10 ++-- .../Sticker/StickerController.cs | 60 +++++++++++++++++-- DysonNetwork.Sphere/Sticker/StickerService.cs | 19 +++--- 6 files changed, 88 insertions(+), 48 deletions(-) diff --git a/DysonNetwork.Shared/Models/CustomApp.cs b/DysonNetwork.Shared/Models/CustomApp.cs index 32731ae..50dd21f 100644 --- a/DysonNetwork.Shared/Models/CustomApp.cs +++ b/DysonNetwork.Shared/Models/CustomApp.cs @@ -38,7 +38,7 @@ public class SnCustomApp : ModelBase, IIdentifiedResource [NotMapped] public SnDeveloper Developer => Project.Developer; - [NotMapped] public string ResourceIdentifier => "custom-app:" + Id; + [NotMapped] public string ResourceIdentifier => "developer.app:" + Id; public Proto.CustomApp ToProto() { diff --git a/DysonNetwork.Shared/Models/FilePool.cs b/DysonNetwork.Shared/Models/FilePool.cs index c050d02..1e16146 100644 --- a/DysonNetwork.Shared/Models/FilePool.cs +++ b/DysonNetwork.Shared/Models/FilePool.cs @@ -50,5 +50,5 @@ public class FilePool : ModelBase, IIdentifiedResource public Guid? AccountId { get; set; } - public string ResourceIdentifier => $"file-pool/{Id}"; + public string ResourceIdentifier => $"file.pool:{Id}"; } diff --git a/DysonNetwork.Shared/Models/Sticker.cs b/DysonNetwork.Shared/Models/Sticker.cs index 44331a8..f32a0f9 100644 --- a/DysonNetwork.Shared/Models/Sticker.cs +++ b/DysonNetwork.Shared/Models/Sticker.cs @@ -11,47 +11,33 @@ public class SnSticker : ModelBase, IIdentifiedResource { public Guid Id { get; set; } = Guid.NewGuid(); - [MaxLength(128)] - public string Slug { get; set; } = null!; - - // Outdated fields, for backward compability - [MaxLength(32)] - public string? ImageId { get; set; } - - [Column(TypeName = "jsonb")] - public SnCloudFileReferenceObject? Image { get; set; } = null!; + [MaxLength(128)] public string Slug { get; set; } = null!; + [Column(TypeName = "jsonb")] public SnCloudFileReferenceObject Image { get; set; } = null!; public Guid PackId { get; set; } + [IgnoreMember] [JsonIgnore] public StickerPack Pack { get; set; } = null!; - [JsonIgnore] - public StickerPack Pack { get; set; } = null!; - - public string ResourceIdentifier => $"sticker/{Id}"; + public string ResourceIdentifier => $"sticker:{Id}"; } [Index(nameof(Prefix), IsUnique = true)] -public class StickerPack : ModelBase +public class StickerPack : ModelBase, IIdentifiedResource { public Guid Id { get; set; } = Guid.NewGuid(); - [MaxLength(1024)] - public string Name { get; set; } = null!; + [Column(TypeName = "jsonb")] public SnCloudFileReferenceObject? Icon { get; set; } + [MaxLength(1024)] public string Name { get; set; } = null!; + [MaxLength(4096)] public string Description { get; set; } = string.Empty; + [MaxLength(128)] public string Prefix { get; set; } = null!; - [MaxLength(4096)] - public string Description { get; set; } = string.Empty; - - [MaxLength(128)] - public string Prefix { get; set; } = null!; - - [IgnoreMember] public List Stickers { get; set; } = []; - [IgnoreMember] - [JsonIgnore] - public List Ownerships { get; set; } = []; + [IgnoreMember] [JsonIgnore] public List Ownerships { get; set; } = []; public Guid PublisherId { get; set; } public SnPublisher Publisher { get; set; } = null!; + + public string ResourceIdentifier => $"sticker.pack:{Id}"; } public class StickerPackOwnership : ModelBase @@ -62,6 +48,5 @@ public class StickerPackOwnership : ModelBase public StickerPack Pack { get; set; } = null!; public Guid AccountId { get; set; } - [NotMapped] - public SnAccount Account { get; set; } = null!; -} + [NotMapped] public SnAccount Account { get; set; } = null!; +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs index 9987a40..513cffd 100644 --- a/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs +++ b/DysonNetwork.Sphere/Migrations/AppDatabaseModelSnapshot.cs @@ -1147,14 +1147,10 @@ namespace DysonNetwork.Sphere.Migrations .HasColumnName("deleted_at"); b.Property("Image") + .IsRequired() .HasColumnType("jsonb") .HasColumnName("image"); - b.Property("ImageId") - .HasMaxLength(32) - .HasColumnType("character varying(32)") - .HasColumnName("image_id"); - b.Property("PackId") .HasColumnType("uuid") .HasColumnName("pack_id"); @@ -1202,6 +1198,10 @@ namespace DysonNetwork.Sphere.Migrations .HasColumnType("character varying(4096)") .HasColumnName("description"); + b.Property("Icon") + .HasColumnType("jsonb") + .HasColumnName("icon"); + b.Property("Name") .IsRequired() .HasMaxLength(1024) diff --git a/DysonNetwork.Sphere/Sticker/StickerController.cs b/DysonNetwork.Sphere/Sticker/StickerController.cs index 808ef83..2e0300c 100644 --- a/DysonNetwork.Sphere/Sticker/StickerController.cs +++ b/DysonNetwork.Sphere/Sticker/StickerController.cs @@ -15,7 +15,8 @@ public class StickerController( AppDatabase db, StickerService st, Publisher.PublisherService ps, - FileService.FileServiceClient files + FileService.FileServiceClient files, + FileReferenceService.FileReferenceServiceClient fileRefs ) : ControllerBase { private async Task _CheckStickerPackPermissions( @@ -114,6 +115,7 @@ public class StickerController( public class StickerPackRequest { + public string? IconId { get; set; } [MaxLength(1024)] public string? Name { get; set; } [MaxLength(4096)] public string? Description { get; set; } [MaxLength(128)] public string? Prefix { get; set; } @@ -147,8 +149,28 @@ public class StickerController( PublisherId = publisher.Id }; + if (request.IconId is not null) + { + var file = await files.GetFileAsync(new GetFileRequest { Id = request.IconId }); + if (file is null) + return BadRequest("Icon not found."); + + pack.Icon = SnCloudFileReferenceObject.FromProtoValue(file); + } + db.StickerPacks.Add(pack); await db.SaveChangesAsync(); + + if (pack.Icon is not null) + { + await fileRefs.CreateReferenceAsync(new CreateReferenceRequest + { + FileId = pack.Icon.Id, + Usage = StickerService.StickerPackUsageIdentifier, + ResourceId = pack.ResourceIdentifier + }); + } + return Ok(pack); } @@ -179,6 +201,32 @@ public class StickerController( if (request.Prefix is not null) pack.Prefix = request.Prefix; + if (request.IconId is not null) + { + var file = await files.GetFileAsync(new GetFileRequest { Id = request.IconId }); + if (file is null) + return BadRequest("Icon not found."); + + if (file.Id != pack.Icon?.Id) + { + await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest + { ResourceId = pack.ResourceIdentifier, Usage = StickerService.StickerPackUsageIdentifier }); + + pack.Icon = SnCloudFileReferenceObject.FromProtoValue(file); + await fileRefs.CreateReferenceAsync(new CreateReferenceRequest + { + FileId = pack.Icon.Id, + Usage = StickerService.StickerPackUsageIdentifier, + ResourceId = pack.ResourceIdentifier + }); + } + else + { + // Still update the column in case user want to sync the changes of the file meta + pack.Icon = SnCloudFileReferenceObject.FromProtoValue(file); + } + } + db.StickerPacks.Update(pack); await db.SaveChangesAsync(); return Ok(pack); @@ -239,7 +287,11 @@ public class StickerController( } [HttpGet("search")] - public async Task>> SearchSticker([FromQuery] string query, [FromQuery] int take = 10, [FromQuery] int offset = 0) + public async Task>> SearchSticker( + [FromQuery] string query, + [FromQuery] int take = 10, + [FromQuery] int offset = 0 + ) { var queryable = db.Stickers .Include(s => s.Pack) @@ -300,7 +352,6 @@ public class StickerController( var file = await files.GetFileAsync(new GetFileRequest { Id = request.ImageId }); if (file is null) return BadRequest("Image not found"); - sticker.ImageId = request.ImageId; sticker.Image = SnCloudFileReferenceObject.FromProtoValue(file); } @@ -367,7 +418,6 @@ public class StickerController( var sticker = new SnSticker { Slug = request.Slug, - ImageId = file.Id, Image = SnCloudFileReferenceObject.FromProtoValue(file), Pack = pack }; @@ -437,4 +487,4 @@ public class StickerController( return NoContent(); } -} +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Sticker/StickerService.cs b/DysonNetwork.Sphere/Sticker/StickerService.cs index d5ac1c6..5038c43 100644 --- a/DysonNetwork.Sphere/Sticker/StickerService.cs +++ b/DysonNetwork.Sphere/Sticker/StickerService.cs @@ -12,6 +12,7 @@ public class StickerService( ) { public const string StickerFileUsageIdentifier = "sticker"; + public const string StickerPackUsageIdentifier = "sticker.pack"; private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(15); @@ -36,7 +37,8 @@ public class StickerService( { if (newImage is not null) { - await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest { ResourceId = sticker.ResourceIdentifier }); + await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest + { ResourceId = sticker.ResourceIdentifier }); sticker.Image = newImage; @@ -63,7 +65,8 @@ public class StickerService( var stickerResourceId = $"sticker:{sticker.Id}"; // Delete all file references for this sticker - await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest { ResourceId = stickerResourceId }); + await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest + { ResourceId = stickerResourceId }); db.Stickers.Remove(sticker); await db.SaveChangesAsync(); @@ -82,11 +85,12 @@ public class StickerService( // Delete all file references for each sticker in the pack foreach (var stickerResourceId in stickers.Select(sticker => $"sticker:{sticker.Id}")) - await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest { ResourceId = stickerResourceId }); + await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest + { ResourceId = stickerResourceId }); // Delete any references for the pack itself - var packResourceId = $"stickerpack:{pack.Id}"; - await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest { ResourceId = packResourceId }); + await fileRefs.DeleteResourceReferencesAsync(new DeleteResourceReferencesRequest + { ResourceId = pack.ResourceIdentifier }); db.Stickers.RemoveRange(stickers); db.StickerPacks.Remove(pack); @@ -119,7 +123,8 @@ public class StickerService( { var packPart = identifierParts[0]; var stickerPart = identifierParts[1]; - query = query.Where(e => EF.Functions.ILike(e.Pack.Prefix, packPart) && EF.Functions.ILike(e.Slug, stickerPart)); + query = query.Where(e => + EF.Functions.ILike(e.Pack.Prefix, packPart) && EF.Functions.ILike(e.Slug, stickerPart)); } else { @@ -144,4 +149,4 @@ public class StickerService( await cache.RemoveAsync($"sticker:lookup:{sticker.Id}"); await cache.RemoveAsync($"sticker:lookup:{sticker.Pack.Prefix}{sticker.Slug}"); } -} +} \ No newline at end of file