♻️ Refactored publishers
This commit is contained in:
parent
2821beb1b7
commit
1f2e9b1de8
@ -1,11 +1,12 @@
|
|||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Account;
|
||||||
using DysonNetwork.Sphere.Post;
|
using DysonNetwork.Sphere.Post;
|
||||||
|
using DysonNetwork.Sphere.Publisher;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Activity;
|
namespace DysonNetwork.Sphere.Activity;
|
||||||
|
|
||||||
public class ActivityService(AppDatabase db, RelationshipService rels, PostService ps)
|
public class ActivityService(AppDatabase db, PublisherService pub, RelationshipService rels, PostService ps)
|
||||||
{
|
{
|
||||||
public async Task<List<Activity>> GetActivitiesForAnyone(int take, Instant? cursor)
|
public async Task<List<Activity>> GetActivitiesForAnyone(int take, Instant? cursor)
|
||||||
{
|
{
|
||||||
@ -20,7 +21,7 @@ public class ActivityService(AppDatabase db, RelationshipService rels, PostServi
|
|||||||
.Where(e => e.RepliedPostId == null)
|
.Where(e => e.RepliedPostId == null)
|
||||||
.Where(p => cursor == null || p.PublishedAt < cursor)
|
.Where(p => cursor == null || p.PublishedAt < cursor)
|
||||||
.OrderByDescending(p => p.PublishedAt)
|
.OrderByDescending(p => p.PublishedAt)
|
||||||
.FilterWithVisibility(null, [], isListing: true)
|
.FilterWithVisibility(null, [], [], isListing: true)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
posts = PostService.TruncatePostContent(posts);
|
posts = PostService.TruncatePostContent(posts);
|
||||||
@ -46,6 +47,9 @@ public class ActivityService(AppDatabase db, RelationshipService rels, PostServi
|
|||||||
{
|
{
|
||||||
var activities = new List<Activity>();
|
var activities = new List<Activity>();
|
||||||
var userFriends = await rels.ListAccountFriends(currentUser);
|
var userFriends = await rels.ListAccountFriends(currentUser);
|
||||||
|
var userPublishers = await pub.GetUserPublishers(currentUser.Id);
|
||||||
|
|
||||||
|
var publishersId = userPublishers.Select(e => e.Id).ToList();
|
||||||
|
|
||||||
// Crunching data
|
// Crunching data
|
||||||
var posts = await db.Posts
|
var posts = await db.Posts
|
||||||
@ -53,10 +57,10 @@ public class ActivityService(AppDatabase db, RelationshipService rels, PostServi
|
|||||||
.Include(e => e.ForwardedPost)
|
.Include(e => e.ForwardedPost)
|
||||||
.Include(e => e.Categories)
|
.Include(e => e.Categories)
|
||||||
.Include(e => e.Tags)
|
.Include(e => e.Tags)
|
||||||
.Where(e => e.RepliedPostId == null)
|
.Where(e => e.RepliedPostId == null || publishersId.Contains(e.RepliedPost!.PublisherId))
|
||||||
.Where(p => cursor == null || p.PublishedAt < cursor)
|
.Where(p => cursor == null || p.PublishedAt < cursor)
|
||||||
.OrderByDescending(p => p.PublishedAt)
|
.OrderByDescending(p => p.PublishedAt)
|
||||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
.FilterWithVisibility(currentUser, userFriends, userPublishers, isListing: true)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
posts = PostService.TruncatePostContent(posts);
|
posts = PostService.TruncatePostContent(posts);
|
||||||
|
@ -30,15 +30,17 @@ public class PostController(
|
|||||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
var currentUser = currentUserValue as Account.Account;
|
var currentUser = currentUserValue as Account.Account;
|
||||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||||
|
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||||
|
|
||||||
var publisher = pubName == null ? null : await db.Publishers.FirstOrDefaultAsync(p => p.Name == pubName);
|
var publisher = pubName == null ? null : await db.Publishers.FirstOrDefaultAsync(p => p.Name == pubName);
|
||||||
|
|
||||||
var query = db.Posts.AsQueryable();
|
var query = db.Posts.AsQueryable();
|
||||||
if (publisher != null)
|
if (publisher != null)
|
||||||
query = query.Where(p => p.Publisher.Id == publisher.Id);
|
query = query.Where(p => p.Publisher.Id == publisher.Id);
|
||||||
|
query = query
|
||||||
|
.FilterWithVisibility(currentUser, userFriends, userPublishers, isListing: true);
|
||||||
|
|
||||||
var totalCount = await query
|
var totalCount = await query
|
||||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
var posts = await query
|
var posts = await query
|
||||||
.Include(e => e.RepliedPost)
|
.Include(e => e.RepliedPost)
|
||||||
@ -46,7 +48,6 @@ public class PostController(
|
|||||||
.Include(e => e.Categories)
|
.Include(e => e.Categories)
|
||||||
.Include(e => e.Tags)
|
.Include(e => e.Tags)
|
||||||
.Where(e => e.RepliedPostId == null)
|
.Where(e => e.RepliedPostId == null)
|
||||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
|
||||||
.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt)
|
.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt)
|
||||||
.Skip(offset)
|
.Skip(offset)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
@ -71,13 +72,14 @@ public class PostController(
|
|||||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
var currentUser = currentUserValue as Account.Account;
|
var currentUser = currentUserValue as Account.Account;
|
||||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||||
|
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||||
|
|
||||||
var post = await db.Posts
|
var post = await db.Posts
|
||||||
.Where(e => e.Id == id)
|
.Where(e => e.Id == id)
|
||||||
.Include(e => e.Publisher)
|
.Include(e => e.Publisher)
|
||||||
.Include(e => e.Tags)
|
.Include(e => e.Tags)
|
||||||
.Include(e => e.Categories)
|
.Include(e => e.Categories)
|
||||||
.FilterWithVisibility(currentUser, userFriends)
|
.FilterWithVisibility(currentUser, userFriends, userPublishers)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (post is null) return NotFound();
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
@ -93,6 +95,7 @@ public class PostController(
|
|||||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
var currentUser = currentUserValue as Account.Account;
|
var currentUser = currentUserValue as Account.Account;
|
||||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||||
|
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||||
|
|
||||||
var parent = await db.Posts
|
var parent = await db.Posts
|
||||||
.Where(e => e.Id == id)
|
.Where(e => e.Id == id)
|
||||||
@ -101,14 +104,14 @@ public class PostController(
|
|||||||
|
|
||||||
var totalCount = await db.Posts
|
var totalCount = await db.Posts
|
||||||
.Where(e => e.RepliedPostId == parent.Id)
|
.Where(e => e.RepliedPostId == parent.Id)
|
||||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
.FilterWithVisibility(currentUser, userFriends, userPublishers, isListing: true)
|
||||||
.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.ForwardedPost)
|
.Include(e => e.ForwardedPost)
|
||||||
.Include(e => e.Categories)
|
.Include(e => e.Categories)
|
||||||
.Include(e => e.Tags)
|
.Include(e => e.Tags)
|
||||||
.FilterWithVisibility(currentUser, userFriends, isListing: true)
|
.FilterWithVisibility(currentUser, userFriends, userPublishers, isListing: true)
|
||||||
.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt)
|
.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt)
|
||||||
.Skip(offset)
|
.Skip(offset)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
@ -223,7 +226,7 @@ public class PostController(
|
|||||||
{
|
{
|
||||||
using var scope = factory.CreateScope();
|
using var scope = factory.CreateScope();
|
||||||
var subs = scope.ServiceProvider.GetRequiredService<PublisherSubscriptionService>();
|
var subs = scope.ServiceProvider.GetRequiredService<PublisherSubscriptionService>();
|
||||||
await subs.NotifySubscribersPostAsync(post);
|
await subs.NotifySubscriberPost(post);
|
||||||
});
|
});
|
||||||
|
|
||||||
als.CreateActionLogFromRequest(
|
als.CreateActionLogFromRequest(
|
||||||
@ -248,12 +251,13 @@ public class PostController(
|
|||||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
if (currentUserValue is not Account.Account currentUser) return Unauthorized();
|
if (currentUserValue is not Account.Account currentUser) return Unauthorized();
|
||||||
var userFriends = await rels.ListAccountFriends(currentUser);
|
var userFriends = await rels.ListAccountFriends(currentUser);
|
||||||
|
var userPublishers = await pub.GetUserPublishers(currentUser.Id);
|
||||||
|
|
||||||
var post = await db.Posts
|
var post = await db.Posts
|
||||||
.Where(e => e.Id == id)
|
.Where(e => e.Id == id)
|
||||||
.Include(e => e.Publisher)
|
.Include(e => e.Publisher)
|
||||||
.ThenInclude(e => e.Account)
|
.ThenInclude(e => e.Account)
|
||||||
.FilterWithVisibility(currentUser, userFriends)
|
.FilterWithVisibility(currentUser, userFriends, userPublishers)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (post is null) return NotFound();
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
@ -274,7 +278,6 @@ public class PostController(
|
|||||||
post,
|
post,
|
||||||
reaction,
|
reaction,
|
||||||
currentUser,
|
currentUser,
|
||||||
post.Publisher.Account,
|
|
||||||
isExistingReaction,
|
isExistingReaction,
|
||||||
isSelfReact
|
isSelfReact
|
||||||
);
|
);
|
||||||
|
@ -11,10 +11,8 @@ namespace DysonNetwork.Sphere.Post;
|
|||||||
|
|
||||||
public class PostService(
|
public class PostService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
FileService fs,
|
|
||||||
FileReferenceService fileRefService,
|
FileReferenceService fileRefService,
|
||||||
IStringLocalizer<NotificationResource> localizer,
|
IStringLocalizer<NotificationResource> localizer,
|
||||||
NotificationService nty,
|
|
||||||
IServiceScopeFactory factory
|
IServiceScopeFactory factory
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -33,6 +31,21 @@ public class PostService(
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public (string title, string content) ChopPostForNotification(Post post)
|
||||||
|
{
|
||||||
|
var content = !string.IsNullOrEmpty(post.Description)
|
||||||
|
? post.Description?.Length >= 40 ? post.Description[..37] + "..." : post.Description
|
||||||
|
: post.Content?.Length >= 100
|
||||||
|
? string.Concat(post.Content.AsSpan(0, 97), "...")
|
||||||
|
: post.Content;
|
||||||
|
var title = post.Title ?? (post.Content?.Length >= 10 ? post.Content[..10] + "..." : post.Content);
|
||||||
|
if (content is null)
|
||||||
|
content = localizer["PostOnlyMedia"];
|
||||||
|
if (title is null)
|
||||||
|
title = localizer["PostOnlyMedia"];
|
||||||
|
return (title, content);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Post> PostAsync(
|
public async Task<Post> PostAsync(
|
||||||
Account.Account user,
|
Account.Account user,
|
||||||
Post post,
|
Post post,
|
||||||
@ -111,9 +124,44 @@ public class PostService(
|
|||||||
{
|
{
|
||||||
using var scope = factory.CreateScope();
|
using var scope = factory.CreateScope();
|
||||||
var pubSub = scope.ServiceProvider.GetRequiredService<PublisherSubscriptionService>();
|
var pubSub = scope.ServiceProvider.GetRequiredService<PublisherSubscriptionService>();
|
||||||
await pubSub.NotifySubscribersPostAsync(post);
|
await pubSub.NotifySubscriberPost(post);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (post.PublishedAt is not null && post.PublishedAt.Value.ToDateTimeUtc() <= DateTime.UtcNow &&
|
||||||
|
post.RepliedPost is not null)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var sender = post.Publisher;
|
||||||
|
using var scope = factory.CreateScope();
|
||||||
|
var pub = scope.ServiceProvider.GetRequiredService<PublisherService>();
|
||||||
|
var nty = scope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||||
|
var logger = scope.ServiceProvider.GetRequiredService<ILogger<PostService>>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var members = await pub.GetPublisherMembers(post.RepliedPost.PublisherId);
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
AccountService.SetCultureInfo(member.Account);
|
||||||
|
var (_, content) = ChopPostForNotification(post);
|
||||||
|
await nty.SendNotification(
|
||||||
|
member.Account,
|
||||||
|
"post.replies",
|
||||||
|
localizer["PostReplyTitle", sender.Nick],
|
||||||
|
null,
|
||||||
|
string.IsNullOrWhiteSpace(post.Title)
|
||||||
|
? localizer["PostReplyBody", sender.Nick, content]
|
||||||
|
: localizer["PostReplyContentBody", sender.Nick, post.Title, content]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
logger.LogError($"Error when sending post reactions notification: {err.Message} {err.StackTrace}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +262,6 @@ public class PostService(
|
|||||||
Post post,
|
Post post,
|
||||||
PostReaction reaction,
|
PostReaction reaction,
|
||||||
Account.Account sender,
|
Account.Account sender,
|
||||||
Account.Account? op,
|
|
||||||
bool isRemoving,
|
bool isRemoving,
|
||||||
bool isSelfReact
|
bool isSelfReact
|
||||||
)
|
)
|
||||||
@ -256,20 +303,36 @@ public class PostService(
|
|||||||
|
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
if (!isSelfReact && op is not null)
|
if (!isSelfReact)
|
||||||
{
|
_ = Task.Run(async () =>
|
||||||
AccountService.SetCultureInfo(op);
|
{
|
||||||
await nty.SendNotification(
|
using var scope = factory.CreateScope();
|
||||||
op,
|
var pub = scope.ServiceProvider.GetRequiredService<PublisherService>();
|
||||||
"posts.reactions.new",
|
var nty = scope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||||
localizer["PostReactTitle", sender.Nick],
|
var logger = scope.ServiceProvider.GetRequiredService<ILogger<PostService>>();
|
||||||
null,
|
try
|
||||||
string.IsNullOrWhiteSpace(post.Title)
|
{
|
||||||
? localizer["PostReactBody", sender.Nick, reaction.Symbol]
|
var members = await pub.GetPublisherMembers(post.PublisherId);
|
||||||
: localizer["PostReactContentBody", sender.Nick, reaction.Symbol,
|
foreach (var member in members)
|
||||||
post.Title]
|
{
|
||||||
);
|
AccountService.SetCultureInfo(member.Account);
|
||||||
}
|
await nty.SendNotification(
|
||||||
|
member.Account,
|
||||||
|
"posts.reactions.new",
|
||||||
|
localizer["PostReactTitle", sender.Nick],
|
||||||
|
null,
|
||||||
|
string.IsNullOrWhiteSpace(post.Title)
|
||||||
|
? localizer["PostReactBody", sender.Nick, reaction.Symbol]
|
||||||
|
: localizer["PostReactContentBody", sender.Nick, reaction.Symbol,
|
||||||
|
post.Title]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError($"Error when sending post reactions notification: {ex.Message} {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return isRemoving;
|
return isRemoving;
|
||||||
}
|
}
|
||||||
@ -342,15 +405,17 @@ public static class PostQueryExtensions
|
|||||||
this IQueryable<Post> source,
|
this IQueryable<Post> source,
|
||||||
Account.Account? currentUser,
|
Account.Account? currentUser,
|
||||||
List<Guid> userFriends,
|
List<Guid> userFriends,
|
||||||
|
List<Publisher.Publisher> publishers,
|
||||||
bool isListing = false
|
bool isListing = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
var publishersId = publishers.Select(e => e.Id).ToList();
|
||||||
|
|
||||||
source = isListing switch
|
source = isListing switch
|
||||||
{
|
{
|
||||||
true when currentUser is not null => source.Where(e =>
|
true when currentUser is not null => source.Where(e =>
|
||||||
e.Visibility != PostVisibility.Unlisted || e.Publisher.AccountId == currentUser.Id),
|
e.Visibility != PostVisibility.Unlisted || publishersId.Contains(e.PublisherId)),
|
||||||
true => source.Where(e => e.Visibility != PostVisibility.Unlisted),
|
true => source.Where(e => e.Visibility != PostVisibility.Unlisted),
|
||||||
_ => source
|
_ => source
|
||||||
};
|
};
|
||||||
@ -361,10 +426,11 @@ public static class PostQueryExtensions
|
|||||||
.Where(e => e.Visibility == PostVisibility.Public);
|
.Where(e => e.Visibility == PostVisibility.Public);
|
||||||
|
|
||||||
return source
|
return source
|
||||||
.Where(e => (e.PublishedAt != null && now >= e.PublishedAt) || e.Publisher.AccountId == currentUser.Id)
|
.Where(e => (e.PublishedAt != null && now >= e.PublishedAt) || publishersId.Contains(e.PublisherId))
|
||||||
.Where(e => e.Visibility != PostVisibility.Private || e.Publisher.AccountId == currentUser.Id)
|
.Where(e => e.Visibility != PostVisibility.Private || publishersId.Contains(e.PublisherId))
|
||||||
.Where(e => e.Visibility != PostVisibility.Friends ||
|
.Where(e => e.Visibility != PostVisibility.Friends ||
|
||||||
(e.Publisher.AccountId != null && userFriends.Contains(e.Publisher.AccountId.Value)) ||
|
(e.Publisher.AccountId != null && userFriends.Contains(e.Publisher.AccountId.Value)) ||
|
||||||
e.Publisher.AccountId == currentUser.Id);
|
publishersId.Contains(e.PublisherId));
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,6 +8,56 @@ namespace DysonNetwork.Sphere.Publisher;
|
|||||||
|
|
||||||
public class PublisherService(AppDatabase db, FileService fs, FileReferenceService fileRefService, ICacheService cache)
|
public class PublisherService(AppDatabase db, FileService fs, FileReferenceService fileRefService, ICacheService cache)
|
||||||
{
|
{
|
||||||
|
private const string UserPublishersCacheKey = "accounts:{0}:publishers";
|
||||||
|
|
||||||
|
public async Task<List<Publisher>> GetUserPublishers(Guid userId)
|
||||||
|
{
|
||||||
|
var cacheKey = string.Format(UserPublishersCacheKey, userId);
|
||||||
|
|
||||||
|
// Try to get publishers from the cache first
|
||||||
|
var publishers = await cache.GetAsync<List<Publisher>>(cacheKey);
|
||||||
|
if (publishers is not null)
|
||||||
|
return publishers;
|
||||||
|
|
||||||
|
// If not in cache, fetch from a database
|
||||||
|
var publishersId = await db.PublisherMembers
|
||||||
|
.Where(p => p.AccountId == userId)
|
||||||
|
.Select(p => p.PublisherId)
|
||||||
|
.ToListAsync();
|
||||||
|
publishers = await db.Publishers
|
||||||
|
.Where(p => publishersId.Contains(p.Id))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Store in a cache for 5 minutes
|
||||||
|
await cache.SetAsync(cacheKey, publishers, TimeSpan.FromMinutes(5));
|
||||||
|
|
||||||
|
return publishers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string PublisherMembersCacheKey = "publishers:{0}:members";
|
||||||
|
|
||||||
|
public async Task<List<PublisherMember>> GetPublisherMembers(Guid publisherId)
|
||||||
|
{
|
||||||
|
var cacheKey = string.Format(PublisherMembersCacheKey, publisherId);
|
||||||
|
|
||||||
|
// Try to get members from the cache first
|
||||||
|
var members = await cache.GetAsync<List<PublisherMember>>(cacheKey);
|
||||||
|
if (members is not null)
|
||||||
|
return members;
|
||||||
|
|
||||||
|
// If not in cache, fetch from a database
|
||||||
|
members = await db.PublisherMembers
|
||||||
|
.Where(p => p.PublisherId == publisherId)
|
||||||
|
.Include(p => p.Account)
|
||||||
|
.ThenInclude(p => p.Profile)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Store in cache for 5 minutes (consistent with other cache durations in the class)
|
||||||
|
await cache.SetAsync(cacheKey, members, TimeSpan.FromMinutes(5));
|
||||||
|
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Publisher> CreateIndividualPublisher(
|
public async Task<Publisher> CreateIndividualPublisher(
|
||||||
Account.Account account,
|
Account.Account account,
|
||||||
string? name,
|
string? name,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using DysonNetwork.Sphere.Account;
|
using DysonNetwork.Sphere.Account;
|
||||||
|
using DysonNetwork.Sphere.Post;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
@ -7,6 +8,7 @@ namespace DysonNetwork.Sphere.Publisher;
|
|||||||
public class PublisherSubscriptionService(
|
public class PublisherSubscriptionService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
NotificationService nty,
|
NotificationService nty,
|
||||||
|
PostService ps,
|
||||||
IStringLocalizer<Notification> localizer)
|
IStringLocalizer<Notification> localizer)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -41,7 +43,7 @@ public class PublisherSubscriptionService(
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="post">The new post</param>
|
/// <param name="post">The new post</param>
|
||||||
/// <returns>The number of subscribers notified</returns>
|
/// <returns>The number of subscribers notified</returns>
|
||||||
public async Task<int> NotifySubscribersPostAsync(Post.Post post)
|
public async Task<int> NotifySubscriberPost(Post.Post post)
|
||||||
{
|
{
|
||||||
var subscribers = await db.PublisherSubscriptions
|
var subscribers = await db.PublisherSubscriptions
|
||||||
.Include(ps => ps.Account)
|
.Include(ps => ps.Account)
|
||||||
@ -52,11 +54,7 @@ public class PublisherSubscriptionService(
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Create notification data
|
// Create notification data
|
||||||
var message = !string.IsNullOrEmpty(post.Description)
|
var (title, message) = ps.ChopPostForNotification(post);
|
||||||
? post.Description?.Length > 40 ? post.Description[..37] + "..." : post.Description
|
|
||||||
: post.Content?.Length > 100
|
|
||||||
? string.Concat(post.Content.AsSpan(0, 97), "...")
|
|
||||||
: post.Content;
|
|
||||||
|
|
||||||
// Data to include with the notification
|
// Data to include with the notification
|
||||||
var data = new Dictionary<string, object>
|
var data = new Dictionary<string, object>
|
||||||
@ -75,8 +73,8 @@ public class PublisherSubscriptionService(
|
|||||||
await nty.SendNotification(
|
await nty.SendNotification(
|
||||||
subscription.Account,
|
subscription.Account,
|
||||||
"posts.new",
|
"posts.new",
|
||||||
localizer["New post from {0}", post.Publisher.Name],
|
localizer["PostSubscriptionTitle", post.Publisher.Name, title],
|
||||||
string.IsNullOrWhiteSpace(post.Title) ? null : post.Title,
|
null,
|
||||||
message,
|
message,
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
|
@ -99,6 +99,30 @@ namespace DysonNetwork.Sphere.Resources.Localization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string PostReplyTitle {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PostReplyTitle", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string PostReplyBody {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PostReplyBody", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string PostReplyContentBody {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PostReplyContentBody", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string PostOnlyMedia {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PostOnlyMedia", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static string AuthCodeTitle {
|
internal static string AuthCodeTitle {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("AuthCodeTitle", resourceCulture);
|
return ResourceManager.GetString("AuthCodeTitle", resourceCulture);
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<value>You just got invited to join {0}</value>
|
<value>You just got invited to join {0}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PostSubscriptionTitle" xml:space="preserve">
|
<data name="PostSubscriptionTitle" xml:space="preserve">
|
||||||
<value>{0} just posted</value>
|
<value>{0} just posted {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PostReactTitle" xml:space="preserve">
|
<data name="PostReactTitle" xml:space="preserve">
|
||||||
<value>{0} reacted your post</value>
|
<value>{0} reacted your post</value>
|
||||||
@ -45,6 +45,18 @@
|
|||||||
<data name="PostReactContentBody" xml:space="preserve">
|
<data name="PostReactContentBody" xml:space="preserve">
|
||||||
<value>{0} added a reaction {1} to your post {2}</value>
|
<value>{0} added a reaction {1} to your post {2}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PostReplyTitle" xml:space="preserve">
|
||||||
|
<value>{0} replied your post</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostReplyBody">
|
||||||
|
<value>{0} replied: {1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostReplyContentBody">
|
||||||
|
<value>{0} replied post {1}: {2}</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostOnlyMedia" xml:space="preserve">
|
||||||
|
<value>shared media</value>
|
||||||
|
</data>
|
||||||
<data name="AuthCodeTitle" xml:space="preserve">
|
<data name="AuthCodeTitle" xml:space="preserve">
|
||||||
<value>Disposable Verification Code</value>
|
<value>Disposable Verification Code</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -38,6 +38,18 @@
|
|||||||
<data name="PostReactContentBody" xml:space="preserve">
|
<data name="PostReactContentBody" xml:space="preserve">
|
||||||
<value>{0} 给你的帖子添加了一个 {1} 的反应 {2}</value>
|
<value>{0} 给你的帖子添加了一个 {1} 的反应 {2}</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PostReplyTitle" xml:space="preserve">
|
||||||
|
<value>{0} 回复了你的帖子</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostReplyBody">
|
||||||
|
<value>{0}:{1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostReplyContentBody">
|
||||||
|
<value>{0} 回复了帖子 {1}: {2}</value>
|
||||||
|
</data>
|
||||||
|
<data name="PostOnlyMedia" xml:space="preserve">
|
||||||
|
<value>分享媒体</value>
|
||||||
|
</data>
|
||||||
<data name="AuthCodeTitle" xml:space="preserve">
|
<data name="AuthCodeTitle" xml:space="preserve">
|
||||||
<value>一次性验证码</value>
|
<value>一次性验证码</value>
|
||||||
</data>
|
</data>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user