Compare commits
2 Commits
1024721e0e
...
c3095f2a9b
| Author | SHA1 | Date | |
|---|---|---|---|
| c3095f2a9b | |||
| 7656a8b298 |
@@ -158,9 +158,18 @@ public class NotificationService
|
||||
var accounts = await _db.Accounts.ToListAsync();
|
||||
var notifications = accounts.Select(x =>
|
||||
{
|
||||
notification.Account = x;
|
||||
notification.AccountId = x.Id;
|
||||
return notification;
|
||||
var newNotification = new 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();
|
||||
await _db.BulkInsertAsync(notifications);
|
||||
}
|
||||
@@ -183,9 +192,18 @@ public class NotificationService
|
||||
{
|
||||
var notifications = accounts.Select(x =>
|
||||
{
|
||||
notification.Account = x;
|
||||
notification.AccountId = x.Id;
|
||||
return notification;
|
||||
var newNotification = new 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();
|
||||
await _db.BulkInsertAsync(notifications);
|
||||
}
|
||||
@@ -222,16 +240,22 @@ public class NotificationService
|
||||
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,
|
||||
notification = new
|
||||
["token"] = subscription.DeviceToken,
|
||||
["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;
|
||||
@@ -242,6 +266,7 @@ public class NotificationService
|
||||
|
||||
await _apns.SendAsync(new Dictionary<string, object>
|
||||
{
|
||||
["topic"] = notification.Topic,
|
||||
["aps"] = new Dictionary<string, object>
|
||||
{
|
||||
["alert"] = new Dictionary<string, object>
|
||||
|
||||
@@ -3,6 +3,7 @@ using DysonNetwork.Sphere.Permission;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using NodaTime;
|
||||
using Npgsql;
|
||||
using Quartz;
|
||||
@@ -360,3 +361,35 @@ public class AppDatabaseFactory : IDesignTimeDbContextFactory<AppDatabase>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -66,14 +66,13 @@ public class ChatService(
|
||||
var scopedCrs = scope.ServiceProvider.GetRequiredService<ChatRoomService>();
|
||||
|
||||
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 notification = new Notification
|
||||
{
|
||||
Topic = "messages.new",
|
||||
Title = $"{sender.Nick ?? sender.Account?.Nick ?? "Unknown"} ({roomSubject})",
|
||||
Title = $"{sender.Nick ?? sender.Account.Nick ?? "Unknown"} ({roomSubject})",
|
||||
Content = !string.IsNullOrEmpty(message.Content)
|
||||
? message.Content[..Math.Min(message.Content.Length, 100)]
|
||||
: "<attachments>",
|
||||
@@ -106,9 +105,8 @@ public class ChatService(
|
||||
logger.LogInformation($"Trying to deliver message to {accountsToNotify.Count} accounts...");
|
||||
// Only send notifications if there are accounts to notify
|
||||
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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class PostController(
|
||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
||||
.CountAsync();
|
||||
var posts = await query
|
||||
.Include(e => e.Publisher)
|
||||
.Include(e => e.RepliedPost)
|
||||
.Include(e => e.ThreadedPost)
|
||||
.Include(e => e.ForwardedPost)
|
||||
.Include(e => e.Attachments)
|
||||
@@ -53,6 +53,7 @@ public class PostController(
|
||||
.Take(take)
|
||||
.ToListAsync();
|
||||
posts = PostService.TruncatePostContent(posts);
|
||||
posts = await ps.LoadPublishers(posts);
|
||||
|
||||
var postsId = posts.Select(e => e.Id).ToList();
|
||||
var reactionMaps = await ps.GetPostReactionMapBatch(postsId);
|
||||
@@ -106,7 +107,6 @@ public class PostController(
|
||||
.CountAsync();
|
||||
var posts = await db.Posts
|
||||
.Where(e => e.RepliedPostId == id)
|
||||
.Include(e => e.Publisher)
|
||||
.Include(e => e.ThreadedPost)
|
||||
.Include(e => e.ForwardedPost)
|
||||
.Include(e => e.Attachments)
|
||||
@@ -118,6 +118,7 @@ public class PostController(
|
||||
.Take(take)
|
||||
.ToListAsync();
|
||||
posts = PostService.TruncatePostContent(posts);
|
||||
posts = await ps.LoadPublishers(posts);
|
||||
|
||||
var postsId = posts.Select(e => e.Id).ToList();
|
||||
var reactionMaps = await ps.GetPostReactionMapBatch(postsId);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_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_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_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>
|
||||
|
||||
Reference in New Issue
Block a user