✨ Provide real user and posts data for the thinking
This commit is contained in:
@@ -12,7 +12,7 @@ namespace DysonNetwork.Sphere.Activity;
|
||||
public class ActivityService(
|
||||
AppDatabase db,
|
||||
Publisher.PublisherService pub,
|
||||
PostService ps,
|
||||
Post.PostService ps,
|
||||
RemoteRealmService rs,
|
||||
DiscoveryService ds,
|
||||
AccountService.AccountServiceClient accounts
|
||||
|
||||
@@ -125,7 +125,7 @@ public class PostController(
|
||||
if (realm != null)
|
||||
query = query.Where(p => p.RealmId == realm.Id);
|
||||
if (type != null)
|
||||
query = query.Where(p => p.Type == (PostType)type);
|
||||
query = query.Where(p => p.Type == (Shared.Models.PostType)type);
|
||||
if (categories is { Count: > 0 })
|
||||
query = query.Where(p => p.Categories.Any(c => categories.Contains(c.Slug)));
|
||||
if (tags is { Count: > 0 })
|
||||
@@ -139,10 +139,10 @@ public class PostController(
|
||||
switch (pinned)
|
||||
{
|
||||
case true when realm != null:
|
||||
query = query.Where(p => p.PinMode == PostPinMode.RealmPage);
|
||||
query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.RealmPage);
|
||||
break;
|
||||
case true when publisher != null:
|
||||
query = query.Where(p => p.PinMode == PostPinMode.PublisherPage);
|
||||
query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.PublisherPage);
|
||||
break;
|
||||
case true:
|
||||
return BadRequest(
|
||||
@@ -360,7 +360,7 @@ public class PostController(
|
||||
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
var posts = await db.Posts
|
||||
.Where(e => e.RepliedPostId == id && e.PinMode == PostPinMode.ReplyPage)
|
||||
.Where(e => e.RepliedPostId == id && e.PinMode == Shared.Models.PostPinMode.ReplyPage)
|
||||
.OrderByDescending(p => p.CreatedAt)
|
||||
.FilterWithVisibility(currentUser, userFriends, userPublishers)
|
||||
.ToListAsync();
|
||||
@@ -425,9 +425,9 @@ public class PostController(
|
||||
[MaxLength(4096)] public string? Description { get; set; }
|
||||
[MaxLength(1024)] public string? Slug { get; set; }
|
||||
public string? Content { get; set; }
|
||||
public PostVisibility? Visibility { get; set; } = PostVisibility.Public;
|
||||
public PostType? Type { get; set; }
|
||||
public PostEmbedView? EmbedView { get; set; }
|
||||
public Shared.Models.PostVisibility? Visibility { get; set; } = Shared.Models.PostVisibility.Public;
|
||||
public Shared.Models.PostType? Type { get; set; }
|
||||
public Shared.Models.PostEmbedView? EmbedView { get; set; }
|
||||
[MaxLength(16)] public List<string>? Tags { get; set; }
|
||||
[MaxLength(8)] public List<string>? Categories { get; set; }
|
||||
[MaxLength(32)] public List<string>? Attachments { get; set; }
|
||||
@@ -477,9 +477,9 @@ public class PostController(
|
||||
Description = request.Description,
|
||||
Slug = request.Slug,
|
||||
Content = request.Content,
|
||||
Visibility = request.Visibility ?? PostVisibility.Public,
|
||||
Visibility = request.Visibility ?? Shared.Models.PostVisibility.Public,
|
||||
PublishedAt = request.PublishedAt,
|
||||
Type = request.Type ?? PostType.Moment,
|
||||
Type = request.Type ?? Shared.Models.PostType.Moment,
|
||||
Meta = request.Meta,
|
||||
EmbedView = request.EmbedView,
|
||||
Publisher = publisher,
|
||||
@@ -565,7 +565,7 @@ public class PostController(
|
||||
public class PostReactionRequest
|
||||
{
|
||||
[MaxLength(256)] public string Symbol { get; set; } = null!;
|
||||
public PostReactionAttitude Attitude { get; set; }
|
||||
public Shared.Models.PostReactionAttitude Attitude { get; set; }
|
||||
}
|
||||
|
||||
public static readonly List<string> ReactionsAllowedDefault =
|
||||
@@ -638,7 +638,7 @@ public class PostController(
|
||||
public class PostAwardRequest
|
||||
{
|
||||
public decimal Amount { get; set; }
|
||||
public PostReactionAttitude Attitude { get; set; }
|
||||
public Shared.Models.PostReactionAttitude Attitude { get; set; }
|
||||
[MaxLength(4096)] public string? Message { get; set; }
|
||||
}
|
||||
|
||||
@@ -671,7 +671,7 @@ public class PostController(
|
||||
public async Task<ActionResult<PostAwardResponse>> AwardPost(Guid id, [FromBody] PostAwardRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||
if (request.Attitude == PostReactionAttitude.Neutral)
|
||||
if (request.Attitude == Shared.Models.PostReactionAttitude.Neutral)
|
||||
return BadRequest("You cannot create a neutral post award");
|
||||
|
||||
var friendsResponse =
|
||||
@@ -714,7 +714,7 @@ public class PostController(
|
||||
|
||||
public class PostPinRequest
|
||||
{
|
||||
[Required] public PostPinMode Mode { get; set; }
|
||||
[Required] public Shared.Models.PostPinMode Mode { get; set; }
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/pin")]
|
||||
@@ -734,7 +734,7 @@ public class PostController(
|
||||
if (!await pub.IsMemberWithRole(post.PublisherId, accountId, PublisherMemberRole.Editor))
|
||||
return StatusCode(403, "You are not an editor of this publisher");
|
||||
|
||||
if (request.Mode == PostPinMode.RealmPage && post.RealmId != null)
|
||||
if (request.Mode == Shared.Models.PostPinMode.RealmPage && post.RealmId != null)
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, new List<int> { RealmMemberRole.Moderator }))
|
||||
return StatusCode(403, "You are not a moderator of this realm");
|
||||
@@ -782,7 +782,7 @@ public class PostController(
|
||||
if (!await pub.IsMemberWithRole(post.PublisherId, accountId, PublisherMemberRole.Editor))
|
||||
return StatusCode(403, "You are not an editor of this publisher");
|
||||
|
||||
if (post is { PinMode: PostPinMode.RealmPage, RealmId: not null })
|
||||
if (post is { PinMode: Shared.Models.PostPinMode.RealmPage, RealmId: not null })
|
||||
{
|
||||
if (!await rs.IsMemberWithRole(post.RealmId.Value, accountId, new List<int> { RealmMemberRole.Moderator }))
|
||||
return StatusCode(403, "You are not a moderator of this realm");
|
||||
|
||||
@@ -290,7 +290,7 @@ public partial class PostService(
|
||||
|
||||
public async Task<SnPost> PreviewPostLinkAsync(SnPost item)
|
||||
{
|
||||
if (item.Type != PostType.Moment || string.IsNullOrEmpty(item.Content)) return item;
|
||||
if (item.Type != Shared.Models.PostType.Moment || string.IsNullOrEmpty(item.Content)) return item;
|
||||
|
||||
// Find all URLs in the content
|
||||
var matches = GetLinkRegex().Matches(item.Content);
|
||||
@@ -420,12 +420,12 @@ public partial class PostService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SnPost> PinPostAsync(SnPost post, Account currentUser, PostPinMode pinMode)
|
||||
public async Task<SnPost> PinPostAsync(SnPost post, Account currentUser, Shared.Models.PostPinMode pinMode)
|
||||
{
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
if (post.RepliedPostId != null)
|
||||
{
|
||||
if (pinMode != PostPinMode.ReplyPage)
|
||||
if (pinMode != Shared.Models.PostPinMode.ReplyPage)
|
||||
throw new InvalidOperationException("Replies can only be pinned in the reply page.");
|
||||
if (post.RepliedPost == null) throw new ArgumentNullException(nameof(post.RepliedPost));
|
||||
|
||||
@@ -516,11 +516,11 @@ public partial class PostService(
|
||||
|
||||
switch (reaction.Attitude)
|
||||
{
|
||||
case PostReactionAttitude.Positive:
|
||||
case Shared.Models.PostReactionAttitude.Positive:
|
||||
if (isRemoving) post.Upvotes--;
|
||||
else post.Upvotes++;
|
||||
break;
|
||||
case PostReactionAttitude.Negative:
|
||||
case Shared.Models.PostReactionAttitude.Negative:
|
||||
if (isRemoving) post.Downvotes--;
|
||||
else post.Downvotes++;
|
||||
break;
|
||||
@@ -771,7 +771,7 @@ public partial class PostService(
|
||||
if (currentUser is null)
|
||||
{
|
||||
// Anonymous user can only view public posts that are published
|
||||
return post.PublishedAt != null && now >= post.PublishedAt && post.Visibility == PostVisibility.Public;
|
||||
return post.PublishedAt != null && now >= post.PublishedAt && post.Visibility == Shared.Models.PostVisibility.Public;
|
||||
}
|
||||
|
||||
// Check publication status - either published or user is member
|
||||
@@ -781,10 +781,10 @@ public partial class PostService(
|
||||
return false;
|
||||
|
||||
// Check visibility
|
||||
if (post.Visibility == PostVisibility.Private && !isMember)
|
||||
if (post.Visibility == Shared.Models.PostVisibility.Private && !isMember)
|
||||
return false;
|
||||
|
||||
if (post.Visibility == PostVisibility.Friends &&
|
||||
if (post.Visibility == Shared.Models.PostVisibility.Friends &&
|
||||
!(post.Publisher.AccountId.HasValue && userFriends.Contains(post.Publisher.AccountId.Value) || isMember))
|
||||
return false;
|
||||
|
||||
@@ -843,7 +843,7 @@ public partial class PostService(
|
||||
var periodEnd = today.InUtc().Date.AtStartOfDayInZone(DateTimeZone.Utc).ToInstant();
|
||||
|
||||
var postsInPeriod = await db.Posts
|
||||
.Where(e => e.Visibility == PostVisibility.Public)
|
||||
.Where(e => e.Visibility == Shared.Models.PostVisibility.Public)
|
||||
.Where(e => e.CreatedAt >= periodStart && e.CreatedAt < periodEnd)
|
||||
.Select(e => e.Id)
|
||||
.ToListAsync();
|
||||
@@ -854,7 +854,7 @@ public partial class PostService(
|
||||
.Select(e => new
|
||||
{
|
||||
PostId = e.Key,
|
||||
Score = e.Sum(r => r.Attitude == PostReactionAttitude.Positive ? 1 : -1)
|
||||
Score = e.Sum(r => r.Attitude == Shared.Models.PostReactionAttitude.Positive ? 1 : -1)
|
||||
})
|
||||
.ToDictionaryAsync(e => e.PostId, e => e.Score);
|
||||
|
||||
@@ -928,7 +928,7 @@ public partial class PostService(
|
||||
Guid postId,
|
||||
Guid accountId,
|
||||
decimal amount,
|
||||
PostReactionAttitude attitude,
|
||||
Shared.Models.PostReactionAttitude attitude,
|
||||
string? message
|
||||
)
|
||||
{
|
||||
@@ -947,7 +947,7 @@ public partial class PostService(
|
||||
db.PostAwards.Add(award);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
var delta = award.Attitude == PostReactionAttitude.Positive ? amount : -amount;
|
||||
var delta = award.Attitude == Shared.Models.PostReactionAttitude.Positive ? amount : -amount;
|
||||
|
||||
await db.Posts.Where(p => p.Id == postId)
|
||||
.ExecuteUpdateAsync(s => s.SetProperty(p => p.AwardedScore, p => p.AwardedScore + delta));
|
||||
@@ -1017,20 +1017,20 @@ public static class PostQueryExtensions
|
||||
source = isListing switch
|
||||
{
|
||||
true when currentUser is not null => source.Where(e =>
|
||||
e.Visibility != PostVisibility.Unlisted || publishersId.Contains(e.PublisherId)),
|
||||
true => source.Where(e => e.Visibility != PostVisibility.Unlisted),
|
||||
e.Visibility != Shared.Models.PostVisibility.Unlisted || publishersId.Contains(e.PublisherId)),
|
||||
true => source.Where(e => e.Visibility != Shared.Models.PostVisibility.Unlisted),
|
||||
_ => source
|
||||
};
|
||||
|
||||
if (currentUser is null)
|
||||
return source
|
||||
.Where(e => e.PublishedAt != null && now >= e.PublishedAt)
|
||||
.Where(e => e.Visibility == PostVisibility.Public);
|
||||
.Where(e => e.Visibility == Shared.Models.PostVisibility.Public);
|
||||
|
||||
return source
|
||||
.Where(e => (e.PublishedAt != null && now >= e.PublishedAt) || publishersId.Contains(e.PublisherId))
|
||||
.Where(e => e.Visibility != PostVisibility.Private || publishersId.Contains(e.PublisherId))
|
||||
.Where(e => e.Visibility != PostVisibility.Friends ||
|
||||
.Where(e => e.Visibility != Shared.Models.PostVisibility.Private || publishersId.Contains(e.PublisherId))
|
||||
.Where(e => e.Visibility != Shared.Models.PostVisibility.Friends ||
|
||||
(e.Publisher.AccountId != null && userFriends.Contains(e.Publisher.AccountId.Value)) ||
|
||||
publishersId.Contains(e.PublisherId));
|
||||
}
|
||||
|
||||
252
DysonNetwork.Sphere/Post/PostServiceGrpc.cs
Normal file
252
DysonNetwork.Sphere/Post/PostServiceGrpc.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Grpc.Core;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DysonNetwork.Sphere.Post;
|
||||
|
||||
public class PostServiceGrpc(AppDatabase db, PostService ps) : Shared.Proto.PostService.PostServiceBase
|
||||
{
|
||||
public override async Task<Shared.Proto.Post> GetPost(GetPostRequest request, ServerCallContext context)
|
||||
{
|
||||
if (!Guid.TryParse(request.Id, out var id))
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument, "invalid post id"));
|
||||
|
||||
var post = await db.Posts
|
||||
.Include(p => p.Publisher)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Categories)
|
||||
.Include(p => p.RepliedPost)
|
||||
.Include(p => p.ForwardedPost)
|
||||
.Include(p => p.FeaturedRecords)
|
||||
.FilterWithVisibility(null, [], [])
|
||||
.FirstOrDefaultAsync(p => p.Id == id);
|
||||
|
||||
if (post == null) throw new RpcException(new Status(StatusCode.NotFound, "post not found"));
|
||||
|
||||
post = await ps.LoadPostInfo(post);
|
||||
|
||||
return post.ToProtoValue();
|
||||
}
|
||||
|
||||
public override async Task<GetPostBatchResponse> GetPostBatch(GetPostBatchRequest request, ServerCallContext context)
|
||||
{
|
||||
var ids = request.Ids
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s) && Guid.TryParse(s, out _))
|
||||
.Select(Guid.Parse)
|
||||
.ToList();
|
||||
|
||||
if (ids.Count == 0) return new GetPostBatchResponse();
|
||||
|
||||
var posts = await db.Posts
|
||||
.Include(p => p.Publisher)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Categories)
|
||||
.Include(p => p.RepliedPost)
|
||||
.Include(p => p.ForwardedPost)
|
||||
.Include(p => p.FeaturedRecords)
|
||||
.Include(p => p.Awards)
|
||||
.Where(p => ids.Contains(p.Id))
|
||||
.FilterWithVisibility(null, [], [])
|
||||
.ToListAsync();
|
||||
|
||||
posts = await ps.LoadPostInfo(posts, null);
|
||||
|
||||
var resp = new GetPostBatchResponse();
|
||||
resp.Posts.AddRange(posts.Select(p => p.ToProtoValue()));
|
||||
return resp;
|
||||
}
|
||||
|
||||
public override async Task<SearchPostsResponse> SearchPosts(SearchPostsRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = db.Posts
|
||||
.Include(p => p.Publisher)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Categories)
|
||||
.Include(p => p.RepliedPost)
|
||||
.Include(p => p.ForwardedPost)
|
||||
.Include(p => p.Attachments)
|
||||
.Include(p => p.Awards)
|
||||
.Include(p => p.Reactions)
|
||||
.Include(p => p.FeaturedRecords)
|
||||
.Where(p => p.DeletedAt == null) // Only active posts
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Query))
|
||||
{
|
||||
// Simple search, assuming full-text search or title/content contains
|
||||
query = query.Where(p =>
|
||||
EF.Functions.ILike(p.Title, $"%{request.Query}%") ||
|
||||
EF.Functions.ILike(p.Content, $"%{request.Query}%") ||
|
||||
EF.Functions.ILike(p.Description, $"%{request.Query}%"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.PublisherId) && Guid.TryParse(request.PublisherId, out var pid))
|
||||
{
|
||||
query = query.Where(p => p.PublisherId == pid);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.RealmId) && Guid.TryParse(request.RealmId, out var rid))
|
||||
{
|
||||
query = query.Where(p => p.RealmId == rid);
|
||||
}
|
||||
|
||||
query = query.FilterWithVisibility(null, [], []);
|
||||
|
||||
var totalSize = await query.CountAsync();
|
||||
|
||||
// Apply pagination
|
||||
var pageSize = request.PageSize > 0 ? request.PageSize : 20;
|
||||
var pageToken = request.PageToken;
|
||||
var offset = string.IsNullOrEmpty(pageToken) ? 0 : int.Parse(pageToken);
|
||||
|
||||
var posts = await query
|
||||
.OrderByDescending(p => p.PublishedAt ?? p.CreatedAt)
|
||||
.Skip(offset)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
posts = await ps.LoadPostInfo(posts, null, true);
|
||||
|
||||
var nextToken = offset + pageSize < totalSize ? (offset + pageSize).ToString() : string.Empty;
|
||||
|
||||
var resp = new SearchPostsResponse();
|
||||
resp.Posts.AddRange(posts.Select(p => p.ToProtoValue()));
|
||||
resp.NextPageToken = nextToken;
|
||||
resp.TotalSize = totalSize;
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
public override async Task<ListPostsResponse> ListPosts(ListPostsRequest request, ServerCallContext context)
|
||||
{
|
||||
var query = db.Posts
|
||||
.Include(p => p.Publisher)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Categories)
|
||||
.Include(p => p.RepliedPost)
|
||||
.Include(p => p.ForwardedPost)
|
||||
.Include(p => p.Awards)
|
||||
.Include(p => p.FeaturedRecords)
|
||||
.Where(p => p.DeletedAt == null)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.PublisherId) && Guid.TryParse(request.PublisherId, out var pid))
|
||||
{
|
||||
query = query.Where(p => p.PublisherId == pid);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.RealmId) && Guid.TryParse(request.RealmId, out var rid))
|
||||
{
|
||||
query = query.Where(p => p.RealmId == rid);
|
||||
}
|
||||
|
||||
if (request.Categories.Count > 0)
|
||||
{
|
||||
query = query.Where(p => p.Categories.Any(c => request.Categories.Contains(c.Slug)));
|
||||
}
|
||||
|
||||
if (request.Tags.Count > 0)
|
||||
{
|
||||
query = query.Where(p => p.Tags.Any(c => request.Tags.Contains(c.Slug)));
|
||||
}
|
||||
|
||||
// TODO: Add types filtering when proto is regenerated
|
||||
// if (request.Types.Count > 0)
|
||||
// {
|
||||
// var types = request.Types.Select(t => (Shared.Models.PostType)t).Distinct();
|
||||
// query = query.Where(p => types.Contains(p.Type));
|
||||
// }
|
||||
|
||||
if (request.OnlyMedia)
|
||||
{
|
||||
query = query.Where(e => e.Attachments.Count > 0);
|
||||
}
|
||||
|
||||
// Pinned filtering
|
||||
switch (request.Pinned)
|
||||
{
|
||||
case Shared.Proto.PostPinMode.RealmPage when !string.IsNullOrWhiteSpace(request.RealmId):
|
||||
query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.RealmPage);
|
||||
break;
|
||||
case Shared.Proto.PostPinMode.PublisherPage when !string.IsNullOrWhiteSpace(request.PublisherId):
|
||||
query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.PublisherPage);
|
||||
break;
|
||||
case Shared.Proto.PostPinMode.ReplyPage:
|
||||
query = query.Where(p => p.PinMode == Shared.Models.PostPinMode.ReplyPage);
|
||||
break;
|
||||
default:
|
||||
if (request.Pinned != null)
|
||||
{
|
||||
// Specific pinned mode but conditions not met, or unknown mode
|
||||
query = query.Where(p => p.PinMode == (Shared.Models.PostPinMode)request.Pinned);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(p => p.PinMode == null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Include/exclude replies
|
||||
if (request.IncludeReplies)
|
||||
{
|
||||
// Include both root and reply posts
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exclude reply posts, only root posts
|
||||
query = query.Where(e => e.RepliedPostId == null);
|
||||
}
|
||||
|
||||
// TODO: Time range filtering when proto fields are available
|
||||
// if (request.After != null)
|
||||
// {
|
||||
// var afterTime = request.After.ToDateTimeOffset();
|
||||
// query = query.Where(p => (p.CreatedAt >= afterTime) || (p.PublishedAt >= afterTime));
|
||||
// }
|
||||
|
||||
// if (request.Before != null)
|
||||
// {
|
||||
// var beforeTime = request.Before.ToDateTimeOffset();
|
||||
// query = query.Where(p => (p.CreatedAt <= beforeTime) || (p.PublishedAt <= beforeTime));
|
||||
// }
|
||||
|
||||
// TODO: Query text search when proto field is available
|
||||
// if (!string.IsNullOrWhiteSpace(request.Query))
|
||||
// {
|
||||
// query = query.Where(p =>
|
||||
// EF.Functions.ILike(p.Title, $"%{request.Query}%") ||
|
||||
// EF.Functions.ILike(p.Content, $"%{request.Query}%") ||
|
||||
// EF.Functions.ILike(p.Description, $"%{request.Query}%"));
|
||||
// }
|
||||
|
||||
// Visibility filter (simplified for grpc - no user context)
|
||||
query = query.FilterWithVisibility(null, [], []);
|
||||
|
||||
var totalSize = await query.CountAsync();
|
||||
|
||||
var pageSize = request.PageSize > 0 ? request.PageSize : 20;
|
||||
var pageToken = request.PageToken;
|
||||
var offset = string.IsNullOrEmpty(pageToken) ? 0 : int.Parse(pageToken);
|
||||
|
||||
// Ordering - TODO: Add shuffle when proto field is available
|
||||
var orderedQuery = query.OrderByDescending(e => e.PublishedAt ?? e.CreatedAt);
|
||||
|
||||
var posts = await orderedQuery
|
||||
.Skip(offset)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
posts = await ps.LoadPostInfo(posts, null, true);
|
||||
|
||||
var nextToken = offset + pageSize < totalSize ? (offset + pageSize).ToString() : string.Empty;
|
||||
|
||||
var resp = new ListPostsResponse();
|
||||
resp.Posts.AddRange(posts.Select(p => p.ToProtoValue()));
|
||||
resp.NextPageToken = nextToken;
|
||||
resp.TotalSize = totalSize;
|
||||
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NodaTime;
|
||||
using PublisherMemberRole = DysonNetwork.Shared.Models.PublisherMemberRole;
|
||||
using PublisherType = DysonNetwork.Shared.Models.PublisherType;
|
||||
|
||||
namespace DysonNetwork.Sphere.Publisher;
|
||||
@@ -161,7 +162,7 @@ public class PublisherService(
|
||||
{
|
||||
var publisher = new SnPublisher
|
||||
{
|
||||
Type = Shared.Models.PublisherType.Individual,
|
||||
Type = PublisherType.Individual,
|
||||
Name = name ?? account.Name,
|
||||
Nick = nick ?? account.Nick,
|
||||
Bio = bio ?? account.Profile.Bio,
|
||||
@@ -177,7 +178,7 @@ public class PublisherService(
|
||||
new()
|
||||
{
|
||||
AccountId = Guid.Parse(account.Id),
|
||||
Role = Shared.Models.PublisherMemberRole.Owner,
|
||||
Role = PublisherMemberRole.Owner,
|
||||
JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow)
|
||||
}
|
||||
]
|
||||
@@ -214,7 +215,7 @@ public class PublisherService(
|
||||
}
|
||||
|
||||
public async Task<SnPublisher> CreateOrganizationPublisher(
|
||||
Shared.Models.SnRealm realm,
|
||||
SnRealm realm,
|
||||
Account account,
|
||||
string? name,
|
||||
string? nick,
|
||||
@@ -225,7 +226,7 @@ public class PublisherService(
|
||||
{
|
||||
var publisher = new SnPublisher
|
||||
{
|
||||
Type = Shared.Models.PublisherType.Organizational,
|
||||
Type = PublisherType.Organizational,
|
||||
Name = name ?? realm.Slug,
|
||||
Nick = nick ?? realm.Name,
|
||||
Bio = bio ?? realm.Description,
|
||||
@@ -237,7 +238,7 @@ public class PublisherService(
|
||||
new()
|
||||
{
|
||||
AccountId = Guid.Parse(account.Id),
|
||||
Role = Shared.Models.PublisherMemberRole.Owner,
|
||||
Role = PublisherMemberRole.Owner,
|
||||
JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow)
|
||||
}
|
||||
}
|
||||
@@ -299,10 +300,10 @@ public class PublisherService(
|
||||
|
||||
var postsCount = await db.Posts.Where(e => e.Publisher.Id == publisher.Id).CountAsync();
|
||||
var postsUpvotes = await db.PostReactions
|
||||
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Positive)
|
||||
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == Shared.Models.PostReactionAttitude.Positive)
|
||||
.CountAsync();
|
||||
var postsDownvotes = await db.PostReactions
|
||||
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == PostReactionAttitude.Negative)
|
||||
.Where(r => r.Post.Publisher.Id == publisher.Id && r.Attitude == Shared.Models.PostReactionAttitude.Negative)
|
||||
.CountAsync();
|
||||
|
||||
var stickerPacksId = await db.StickerPacks.Where(e => e.Publisher.Id == publisher.Id).Select(e => e.Id)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.Publisher;
|
||||
|
||||
public class PublisherSubscriptionService(
|
||||
AppDatabase db,
|
||||
PostService ps,
|
||||
Post.PostService ps,
|
||||
IStringLocalizer<NotificationResource> localizer,
|
||||
ICacheService cache,
|
||||
RingService.RingServiceClient pusher,
|
||||
@@ -54,7 +54,7 @@ public class PublisherSubscriptionService(
|
||||
{
|
||||
if (post.RepliedPostId is not null)
|
||||
return 0;
|
||||
if (post.Visibility != PostVisibility.Public)
|
||||
if (post.Visibility != Shared.Models.PostVisibility.Public)
|
||||
return 0;
|
||||
|
||||
// Create notification data
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Http;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
|
||||
namespace DysonNetwork.Sphere.Startup;
|
||||
@@ -20,6 +21,7 @@ public static class ApplicationConfiguration
|
||||
app.MapControllers();
|
||||
|
||||
// Map gRPC services
|
||||
app.MapGrpcService<PostServiceGrpc>();
|
||||
app.MapGrpcService<PublisherServiceGrpc>();
|
||||
|
||||
return app;
|
||||
|
||||
@@ -24,7 +24,7 @@ public class PaymentOrderAwardMeta
|
||||
[JsonPropertyName("account_id")] public Guid AccountId { get; set; }
|
||||
[JsonPropertyName("post_id")] public Guid PostId { get; set; }
|
||||
[JsonPropertyName("amount")] public string Amount { get; set; } = null!;
|
||||
[JsonPropertyName("attitude")] public PostReactionAttitude Attitude { get; set; }
|
||||
[JsonPropertyName("attitude")] public Shared.Models.PostReactionAttitude Attitude { get; set; }
|
||||
[JsonPropertyName("message")] public string? Message { get; set; }
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class BroadcastEventHandler(
|
||||
logger.LogInformation("Handling post award order: {OrderId}", evt.OrderId);
|
||||
|
||||
await using var scope = serviceProvider.CreateAsyncScope();
|
||||
var ps = scope.ServiceProvider.GetRequiredService<PostService>();
|
||||
var ps = scope.ServiceProvider.GetRequiredService<Post.PostService>();
|
||||
|
||||
var amountNum = decimal.Parse(meta.Amount);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user