[backend/api] Respect note filters (ISH-243)
This commit is contained in:
parent
01e640e8e5
commit
e4c4ec186c
6 changed files with 65 additions and 26 deletions
|
@ -69,7 +69,8 @@ public class NoteController(
|
||||||
.PrecomputeNoteContextVisibilities(user)
|
.PrecomputeNoteContextVisibilities(user)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user));
|
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user,
|
||||||
|
Filter.FilterContext.Threads));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/descendants")]
|
[HttpGet("{id}/descendants")]
|
||||||
|
@ -97,7 +98,8 @@ public class NoteController(
|
||||||
.PrecomputeNoteContextVisibilities(user)
|
.PrecomputeNoteContextVisibilities(user)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user));
|
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user,
|
||||||
|
Filter.FilterContext.Threads));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/reactions/{name}")]
|
[HttpGet("{id}/reactions/{name}")]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
using Iceshrimp.Shared.Schemas;
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
@ -9,13 +10,28 @@ namespace Iceshrimp.Backend.Controllers.Renderers;
|
||||||
|
|
||||||
public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiService emojiSvc)
|
public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiService emojiSvc)
|
||||||
{
|
{
|
||||||
public async Task<NoteResponse> RenderOne(Note note, User? localUser, NoteRendererDto? data = null)
|
public async Task<NoteResponse> RenderOne(
|
||||||
|
Note note, User? user, Filter.FilterContext? filterContext = null, NoteRendererDto? data = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var res = await RenderBaseInternal(note, localUser, data);
|
var res = await RenderBaseInternal(note, user, data);
|
||||||
|
|
||||||
var renote = note.Renote is { IsPureRenote: true } ? await RenderRenote(note.Renote, localUser, data) : null;
|
var renote = note.Renote is { IsPureRenote: true } ? await RenderRenote(note.Renote, user, data) : null;
|
||||||
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, localUser, data) : null;
|
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, user, data) : null;
|
||||||
var reply = note.Reply != null ? await RenderBase(note.Reply, localUser, data) : null;
|
var reply = note.Reply != null ? await RenderBase(note.Reply, user, data) : null;
|
||||||
|
|
||||||
|
var filters = data?.Filters ?? await GetFilters(user, filterContext);
|
||||||
|
var filtered = FilterHelper.IsFiltered([note, note.Reply, note.Renote, note.Renote?.Renote], filters);
|
||||||
|
|
||||||
|
if (filtered.HasValue)
|
||||||
|
{
|
||||||
|
res.Filtered = new NoteFilteredSchema
|
||||||
|
{
|
||||||
|
Id = filtered.Value.filter.Id,
|
||||||
|
Keyword = filtered.Value.keyword,
|
||||||
|
Hide = filtered.Value.filter.Action == Filter.FilterAction.Hide
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
res.Renote = renote;
|
res.Renote = renote;
|
||||||
res.RenoteId = note.RenoteId;
|
res.RenoteId = note.RenoteId;
|
||||||
|
@ -27,10 +43,10 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<NoteWithQuote> RenderRenote(Note note, User? localUser, NoteRendererDto? data = null)
|
private async Task<NoteWithQuote> RenderRenote(Note note, User? user, NoteRendererDto? data = null)
|
||||||
{
|
{
|
||||||
var res = await RenderBaseInternal(note, localUser, data);
|
var res = await RenderBaseInternal(note, user, data);
|
||||||
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, localUser, data) : null;
|
var quote = note.Renote is { IsPureRenote: false } ? await RenderBase(note.Renote, user, data) : null;
|
||||||
|
|
||||||
res.Quote = quote;
|
res.Quote = quote;
|
||||||
res.QuoteId = note.RenoteId;
|
res.QuoteId = note.RenoteId;
|
||||||
|
@ -41,11 +57,11 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
private async Task<NoteBase> RenderBase(Note note, User? localUser, NoteRendererDto? data = null)
|
private async Task<NoteBase> RenderBase(Note note, User? localUser, NoteRendererDto? data = null)
|
||||||
=> await RenderBaseInternal(note, localUser, data);
|
=> await RenderBaseInternal(note, localUser, data);
|
||||||
|
|
||||||
private async Task<NoteResponse> RenderBaseInternal(Note note, User? localUser, NoteRendererDto? data = null)
|
private async Task<NoteResponse> RenderBaseInternal(Note note, User? user, NoteRendererDto? data = null)
|
||||||
{
|
{
|
||||||
var user = (data?.Users ?? await GetUsers([note])).First(p => p.Id == note.User.Id);
|
var noteUser = (data?.Users ?? await GetUsers([note])).First(p => p.Id == note.User.Id);
|
||||||
var attachments = (data?.Attachments ?? await GetAttachments([note])).Where(p => note.FileIds.Contains(p.Id));
|
var attachments = (data?.Attachments ?? await GetAttachments([note])).Where(p => note.FileIds.Contains(p.Id));
|
||||||
var reactions = (data?.Reactions ?? await GetReactions([note], localUser)).Where(p => p.NoteId == note.Id);
|
var reactions = (data?.Reactions ?? await GetReactions([note], user)).Where(p => p.NoteId == note.Id);
|
||||||
|
|
||||||
return new NoteResponse
|
return new NoteResponse
|
||||||
{
|
{
|
||||||
|
@ -54,7 +70,7 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
Text = note.Text,
|
Text = note.Text,
|
||||||
Cw = note.Cw,
|
Cw = note.Cw,
|
||||||
Visibility = RenderVisibility(note.Visibility),
|
Visibility = RenderVisibility(note.Visibility),
|
||||||
User = user,
|
User = noteUser,
|
||||||
Attachments = attachments.ToList(),
|
Attachments = attachments.ToList(),
|
||||||
Reactions = reactions.ToList()
|
Reactions = reactions.ToList()
|
||||||
};
|
};
|
||||||
|
@ -130,7 +146,15 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<NoteResponse>> RenderMany(IEnumerable<Note> notes, User? user)
|
private async Task<List<Filter>> GetFilters(User? user, Filter.FilterContext? filterContext)
|
||||||
|
{
|
||||||
|
if (filterContext == null) return [];
|
||||||
|
return await db.Filters.Where(p => p.User == user && p.Contexts.Contains(filterContext.Value)).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<NoteResponse>> RenderMany(
|
||||||
|
IEnumerable<Note> notes, User? user, Filter.FilterContext? filterContext = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var notesList = notes.ToList();
|
var notesList = notes.ToList();
|
||||||
if (notesList.Count == 0) return [];
|
if (notesList.Count == 0) return [];
|
||||||
|
@ -139,10 +163,11 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
{
|
{
|
||||||
Users = await GetUsers(allNotes),
|
Users = await GetUsers(allNotes),
|
||||||
Attachments = await GetAttachments(allNotes),
|
Attachments = await GetAttachments(allNotes),
|
||||||
Reactions = await GetReactions(allNotes, user)
|
Reactions = await GetReactions(allNotes, user),
|
||||||
|
Filters = await GetFilters(user, filterContext)
|
||||||
};
|
};
|
||||||
|
|
||||||
return await notesList.Select(p => RenderOne(p, user, data)).AwaitAllAsync();
|
return await notesList.Select(p => RenderOne(p, user, filterContext, data)).AwaitAllAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NoteRendererDto
|
public class NoteRendererDto
|
||||||
|
@ -150,5 +175,6 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
public List<NoteAttachment>? Attachments;
|
public List<NoteAttachment>? Attachments;
|
||||||
public List<NoteReactionSchema>? Reactions;
|
public List<NoteReactionSchema>? Reactions;
|
||||||
public List<UserResponse>? Users;
|
public List<UserResponse>? Users;
|
||||||
|
public List<Filter>? Filters;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -62,7 +62,7 @@ public class NotificationRenderer(
|
||||||
private async Task<List<NoteResponse>> GetNotes(IEnumerable<Notification> notifications, User user)
|
private async Task<List<NoteResponse>> GetNotes(IEnumerable<Notification> notifications, User user)
|
||||||
{
|
{
|
||||||
var notes = notifications.Select(p => p.Note).OfType<Note>().DistinctBy(p => p.Id);
|
var notes = notifications.Select(p => p.Note).OfType<Note>().DistinctBy(p => p.Id);
|
||||||
return await noteRenderer.RenderMany(notes, user).ToListAsync();
|
return await noteRenderer.RenderMany(notes, user, Filter.FilterContext.Notifications).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<NotificationResponse>> RenderMany(IEnumerable<Notification> notifications, User user)
|
public async Task<IEnumerable<NotificationResponse>> RenderMany(IEnumerable<Notification> notifications, User user)
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
@ -39,6 +40,6 @@ public class TimelineController(DatabaseContext db, CacheService cache, NoteRend
|
||||||
.PrecomputeVisibilities(user)
|
.PrecomputeVisibilities(user)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user));
|
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), user, Filter.FilterContext.Home));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Shared.Schemas;
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -69,6 +70,7 @@ public class UserController(
|
||||||
.Paginate(pq, ControllerContext)
|
.Paginate(pq, ControllerContext)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), localUser));
|
return Ok(await noteRenderer.RenderMany(notes.EnforceRenoteReplyVisibility(), localUser,
|
||||||
|
Filter.FilterContext.Accounts));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ public class NoteResponse : NoteWithQuote
|
||||||
[J("replyId")] public string? ReplyId { get; set; }
|
[J("replyId")] public string? ReplyId { get; set; }
|
||||||
[J("renote")] public NoteWithQuote? Renote { get; set; }
|
[J("renote")] public NoteWithQuote? Renote { get; set; }
|
||||||
[J("renoteId")] public string? RenoteId { get; set; }
|
[J("renoteId")] public string? RenoteId { get; set; }
|
||||||
|
[J("filtered")] public NoteFilteredSchema? Filtered { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NoteWithQuote : NoteBase
|
public class NoteWithQuote : NoteBase
|
||||||
|
@ -46,3 +47,10 @@ public class NoteReactionSchema
|
||||||
[J("reacted")] public required bool Reacted { get; set; }
|
[J("reacted")] public required bool Reacted { get; set; }
|
||||||
[J("url")] public required string? Url { get; set; }
|
[J("url")] public required string? Url { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class NoteFilteredSchema
|
||||||
|
{
|
||||||
|
[J("filterId")] public required long Id { get; set; }
|
||||||
|
[J("keyword")] public required string Keyword { get; set; }
|
||||||
|
[J("drop")] public required bool Hide { get; set; }
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue