Compare commits

..

2 Commits

Author SHA1 Message Date
c3095f2a9b ♻️ Refactor the publisher loading in posts 2025-05-31 12:11:45 +08:00
7656a8b298 🐛 Trying to fix the message notification... 2025-05-31 11:54:23 +08:00
6 changed files with 136 additions and 37 deletions

View File

@@ -158,9 +158,18 @@ public class NotificationService
var accounts = await _db.Accounts.ToListAsync(); var accounts = await _db.Accounts.ToListAsync();
var notifications = accounts.Select(x => var notifications = accounts.Select(x =>
{ {
notification.Account = x; var newNotification = new Notification
notification.AccountId = x.Id; {
return notification; Topic = notification.Topic,
Title = notification.Title,
Subtitle = notification.Subtitle,
Content = notification.Content,
Meta = notification.Meta,
Priority = notification.Priority,
Account = x,
AccountId = x.Id
};
return newNotification;
}).ToList(); }).ToList();
await _db.BulkInsertAsync(notifications); await _db.BulkInsertAsync(notifications);
} }
@@ -183,9 +192,18 @@ public class NotificationService
{ {
var notifications = accounts.Select(x => var notifications = accounts.Select(x =>
{ {
notification.Account = x; var newNotification = new Notification
notification.AccountId = x.Id; {
return notification; Topic = notification.Topic,
Title = notification.Title,
Subtitle = notification.Subtitle,
Content = notification.Content,
Meta = notification.Meta,
Priority = notification.Priority,
Account = x,
AccountId = x.Id
};
return newNotification;
}).ToList(); }).ToList();
await _db.BulkInsertAsync(notifications); await _db.BulkInsertAsync(notifications);
} }
@@ -222,16 +240,22 @@ public class NotificationService
notification.Content ?? string.Empty).Trim(); notification.Content ?? string.Empty).Trim();
} }
await _fcm.SendAsync(new await _fcm.SendAsync(new Dictionary<string, object>
{ {
message = new ["message"] = new Dictionary<string, object>
{ {
token = subscription.DeviceToken, ["token"] = subscription.DeviceToken,
notification = new ["notification"] = new Dictionary<string, object>
{ {
title = notification.Title ?? string.Empty, body ["title"] = notification.Title ?? string.Empty,
["body"] = body
}, },
data = notification.Meta ?? new Dictionary<string, object>() ["data"] = new Dictionary<string, object>
{
["id"] = notification.Id,
["topic"] = notification.Topic,
["meta"] = notification.Meta ?? new Dictionary<string, object>()
}
} }
}); });
break; break;
@@ -242,6 +266,7 @@ public class NotificationService
await _apns.SendAsync(new Dictionary<string, object> await _apns.SendAsync(new Dictionary<string, object>
{ {
["topic"] = notification.Topic,
["aps"] = new Dictionary<string, object> ["aps"] = new Dictionary<string, object>
{ {
["alert"] = new Dictionary<string, object> ["alert"] = new Dictionary<string, object>

View File

@@ -3,6 +3,7 @@ using DysonNetwork.Sphere.Permission;
using DysonNetwork.Sphere.Publisher; using DysonNetwork.Sphere.Publisher;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Query;
using NodaTime; using NodaTime;
using Npgsql; using Npgsql;
using Quartz; using Quartz;
@@ -360,3 +361,35 @@ public class AppDatabaseFactory : IDesignTimeDbContextFactory<AppDatabase>
return new AppDatabase(optionsBuilder.Options, configuration); return new AppDatabase(optionsBuilder.Options, configuration);
} }
} }
public static class OptionalQueryExtensions
{
public static IQueryable<T> If<T>(
this IQueryable<T> source,
bool condition,
Func<IQueryable<T>, IQueryable<T>> transform
)
{
return condition ? transform(source) : source;
}
public static IQueryable<T> If<T, TP>(
this IIncludableQueryable<T, TP> source,
bool condition,
Func<IIncludableQueryable<T, TP>, IQueryable<T>> transform
)
where T : class
{
return condition ? transform(source) : source;
}
public static IQueryable<T> If<T, TP>(
this IIncludableQueryable<T, IEnumerable<TP>> source,
bool condition,
Func<IIncludableQueryable<T, IEnumerable<TP>>, IQueryable<T>> transform
)
where T : class
{
return condition ? transform(source) : source;
}
}

View File

@@ -66,14 +66,13 @@ public class ChatService(
var scopedCrs = scope.ServiceProvider.GetRequiredService<ChatRoomService>(); var scopedCrs = scope.ServiceProvider.GetRequiredService<ChatRoomService>();
var roomSubject = room.Realm is not null ? $"{room.Name}, {room.Realm.Name}" : room.Name; var roomSubject = room.Realm is not null ? $"{room.Name}, {room.Realm.Name}" : room.Name;
var tasks = new List<Task>();
var members = await scopedCrs.ListRoomMembers(room.Id); var members = await scopedCrs.ListRoomMembers(room.Id);
var notification = new Notification var notification = new Notification
{ {
Topic = "messages.new", Topic = "messages.new",
Title = $"{sender.Nick ?? sender.Account?.Nick ?? "Unknown"} ({roomSubject})", Title = $"{sender.Nick ?? sender.Account.Nick ?? "Unknown"} ({roomSubject})",
Content = !string.IsNullOrEmpty(message.Content) Content = !string.IsNullOrEmpty(message.Content)
? message.Content[..Math.Min(message.Content.Length, 100)] ? message.Content[..Math.Min(message.Content.Length, 100)]
: "<attachments>", : "<attachments>",
@@ -106,9 +105,8 @@ public class ChatService(
logger.LogInformation($"Trying to deliver message to {accountsToNotify.Count} accounts..."); logger.LogInformation($"Trying to deliver message to {accountsToNotify.Count} accounts...");
// Only send notifications if there are accounts to notify // Only send notifications if there are accounts to notify
if (accountsToNotify.Count > 0) if (accountsToNotify.Count > 0)
tasks.Add(scopedNty.SendNotificationBatch(notification, accountsToNotify, save: false)); await scopedNty.SendNotificationBatch(notification, accountsToNotify, save: false);
await Task.WhenAll(tasks);
logger.LogInformation($"Delivered message to {accountsToNotify.Count} accounts."); logger.LogInformation($"Delivered message to {accountsToNotify.Count} accounts.");
} }

View File

@@ -40,7 +40,7 @@ public class PostController(
.FilterWithVisibility(currentUser, userFriends, isListing: true) .FilterWithVisibility(currentUser, userFriends, isListing: true)
.CountAsync(); .CountAsync();
var posts = await query var posts = await query
.Include(e => e.Publisher) .Include(e => e.RepliedPost)
.Include(e => e.ThreadedPost) .Include(e => e.ThreadedPost)
.Include(e => e.ForwardedPost) .Include(e => e.ForwardedPost)
.Include(e => e.Attachments) .Include(e => e.Attachments)
@@ -53,6 +53,7 @@ public class PostController(
.Take(take) .Take(take)
.ToListAsync(); .ToListAsync();
posts = PostService.TruncatePostContent(posts); posts = PostService.TruncatePostContent(posts);
posts = await ps.LoadPublishers(posts);
var postsId = posts.Select(e => e.Id).ToList(); var postsId = posts.Select(e => e.Id).ToList();
var reactionMaps = await ps.GetPostReactionMapBatch(postsId); var reactionMaps = await ps.GetPostReactionMapBatch(postsId);
@@ -106,7 +107,6 @@ public class PostController(
.CountAsync(); .CountAsync();
var posts = await db.Posts var posts = await db.Posts
.Where(e => e.RepliedPostId == id) .Where(e => e.RepliedPostId == id)
.Include(e => e.Publisher)
.Include(e => e.ThreadedPost) .Include(e => e.ThreadedPost)
.Include(e => e.ForwardedPost) .Include(e => e.ForwardedPost)
.Include(e => e.Attachments) .Include(e => e.Attachments)
@@ -118,6 +118,7 @@ public class PostController(
.Take(take) .Take(take)
.ToListAsync(); .ToListAsync();
posts = PostService.TruncatePostContent(posts); posts = PostService.TruncatePostContent(posts);
posts = await ps.LoadPublishers(posts);
var postsId = posts.Select(e => e.Id).ToList(); var postsId = posts.Select(e => e.Id).ToList();
var reactionMaps = await ps.GetPostReactionMapBatch(postsId); var reactionMaps = await ps.GetPostReactionMapBatch(postsId);

View File

@@ -271,6 +271,47 @@ public class PostService(
) )
); );
} }
public async Task<List<Post>> LoadPublishers(List<Post> posts)
{
var publisherIds = posts
.Where(e => e.Publisher.AccountId != null)
.SelectMany<Post, Guid?>(e =>
[
e.Publisher.Id,
e.RepliedPost?.Publisher.Id,
e.ForwardedPost?.Publisher.Id,
e.ThreadedPost?.Publisher.Id
])
.Where(e => e != null)
.Distinct()
.ToList();
if (publisherIds.Count == 0) return posts;
var publishers = await db.Publishers
.Where(e => e.AccountId != null && publisherIds.Contains(e.AccountId.Value))
.ToDictionaryAsync(e => e.AccountId!.Value);
foreach (var post in posts)
{
if (publishers.TryGetValue(post.Publisher.Id, out var publisher))
post.Publisher = publisher;
if (post.RepliedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.RepliedPost.Publisher.Id, out var repliedPublisher))
post.RepliedPost.Publisher = repliedPublisher;
if (post.ForwardedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.ForwardedPost.Publisher.Id, out var forwardedPublisher))
post.ForwardedPost.Publisher = forwardedPublisher;
if (post.ThreadedPost?.Publisher.Id != null &&
publishers.TryGetValue(post.ThreadedPost.Publisher.Id, out var threadedPublisher))
post.ThreadedPost.Publisher = threadedPublisher;
}
return posts;
}
} }
public static class PostQueryExtensions public static class PostQueryExtensions

View File

@@ -23,6 +23,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fadcd336f9cde4e71998a851d7eb945bb87e00_003F0c_003F96dc130e_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F109293935a4844d5aa1610150b96edcde55000_003Fb7_003F8b7e5594_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F109293935a4844d5aa1610150b96edcde55000_003Fb7_003F8b7e5594_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fbf_003F44af6d95_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fbf_003F44af6d95_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>