[backend/akko-client] Fix notifications, including emoji reaction notifications
This commit is contained in:
parent
834661981a
commit
9d6d892091
9 changed files with 60 additions and 17 deletions
|
@ -34,6 +34,8 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var user = HttpContext.GetUserOrFail();
|
var user = HttpContext.GetUserOrFail();
|
||||||
|
var isPleroma = HttpContext.GetOauthToken()!.IsPleroma;
|
||||||
|
|
||||||
return await db.Notifications
|
return await db.Notifications
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.Where(p => p.Notifiee == user)
|
.Where(p => p.Notifiee == user)
|
||||||
|
@ -54,7 +56,7 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
|
||||||
.FilterMutedThreads(user, db)
|
.FilterMutedThreads(user, db)
|
||||||
.Paginate(p => p.MastoId, query, ControllerContext)
|
.Paginate(p => p.MastoId, query, ControllerContext)
|
||||||
.PrecomputeNoteVisibilities(user)
|
.PrecomputeNoteVisibilities(user)
|
||||||
.RenderAllForMastodonAsync(notificationRenderer, user);
|
.RenderAllForMastodonAsync(notificationRenderer, user, isPleroma);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id:long}")]
|
[HttpGet("{id:long}")]
|
||||||
|
@ -64,6 +66,8 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
|
||||||
public async Task<NotificationEntity> GetNotification(long id)
|
public async Task<NotificationEntity> GetNotification(long id)
|
||||||
{
|
{
|
||||||
var user = HttpContext.GetUserOrFail();
|
var user = HttpContext.GetUserOrFail();
|
||||||
|
var isPleroma = HttpContext.GetOauthToken()!.IsPleroma;
|
||||||
|
|
||||||
var notification = await db.Notifications
|
var notification = await db.Notifications
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.Where(p => p.Notifiee == user && p.MastoId == id)
|
.Where(p => p.Notifiee == user && p.MastoId == id)
|
||||||
|
@ -72,7 +76,7 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
var res = await notificationRenderer.RenderAsync(notification.EnforceRenoteReplyVisibility(p => p.Note), user);
|
var res = await notificationRenderer.RenderAsync(notification.EnforceRenoteReplyVisibility(p => p.Note), user, isPleroma);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,17 @@
|
||||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
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.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
|
using Iceshrimp.Backend.Core.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Mastodon.Renderers;
|
namespace Iceshrimp.Backend.Controllers.Mastodon.Renderers;
|
||||||
|
|
||||||
public class NotificationRenderer(NoteRenderer noteRenderer, UserRenderer userRenderer)
|
public class NotificationRenderer(DatabaseContext db, NoteRenderer noteRenderer, UserRenderer userRenderer)
|
||||||
{
|
{
|
||||||
public async Task<NotificationEntity> RenderAsync(
|
public async Task<NotificationEntity> RenderAsync(
|
||||||
Notification notification, User user, List<AccountEntity>? accounts = null,
|
Notification notification, User user, bool isPleroma, List<AccountEntity>? accounts = null,
|
||||||
IEnumerable<StatusEntity>? statuses = null
|
IEnumerable<StatusEntity>? statuses = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -25,20 +28,35 @@ public class NotificationRenderer(NoteRenderer noteRenderer, UserRenderer userRe
|
||||||
var notifier = accounts?.FirstOrDefault(p => p.Id == dbNotifier.Id) ??
|
var notifier = accounts?.FirstOrDefault(p => p.Id == dbNotifier.Id) ??
|
||||||
await userRenderer.RenderAsync(dbNotifier);
|
await userRenderer.RenderAsync(dbNotifier);
|
||||||
|
|
||||||
|
string? emojiUrl = null;
|
||||||
|
if (notification.Reaction != null && EmojiService.IsCustomEmoji(notification.Reaction)) {
|
||||||
|
var parts = notification.Reaction.Trim(':').Split('@');
|
||||||
|
emojiUrl = await db.Emojis.Where(e => e.Name == parts[0] && e.Host == (parts.Length > 1 ? parts[1] : null)).Select(e => e.PublicUrl).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
var res = new NotificationEntity
|
var res = new NotificationEntity
|
||||||
{
|
{
|
||||||
Id = notification.MastoId.ToString(),
|
Id = notification.MastoId.ToString(),
|
||||||
Type = NotificationEntity.EncodeType(notification.Type),
|
Type = NotificationEntity.EncodeType(notification.Type, isPleroma),
|
||||||
Note = note,
|
Note = note,
|
||||||
Notifier = notifier,
|
Notifier = notifier,
|
||||||
CreatedAt = notification.CreatedAt.ToStringIso8601Like()
|
CreatedAt = notification.CreatedAt.ToStringIso8601Like(),
|
||||||
|
|
||||||
|
Emoji = notification.Reaction,
|
||||||
|
EmojiUrl = emojiUrl,
|
||||||
|
|
||||||
|
Pleroma = new() {
|
||||||
|
// TODO: stub
|
||||||
|
IsMuted = false,
|
||||||
|
IsSeen = notification.IsRead
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<NotificationEntity>> RenderManyAsync(
|
public async Task<IEnumerable<NotificationEntity>> RenderManyAsync(
|
||||||
IEnumerable<Notification> notifications, User user
|
IEnumerable<Notification> notifications, User user, bool isPleroma
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var notificationList = notifications.ToList();
|
var notificationList = notifications.ToList();
|
||||||
|
@ -64,7 +82,7 @@ public class NotificationRenderer(NoteRenderer noteRenderer, UserRenderer userRe
|
||||||
user, Filter.FilterContext.Notifications, accounts);
|
user, Filter.FilterContext.Notifications, accounts);
|
||||||
|
|
||||||
return await notificationList
|
return await notificationList
|
||||||
.Select(p => RenderAsync(p, user, accounts, notes))
|
.Select(p => RenderAsync(p, user, isPleroma, accounts, notes))
|
||||||
.AwaitAllAsync();
|
.AwaitAllAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||||
using static Iceshrimp.Backend.Core.Database.Tables.Notification;
|
using static Iceshrimp.Backend.Core.Database.Tables.Notification;
|
||||||
|
using Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
|
@ -13,9 +14,12 @@ public class NotificationEntity : IEntity
|
||||||
[J("status")] public required StatusEntity? Note { get; set; }
|
[J("status")] public required StatusEntity? Note { get; set; }
|
||||||
[J("id")] public required string Id { get; set; }
|
[J("id")] public required string Id { get; set; }
|
||||||
|
|
||||||
//TODO: [J("reaction")] public required Reaction? Reaction { get; set; }
|
|
||||||
|
|
||||||
public static string EncodeType(NotificationType type)
|
[J("emoji")] public string? Emoji { get; set; }
|
||||||
|
[J("emoji_url")] public string? EmojiUrl { get; set; }
|
||||||
|
[J("pleroma")] public required PleromaNotificationExtensions Pleroma { get; set; }
|
||||||
|
|
||||||
|
public static string EncodeType(NotificationType type, bool isPleroma)
|
||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
|
@ -28,7 +32,9 @@ public class NotificationEntity : IEntity
|
||||||
NotificationType.PollEnded => "poll",
|
NotificationType.PollEnded => "poll",
|
||||||
NotificationType.FollowRequestReceived => "follow_request",
|
NotificationType.FollowRequestReceived => "follow_request",
|
||||||
NotificationType.Edit => "update",
|
NotificationType.Edit => "update",
|
||||||
NotificationType.Reaction => "reaction",
|
|
||||||
|
NotificationType.Reaction when isPleroma => "pleroma:emoji_reaction",
|
||||||
|
NotificationType.Reaction when !isPleroma => "reaction",
|
||||||
|
|
||||||
_ => throw new GracefulException($"Unsupported notification type: {type}")
|
_ => throw new GracefulException($"Unsupported notification type: {type}")
|
||||||
};
|
};
|
||||||
|
@ -46,6 +52,7 @@ public class NotificationEntity : IEntity
|
||||||
"follow_request" => [NotificationType.FollowRequestReceived],
|
"follow_request" => [NotificationType.FollowRequestReceived],
|
||||||
"update" => [NotificationType.Edit],
|
"update" => [NotificationType.Edit],
|
||||||
"reaction" => [NotificationType.Reaction],
|
"reaction" => [NotificationType.Reaction],
|
||||||
|
"pleroma:emoji_reaction" => [NotificationType.Reaction],
|
||||||
_ => []
|
_ => []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
NotificationEntity rendered;
|
NotificationEntity rendered;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rendered = await renderer.RenderAsync(notification, connection.Token.User);
|
rendered = await renderer.RenderAsync(notification, connection.Token.User, connection.Token.IsPleroma);
|
||||||
}
|
}
|
||||||
catch (GracefulException)
|
catch (GracefulException)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||||
|
|
||||||
|
namespace Iceshrimp.Backend.Controllers.Pleroma.Schemas.Entities;
|
||||||
|
|
||||||
|
public class PleromaNotificationExtensions
|
||||||
|
{
|
||||||
|
[J("is_muted")] public required bool IsMuted { get; set; }
|
||||||
|
[J("is_seen")] public required bool IsSeen { get; set; }
|
||||||
|
}
|
|
@ -523,13 +523,13 @@ public static class QueryableExtensions
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<NotificationEntity>> RenderAllForMastodonAsync(
|
public static async Task<List<NotificationEntity>> RenderAllForMastodonAsync(
|
||||||
this IQueryable<Notification> notifications, NotificationRenderer renderer, User user
|
this IQueryable<Notification> notifications, NotificationRenderer renderer, User user, bool isPleroma
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var list = (await notifications.ToListAsync())
|
var list = (await notifications.ToListAsync())
|
||||||
.EnforceRenoteReplyVisibility(p => p.Note)
|
.EnforceRenoteReplyVisibility(p => p.Note)
|
||||||
.ToList();
|
.ToList();
|
||||||
return (await renderer.RenderManyAsync(list, user)).ToList();
|
return (await renderer.RenderManyAsync(list, user, isPleroma)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IQueryable<Note> FilterByAccountStatusesRequest(
|
public static IQueryable<Note> FilterByAccountStatusesRequest(
|
||||||
|
|
|
@ -230,6 +230,8 @@ public partial class EmojiService(
|
||||||
return emoji;
|
return emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsCustomEmoji(string s) => CustomEmojiRegex().IsMatch(s);
|
||||||
|
|
||||||
[GeneratedRegex(@"^:?([\w+-]+)(?:@\.)?:?$", RegexOptions.Compiled)]
|
[GeneratedRegex(@"^:?([\w+-]+)(?:@\.)?:?$", RegexOptions.Compiled)]
|
||||||
private static partial Regex CustomEmojiRegex();
|
private static partial Regex CustomEmojiRegex();
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,8 @@ public class NotificationService(
|
||||||
Note = reaction.Note,
|
Note = reaction.Note,
|
||||||
Notifiee = reaction.Note.User,
|
Notifiee = reaction.Note.User,
|
||||||
Notifier = reaction.User,
|
Notifier = reaction.User,
|
||||||
Type = Notification.NotificationType.Reaction
|
Type = Notification.NotificationType.Reaction,
|
||||||
|
Reaction = reaction.Reaction
|
||||||
};
|
};
|
||||||
|
|
||||||
await db.AddAsync(notification);
|
await db.AddAsync(notification);
|
||||||
|
|
|
@ -33,12 +33,14 @@ public class PushService(
|
||||||
|
|
||||||
private async void MastodonPushHandler(object? _, Notification notification)
|
private async void MastodonPushHandler(object? _, Notification notification)
|
||||||
{
|
{
|
||||||
|
// TODO: do not hardcode isPleroma
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using var scope = scopeFactory.CreateAsyncScope();
|
await using var scope = scopeFactory.CreateAsyncScope();
|
||||||
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||||
|
|
||||||
var type = NotificationEntity.EncodeType(notification.Type);
|
var type = NotificationEntity.EncodeType(notification.Type, isPleroma: false);
|
||||||
var subscriptions = await db.PushSubscriptions.Where(p => p.User == notification.Notifiee)
|
var subscriptions = await db.PushSubscriptions.Where(p => p.User == notification.Notifiee)
|
||||||
.Include(pushSubscription => pushSubscription.OauthToken)
|
.Include(pushSubscription => pushSubscription.OauthToken)
|
||||||
.Where(p => p.Types.Contains(type))
|
.Where(p => p.Types.Contains(type))
|
||||||
|
@ -74,7 +76,7 @@ public class PushService(
|
||||||
p.Followee == notification.Notifiee);
|
p.Followee == notification.Notifiee);
|
||||||
|
|
||||||
var renderer = scope.ServiceProvider.GetRequiredService<NotificationRenderer>();
|
var renderer = scope.ServiceProvider.GetRequiredService<NotificationRenderer>();
|
||||||
var rendered = await renderer.RenderAsync(notification, notification.Notifiee);
|
var rendered = await renderer.RenderAsync(notification, notification.Notifiee, isPleroma: false);
|
||||||
var name = rendered.Notifier.DisplayName;
|
var name = rendered.Notifier.DisplayName;
|
||||||
var subject = rendered.Type switch
|
var subject = rendered.Type switch
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue