Iceshrimp.NET/Iceshrimp.Backend/Controllers/Mastodon/AnnouncementController.cs
Kopper 89060599eb
[backend] Implement inline media
Inline media can be created by:

1. Attach media to note as usual
2. Copy media URL (public one, for remote instances)
3. Use the new $[media url ] MFM extension to place it wherever you
   wish. (The trailing space is necessary as the parser currently
   treats the closing ] as a part of the URL)

The Iceshrimp frontend may make this easier later on (by having a
"copy inline MFM" button on attachments, maybe?)

Federates as <img>, <video>, <audio>, or <a download> HTML tags
depending on the media type for interoperability. (<a download> is
not handled for incoming media yet).

The media will also be present in the attachments field, both as a
fallback for instance software that do not support inline media,
but also for MFM federation to discover which media it is allowed to
embed (and metadata like alt text and sensitive-ness). This is not
required for remote instances sending inline media, as it will be
extracted out from the HTML.

The Iceshrimp frontend does not render inline media yet. That is
blocked on #67.
2024-12-13 22:19:30 +01:00

103 lines
No EOL
4.2 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Helpers;
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.EntityFrameworkCore;
namespace Iceshrimp.Backend.Controllers.Mastodon;
[MastodonApiController]
[Route("/api/v1/announcements")]
[EnableCors("mastodon")]
[Authenticate]
[EnableRateLimiting("sliding")]
[Produces(MediaTypeNames.Application.Json)]
public class AnnouncementController(DatabaseContext db, MfmConverter mfmConverter) : ControllerBase
{
[HttpGet]
[Authorize]
[ProducesResults(HttpStatusCode.OK)]
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall", Justification = "Projectables")]
public async Task<IEnumerable<AnnouncementEntity>> GetAnnouncements(
[FromQuery(Name = "with_dismissed")] bool withDismissed
)
{
var user = HttpContext.GetUserOrFail();
var announcements = db.Announcements.AsQueryable();
if (!withDismissed)
announcements = announcements.Where(p => p.IsReadBy(user));
var res = await announcements.OrderByDescending(p => p.UpdatedAt ?? p.CreatedAt)
.Select(p => new AnnouncementEntity
{
Id = p.Id,
PublishedAt = p.CreatedAt.ToStringIso8601Like(),
UpdatedAt = (p.UpdatedAt ?? p.CreatedAt).ToStringIso8601Like(),
IsRead = p.IsReadBy(user),
Content = $"""
**{p.Title}**
{p.Text}
""",
Mentions = new List<MentionEntity>(), //TODO
Emoji = new List<EmojiEntity>() //TODO
})
.ToListAsync();
await res.Select(async p => p.Content = (await mfmConverter.ToHtmlAsync(p.Content, [], null)).Html).AwaitAllAsync();
return res;
}
[HttpPost("{id}/dismiss")]
[Authorize("write:accounts")]
[OverrideResultType<object>]
[ProducesResults(HttpStatusCode.OK)]
[ProducesErrors(HttpStatusCode.NotFound)]
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall", Justification = "Projectables")]
public async Task<object> DismissAnnouncement(string id)
{
var user = HttpContext.GetUserOrFail();
var announcement = await db.Announcements.FirstOrDefaultAsync(p => p.Id == id) ??
throw GracefulException.NotFound("Announcement not found");
if (await db.Announcements.AnyAsync(p => p == announcement && !p.IsReadBy(user)))
{
var announcementRead = new AnnouncementRead
{
Id = IdHelpers.GenerateSnowflakeId(),
CreatedAt = DateTime.UtcNow,
Announcement = announcement,
User = user
};
await db.AnnouncementReads.AddAsync(announcementRead);
await db.SaveChangesAsync();
}
return new object();
}
[HttpPut("{id}/reactions/{name}")]
[Authorize("write:favourites")]
[ProducesErrors(HttpStatusCode.NotImplemented)]
public IActionResult ReactToAnnouncement(string id, string name) =>
throw new GracefulException(HttpStatusCode.NotImplemented,
"Iceshrimp.NET does not support this endpoint due to database schema differences to Mastodon");
[HttpDelete("{id}/reactions/{name}")]
[Authorize("write:favourites")]
[ProducesErrors(HttpStatusCode.NotImplemented)]
public IActionResult RemoveAnnouncementReaction(string id, string name) =>
throw new GracefulException(HttpStatusCode.NotImplemented,
"Iceshrimp.NET does not support this endpoint due to database schema differences to Mastodon");
}