♻️ I have no idea what am I doing. Might be mixing stuff
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.Activity;
|
||||
using DysonNetwork.Sphere.Storage;
|
||||
using NodaTime;
|
||||
using NpgsqlTypes;
|
||||
|
||||
@@ -52,8 +53,6 @@ public class Post : ModelBase, IIdentifiedResource, IActivity
|
||||
public Guid? ForwardedPostId { get; set; }
|
||||
public Post? ForwardedPost { get; set; }
|
||||
|
||||
// Outdated fields, keep for backward compability
|
||||
public ICollection<CloudFile> OutdatedAttachments { get; set; } = new List<CloudFile>();
|
||||
[Column(TypeName = "jsonb")] public List<CloudFileReferenceObject> Attachments { get; set; } = [];
|
||||
|
||||
[JsonIgnore] public NpgsqlTsVector SearchVector { get; set; } = null!;
|
||||
@@ -69,7 +68,7 @@ public class Post : ModelBase, IIdentifiedResource, IActivity
|
||||
[JsonIgnore] public bool Empty => Content == null && Attachments.Count == 0 && ForwardedPostId == null;
|
||||
[NotMapped] public bool IsTruncated { get; set; } = false;
|
||||
|
||||
public string ResourceIdentifier => $"post/{Id}";
|
||||
public string ResourceIdentifier => $"post:{Id}";
|
||||
|
||||
public Activity.Activity ToActivity()
|
||||
{
|
||||
@@ -130,5 +129,4 @@ public class PostReaction : ModelBase
|
||||
public Guid PostId { get; set; }
|
||||
[JsonIgnore] public Post Post { get; set; } = null!;
|
||||
public Guid AccountId { get; set; }
|
||||
public Account.Account Account { get; set; } = null!;
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ public class PostController(
|
||||
)
|
||||
{
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account.Account;
|
||||
var currentUser = currentUserValue as Account;
|
||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||
|
||||
@@ -70,7 +70,7 @@ public class PostController(
|
||||
return RedirectToPage("/Posts/PostDetail", new { PostId = id });
|
||||
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account.Account;
|
||||
var currentUser = currentUserValue as Account;
|
||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||
|
||||
@@ -102,7 +102,7 @@ public class PostController(
|
||||
return BadRequest("Search query cannot be empty");
|
||||
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account.Account;
|
||||
var currentUser = currentUserValue as Account;
|
||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||
|
||||
@@ -139,7 +139,7 @@ public class PostController(
|
||||
[FromQuery] int take = 20)
|
||||
{
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account.Account;
|
||||
var currentUser = currentUserValue as Account;
|
||||
var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser);
|
||||
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id);
|
||||
|
||||
@@ -201,7 +201,7 @@ public class PostController(
|
||||
request.Content = TextSanitizer.Sanitize(request.Content);
|
||||
if (string.IsNullOrWhiteSpace(request.Content) && request.Attachments is { Count: 0 })
|
||||
return BadRequest("Content is required.");
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
Publisher.Publisher? publisher;
|
||||
if (publisherName is null)
|
||||
@@ -287,7 +287,7 @@ public class PostController(
|
||||
public async Task<ActionResult<PostReaction>> ReactPost(Guid id, [FromBody] PostReactionRequest request)
|
||||
{
|
||||
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
if (currentUserValue is not Account.Account currentUser) return Unauthorized();
|
||||
if (currentUserValue is not Account currentUser) return Unauthorized();
|
||||
var userFriends = await rels.ListAccountFriends(currentUser);
|
||||
var userPublishers = await pub.GetUserPublishers(currentUser.Id);
|
||||
|
||||
@@ -336,7 +336,7 @@ public class PostController(
|
||||
request.Content = TextSanitizer.Sanitize(request.Content);
|
||||
if (string.IsNullOrWhiteSpace(request.Content) && request.Attachments is { Count: 0 })
|
||||
return BadRequest("Content is required.");
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
var post = await db.Posts
|
||||
.Where(e => e.Id == id)
|
||||
@@ -382,7 +382,7 @@ public class PostController(
|
||||
[HttpDelete("{id:guid}")]
|
||||
public async Task<ActionResult<Post>> DeletePost(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
|
||||
var post = await db.Posts
|
||||
.Where(e => e.Id == id)
|
||||
|
@@ -1,9 +1,11 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using DysonNetwork.Sphere.Account;
|
||||
using DysonNetwork.Sphere.Connection.WebReader;
|
||||
using DysonNetwork.Shared;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.WebReader;
|
||||
using DysonNetwork.Sphere.Localization;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using DysonNetwork.Sphere.Storage;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using NodaTime;
|
||||
@@ -12,13 +14,14 @@ namespace DysonNetwork.Sphere.Post;
|
||||
|
||||
public partial class PostService(
|
||||
AppDatabase db,
|
||||
FileReferenceService fileRefService,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
IServiceScopeFactory factory,
|
||||
FlushBufferService flushBuffer,
|
||||
ICacheService cache,
|
||||
WebReaderService reader,
|
||||
ILogger<PostService> logger
|
||||
ILogger<PostService> logger,
|
||||
FileService.FileServiceClient files,
|
||||
FileReferenceService.FileReferenceServiceClient fileRefs,
|
||||
WebReaderService reader
|
||||
)
|
||||
{
|
||||
private const string PostFileUsageIdentifier = "post";
|
||||
@@ -69,7 +72,7 @@ public partial class PostService(
|
||||
}
|
||||
|
||||
public async Task<Post> PostAsync(
|
||||
Account.Account user,
|
||||
Account user,
|
||||
Post post,
|
||||
List<string>? attachments = null,
|
||||
List<string>? tags = null,
|
||||
@@ -91,8 +94,11 @@ public partial class PostService(
|
||||
|
||||
if (attachments is not null)
|
||||
{
|
||||
post.Attachments = (await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync())
|
||||
.Select(x => x.ToReferenceObject()).ToList();
|
||||
var queryRequest = new GetFileBatchRequest();
|
||||
queryRequest.Ids.AddRange(attachments);
|
||||
var queryResponse = await files.GetFileBatchAsync(queryRequest);
|
||||
|
||||
post.Attachments = queryResponse.Files.Select(CloudFileReferenceObject.FromProtoValue).ToList();
|
||||
// Re-order the list to match the id list places
|
||||
post.Attachments = attachments
|
||||
.Select(id => post.Attachments.First(a => a.Id == id))
|
||||
@@ -128,17 +134,16 @@ public partial class PostService(
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// Create file references for each attachment
|
||||
if (post.Attachments.Any())
|
||||
if (post.Attachments.Count != 0)
|
||||
{
|
||||
var postResourceId = $"post:{post.Id}";
|
||||
foreach (var file in post.Attachments)
|
||||
var request = new CreateReferenceBatchRequest
|
||||
{
|
||||
await fileRefService.CreateReferenceAsync(
|
||||
file.Id,
|
||||
PostFileUsageIdentifier,
|
||||
postResourceId
|
||||
);
|
||||
}
|
||||
Usage = PostFileUsageIdentifier,
|
||||
ResourceId = post.ResourceIdentifier,
|
||||
};
|
||||
request.FilesId.AddRange(post.Attachments.Select(a => a.Id));
|
||||
await fileRefs.CreateReferenceBatchAsync(request);
|
||||
}
|
||||
|
||||
if (post.PublishedAt is not null && post.PublishedAt.Value.ToDateTimeUtc() <= DateTime.UtcNow)
|
||||
@@ -157,24 +162,33 @@ public partial class PostService(
|
||||
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>>();
|
||||
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
||||
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||
try
|
||||
{
|
||||
var members = await pub.GetPublisherMembers(post.RepliedPost.PublisherId);
|
||||
foreach (var member in members)
|
||||
var queryRequest = new GetAccountBatchRequest();
|
||||
queryRequest.Id.AddRange(members.Select(m => m.AccountId.ToString()));
|
||||
var queryResponse = await accounts.GetAccountBatchAsync(queryRequest);
|
||||
foreach (var member in queryResponse.Accounts)
|
||||
{
|
||||
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],
|
||||
actionUri: $"/posts/{post.Id}"
|
||||
if (member is null) continue;
|
||||
CultureService.SetCultureInfo(member);
|
||||
await nty.SendPushNotificationToUserAsync(
|
||||
new SendPushNotificationToUserRequest
|
||||
{
|
||||
UserId = member.Id,
|
||||
Notification = new PushNotification
|
||||
{
|
||||
Topic = "post.replies",
|
||||
Title = localizer["PostReplyTitle", sender.Nick],
|
||||
Body = string.IsNullOrWhiteSpace(post.Title)
|
||||
? localizer["PostReplyBody", sender.Nick, ChopPostForNotification(post).content]
|
||||
: localizer["PostReplyContentBody", sender.Nick, post.Title, ChopPostForNotification(post).content],
|
||||
IsSavable = true,
|
||||
ActionUri = $"/posts/{post.Id}"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -218,18 +232,20 @@ public partial class PostService(
|
||||
var postResourceId = $"post:{post.Id}";
|
||||
|
||||
// Update resource references using the new file list
|
||||
await fileRefService.UpdateResourceFilesAsync(
|
||||
postResourceId,
|
||||
attachments,
|
||||
PostFileUsageIdentifier
|
||||
);
|
||||
var request = new UpdateResourceFilesRequest
|
||||
{
|
||||
ResourceId = postResourceId,
|
||||
Usage = PostFileUsageIdentifier,
|
||||
};
|
||||
request.FileIds.AddRange(attachments);
|
||||
await fileRefs.UpdateResourceFilesAsync(request);
|
||||
|
||||
// Update post attachments by getting files from database
|
||||
var files = await db.Files
|
||||
.Where(f => attachments.Contains(f.Id))
|
||||
.ToListAsync();
|
||||
var queryRequest = new GetFileBatchRequest();
|
||||
queryRequest.Ids.AddRange(attachments);
|
||||
var queryResponse = await files.GetFileBatchAsync(queryRequest);
|
||||
|
||||
post.Attachments = files.Select(x => x.ToReferenceObject()).ToList();
|
||||
post.Attachments = queryResponse.Files.Select(CloudFileReferenceObject.FromProtoValue).ToList();
|
||||
}
|
||||
|
||||
if (tags is not null)
|
||||
@@ -369,10 +385,10 @@ public partial class PostService(
|
||||
|
||||
public async Task DeletePostAsync(Post post)
|
||||
{
|
||||
var postResourceId = $"post:{post.Id}";
|
||||
|
||||
// Delete all file references for this post
|
||||
await fileRefService.DeleteResourceReferencesAsync(postResourceId);
|
||||
await fileRefs.DeleteResourceReferencesAsync(
|
||||
new DeleteResourceReferencesRequest { ResourceId = post.ResourceIdentifier }
|
||||
);
|
||||
|
||||
db.Posts.Remove(post);
|
||||
await db.SaveChangesAsync();
|
||||
@@ -391,7 +407,7 @@ public partial class PostService(
|
||||
public async Task<bool> ModifyPostVotes(
|
||||
Post post,
|
||||
PostReaction reaction,
|
||||
Account.Account sender,
|
||||
Account sender,
|
||||
bool isRemoving,
|
||||
bool isSelfReact
|
||||
)
|
||||
@@ -438,24 +454,35 @@ public partial class PostService(
|
||||
{
|
||||
using var scope = factory.CreateScope();
|
||||
var pub = scope.ServiceProvider.GetRequiredService<PublisherService>();
|
||||
var nty = scope.ServiceProvider.GetRequiredService<NotificationService>();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<PostService>>();
|
||||
var nty = scope.ServiceProvider.GetRequiredService<PusherService.PusherServiceClient>();
|
||||
var accounts = scope.ServiceProvider.GetRequiredService<AccountService.AccountServiceClient>();
|
||||
try
|
||||
{
|
||||
var members = await pub.GetPublisherMembers(post.PublisherId);
|
||||
foreach (var member in members)
|
||||
var queryRequest = new GetAccountBatchRequest();
|
||||
queryRequest.Id.AddRange(members.Select(m => m.AccountId.ToString()));
|
||||
var queryResponse = await accounts.GetAccountBatchAsync(queryRequest);
|
||||
foreach (var member in queryResponse.Accounts)
|
||||
{
|
||||
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],
|
||||
actionUri: $"/posts/{post.Id}"
|
||||
if (member is null) continue;
|
||||
CultureService.SetCultureInfo(member);
|
||||
|
||||
await nty.SendPushNotificationToUserAsync(
|
||||
new SendPushNotificationToUserRequest
|
||||
{
|
||||
UserId = member.Id,
|
||||
Notification = new PushNotification
|
||||
{
|
||||
Topic = "posts.reactions.new",
|
||||
Title = localizer["PostReactTitle", sender.Nick],
|
||||
Body = string.IsNullOrWhiteSpace(post.Title)
|
||||
? localizer["PostReactBody", sender.Nick, reaction.Symbol]
|
||||
: localizer["PostReactContentBody", sender.Nick, reaction.Symbol,
|
||||
post.Title],
|
||||
IsSavable = true,
|
||||
ActionUri = $"/posts/{post.Id}"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -563,7 +590,7 @@ public partial class PostService(
|
||||
return posts;
|
||||
}
|
||||
|
||||
public async Task<List<Post>> LoadInteractive(List<Post> posts, Account.Account? currentUser = null)
|
||||
public async Task<List<Post>> LoadInteractive(List<Post> posts, Account? currentUser = null)
|
||||
{
|
||||
if (posts.Count == 0) return posts;
|
||||
|
||||
@@ -586,7 +613,7 @@ public partial class PostService(
|
||||
|
||||
// Track view for each post in the list
|
||||
if (currentUser != null)
|
||||
await IncreaseViewCount(post.Id, currentUser.Id.ToString());
|
||||
await IncreaseViewCount(post.Id, currentUser.Id);
|
||||
else
|
||||
await IncreaseViewCount(post.Id);
|
||||
}
|
||||
@@ -605,8 +632,11 @@ public partial class PostService(
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<List<Post>> LoadPostInfo(List<Post> posts, Account.Account? currentUser = null,
|
||||
bool truncate = false)
|
||||
public async Task<List<Post>> LoadPostInfo(
|
||||
List<Post> posts,
|
||||
Account? currentUser = null,
|
||||
bool truncate = false
|
||||
)
|
||||
{
|
||||
if (posts.Count == 0) return posts;
|
||||
|
||||
@@ -619,7 +649,7 @@ public partial class PostService(
|
||||
return posts;
|
||||
}
|
||||
|
||||
public async Task<Post> LoadPostInfo(Post post, Account.Account? currentUser = null, bool truncate = false)
|
||||
public async Task<Post> LoadPostInfo(Post post, Account? currentUser = null, bool truncate = false)
|
||||
{
|
||||
// Convert single post to list, process it, then return the single post
|
||||
var posts = await LoadPostInfo([post], currentUser, truncate);
|
||||
@@ -631,7 +661,7 @@ public static class PostQueryExtensions
|
||||
{
|
||||
public static IQueryable<Post> FilterWithVisibility(
|
||||
this IQueryable<Post> source,
|
||||
Account.Account? currentUser,
|
||||
Account? currentUser,
|
||||
List<Guid> userFriends,
|
||||
List<Publisher.Publisher> publishers,
|
||||
bool isListing = false
|
||||
|
Reference in New Issue
Block a user