diff --git a/Iceshrimp.Backend/Controllers/Mastodon/MediaController.cs b/Iceshrimp.Backend/Controllers/Mastodon/MediaController.cs index fe488f22..6c222dec 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/MediaController.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/MediaController.cs @@ -77,8 +77,9 @@ public class MediaController(DriveService driveSvc, DatabaseContext db) : Contro Blurhash = file.Blurhash, Description = file.Comment, PreviewUrl = file.ThumbnailUrl, - RemoteUrl = file.Uri - //Metadata = TODO + RemoteUrl = file.Uri, + Sensitive = file.IsSensitive, + //Metadata = TODO, }; } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs index 99aee605..08f9eba2 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs @@ -129,6 +129,57 @@ public class NoteRenderer( return res; } + public async Task> RenderHistoryAsync(Note note) + { + var edits = await db.NoteEdits.Where(p => p.Note == note).OrderBy(p => p.Id).ToListAsync(); + edits.Add(RenderEdit(note)); + + var attachments = await GetAttachments(note.FileIds.Concat(edits.SelectMany(p => p.FileIds))); + var mentions = await GetMentions([note]); + var mentionedUsers = mentions.Select(p => new Note.MentionedUser + { + Host = p.Host ?? config.Value.AccountDomain, + Uri = p.Uri, + Username = p.Username, + Url = p.Url + }) + .ToList(); + + var account = await userRenderer.RenderAsync(note.User); + var lastDate = note.CreatedAt; + + List history = []; + foreach (var edit in edits) + { + var files = attachments.Where(p => edit.FileIds.Contains(p.Id)).ToList(); + var entry = new StatusEdit + { + Account = account, + Content = await mfmConverter.ToHtmlAsync(edit.Text ?? "", mentionedUsers, note.UserHost), + CreatedAt = lastDate.ToStringIso8601Like(), + Emojis = [], + IsSensitive = files.Any(p => p.Sensitive), + Attachments = files, + Poll = null, + ContentWarning = edit.Cw ?? "" + }; + history.Add(entry); + } + + return history; + } + + private NoteEdit RenderEdit(Note note) + { + return new NoteEdit + { + Text = note.Text, + Cw = note.Cw, + FileIds = note.FileIds, + UpdatedAt = note.UpdatedAt ?? note.CreatedAt + }; + } + private static List GetFilterResult((Filter filter, string keyword)? filtered) { if (filtered == null) return []; @@ -161,7 +212,28 @@ public class NoteRenderer( Description = f.Comment, Metadata = null, RemoteUrl = f.Uri, - Type = AttachmentEntity.GetType(f.Type) + Type = AttachmentEntity.GetType(f.Type), + Sensitive = f.IsSensitive + }) + .ToListAsync(); + } + + private async Task> GetAttachments(IEnumerable fileIds) + { + var ids = fileIds.Distinct().ToList(); + if (ids.Count == 0) return []; + return await db.DriveFiles.Where(p => ids.Contains(p.Id)) + .Select(f => new AttachmentEntity + { + Id = f.Id, + Url = f.PublicUrl, + Blurhash = f.Blurhash, + PreviewUrl = f.PublicThumbnailUrl, + Description = f.Comment, + Metadata = null, + RemoteUrl = f.Uri, + Type = AttachmentEntity.GetType(f.Type), + Sensitive = f.IsSensitive }) .ToListAsync(); } diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/AttachmentEntity.cs b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/AttachmentEntity.cs index 32501028..72abd60c 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/AttachmentEntity.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/AttachmentEntity.cs @@ -1,10 +1,12 @@ using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; +using JI = System.Text.Json.Serialization.JsonIgnoreAttribute; namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities; public class AttachmentEntity { - public required AttachmentType Type; + [JI] public required bool Sensitive; + [JI] public required AttachmentType Type; [J("id")] public required string Id { get; set; } [J("url")] public required string Url { get; set; } [J("remote_url")] public string? RemoteUrl { get; set; } diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/StatusEntity.cs b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/StatusEntity.cs index 3f5c4543..26ad91fd 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/StatusEntity.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Schemas/Entities/StatusEntity.cs @@ -95,4 +95,16 @@ public class StatusSource [J("id")] public required string Id { get; set; } [J("text")] public required string Text { get; set; } [J("spoiler_text")] public required string ContentWarning { get; set; } +} + +public class StatusEdit +{ + [J("content")] public required string? Content { get; set; } + [J("spoiler_text")] public required string ContentWarning { get; set; } + [J("sensitive")] public required bool IsSensitive { get; set; } + [J("created_at")] public required string CreatedAt { get; set; } + [J("account")] public required AccountEntity Account { get; set; } + [J("poll")] public required PollEntity? Poll { get; set; } + [J("media_attachments")] public required List Attachments { get; set; } + [J("emojis")] public required List Emojis { get; set; } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Controllers/Mastodon/StatusController.cs b/Iceshrimp.Backend/Controllers/Mastodon/StatusController.cs index 2c13eef4..1bf779a9 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/StatusController.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/StatusController.cs @@ -552,4 +552,22 @@ public class StatusController( return Ok(res); } + + [HttpGet("{id}/history")] + [Authenticate("read:statuses")] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable))] + [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(MastodonErrorResponse))] + public async Task GetNoteEditHistory(string id) + { + var user = HttpContext.GetUser(); + var note = await db.Notes + .Where(p => p.Id == id) + .EnsureVisibleFor(user) + .FilterHidden(user, db, filterMutes: false) + .FirstOrDefaultAsync() ?? + throw GracefulException.RecordNotFound(); + + var res = await noteRenderer.RenderHistoryAsync(note); + return Ok(res); + } } \ No newline at end of file