-
-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/group invite display #485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
7332ac6
improve group invite flow and display
NielsPilgaard 4e20b42
add signalr notifs
NielsPilgaard df53474
fix review comments
NielsPilgaard 3df3f51
Update GroupServiceTests.cs
NielsPilgaard 399f502
fix facebook+cloud icon colors
NielsPilgaard 8579529
fix cookie banner colors
NielsPilgaard db07186
allow leaving groups
NielsPilgaard ace35f0
revamp signalr approach to be sturdier
NielsPilgaard 50f27c1
finish group membership signal r impl
NielsPilgaard b34738a
resolve signalr / db issue
NielsPilgaard 4b80e25
Update GroupService.cs
NielsPilgaard 3924be2
Update CookieBanner.razor
NielsPilgaard 63373ec
Update GroupServiceTests.cs
NielsPilgaard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/shared/Jordnaer.Shared/Groups/Events/GroupPostCreated.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| namespace Jordnaer.Shared; | ||
|
|
||
| public class GroupPostCreated | ||
| { | ||
| public required Guid PostId { get; init; } | ||
| public required Guid GroupId { get; init; } | ||
| public required string GroupName { get; init; } | ||
| public required string AuthorId { get; init; } | ||
| public required string AuthorDisplayName { get; init; } | ||
| public required string PostText { get; init; } | ||
| public DateTimeOffset CreatedUtc { get; init; } = DateTimeOffset.UtcNow; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| namespace Jordnaer.Shared; | ||
|
|
||
| public class GroupMemberSlim | ||
| { | ||
| public required string Id { get; init; } | ||
|
|
||
| public required string DisplayName { get; init; } | ||
|
|
||
| public required string ProfilePictureUrl { get; init; } | ||
|
|
||
| public required string? UserName { get; init; } | ||
|
|
||
| public required OwnershipLevel OwnershipLevel { get; init; } | ||
|
|
||
| public required PermissionLevel PermissionLevel { get; init; } | ||
|
|
||
| public override string ToString() => DisplayName; | ||
| } |
7 changes: 7 additions & 0 deletions
7
src/shared/Jordnaer.Shared/Groups/GroupMembershipStatusChanged.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| namespace Jordnaer.Shared; | ||
|
|
||
| public class GroupMembershipStatusChanged | ||
| { | ||
| public required Guid GroupId { get; init; } | ||
| public int PendingCountChange { get; init; } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| using Jordnaer.Database; | ||
| using Jordnaer.Features.Email; | ||
| using Jordnaer.Features.Metrics; | ||
| using Jordnaer.Shared; | ||
| using MassTransit; | ||
| using Microsoft.AspNetCore.Components; | ||
| using Microsoft.EntityFrameworkCore; | ||
| using System.Net; | ||
| using System.Text.RegularExpressions; | ||
|
|
||
| namespace Jordnaer.Consumers; | ||
|
|
||
| public partial class GroupPostCreatedConsumer( | ||
| IDbContextFactory<JordnaerDbContext> contextFactory, | ||
| ILogger<GroupPostCreatedConsumer> logger, | ||
| IPublishEndpoint publishEndpoint, | ||
| NavigationManager navigationManager) : IConsumer<GroupPostCreated> | ||
| { | ||
| public async Task Consume(ConsumeContext<GroupPostCreated> consumeContext) | ||
| { | ||
| JordnaerMetrics.GroupPostCreatedConsumerReceivedCounter.Add(1); | ||
|
|
||
| logger.LogInformation("Consuming GroupPostCreated message. PostId: {PostId}, GroupId: {GroupId}", | ||
| consumeContext.Message.PostId, consumeContext.Message.GroupId); | ||
|
|
||
| var message = consumeContext.Message; | ||
|
|
||
| try | ||
| { | ||
| await using var context = await contextFactory.CreateDbContextAsync(consumeContext.CancellationToken); | ||
|
|
||
| // Get all active members excluding the post author | ||
| var activeMembers = context.GroupMemberships | ||
| .AsNoTracking() | ||
| .Where(x => x.GroupId == message.GroupId && | ||
| x.MembershipStatus == MembershipStatus.Active && | ||
| x.UserProfileId != message.AuthorId) | ||
| .Select(x => x.UserProfileId); | ||
|
|
||
| // Get their email addresses | ||
| var emails = await context.Users | ||
| .AsNoTracking() | ||
| .Where(user => activeMembers.Any(userId => userId == user.Id) && | ||
| !string.IsNullOrEmpty(user.Email)) | ||
| .Select(user => new EmailRecipient | ||
| { | ||
| Email = user.Email!, | ||
| DisplayName = user.UserName | ||
| }) | ||
| .ToListAsync(consumeContext.CancellationToken); | ||
|
|
||
| if (emails.Count == 0) | ||
| { | ||
| logger.LogInformation("No members to notify for new post in group {GroupName}", message.GroupName); | ||
| JordnaerMetrics.GroupPostCreatedConsumerSucceededCounter.Add(1); | ||
| return; | ||
| } | ||
|
|
||
| logger.LogInformation("Sending new post notification to {Count} members in group {GroupName}", | ||
| emails.Count, message.GroupName); | ||
|
|
||
| var groupUrl = $"{navigationManager.BaseUri}groups/{message.GroupName}"; | ||
| var postPreview = GetPostPreview(message.PostText); | ||
|
|
||
| var email = new SendEmail | ||
| { | ||
| Subject = $"Nyt opslag i {message.GroupName}", | ||
| HtmlContent = CreateNewPostEmailContent(message.AuthorDisplayName, postPreview, groupUrl), | ||
| Bcc = emails | ||
| }; | ||
|
|
||
| await publishEndpoint.Publish(email, consumeContext.CancellationToken); | ||
|
|
||
| JordnaerMetrics.GroupPostCreatedConsumerSucceededCounter.Add(1); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| JordnaerMetrics.GroupPostCreatedConsumerFailedCounter.Add(1); | ||
|
|
||
| logger.LogError(ex, "Failed to send new post notifications for post {PostId} in group {GroupId}", | ||
| message.PostId, message.GroupId); | ||
| // Don't rethrow - we don't want email failures to break post creation | ||
| } | ||
| } | ||
|
|
||
| private static string GetPostPreview(string text) | ||
| { | ||
| // Strip HTML tags and limit to 200 characters | ||
| var plainText = HtmlTagsRegex().Replace(text, string.Empty); | ||
| return plainText.Length <= 200 | ||
| ? plainText | ||
| : plainText.Substring(0, 200) + "..."; | ||
| } | ||
|
|
||
| private static string CreateNewPostEmailContent(string authorName, string postPreview, string groupUrl) | ||
| { | ||
| // HTML-encode to prevent XSS attacks | ||
| var encodedAuthorName = WebUtility.HtmlEncode(authorName); | ||
| var encodedPostPreview = WebUtility.HtmlEncode(postPreview); | ||
|
|
||
| // Convert newlines to <br/> tags for proper display after encoding | ||
| encodedPostPreview = encodedPostPreview.Replace("\r\n", "<br/>").Replace("\n", "<br/>"); | ||
|
|
||
| var encodedGroupUrl = WebUtility.HtmlEncode(groupUrl); | ||
|
|
||
| return $""" | ||
| <h4>Nyt opslag i din gruppe</h4> | ||
|
|
||
| <p><b>{encodedAuthorName}</b> har oprettet et nyt opslag:</p> | ||
|
|
||
| <blockquote style="border-left: 3px solid #ccc; padding-left: 10px; color: #666;"> | ||
| {encodedPostPreview} | ||
| </blockquote> | ||
|
|
||
| <p><a href="{encodedGroupUrl}">Klik her for at se opslaget</a></p> | ||
|
|
||
| {EmailConstants.Signature} | ||
| """; | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| [GeneratedRegex("<.*?>")] | ||
| private static partial Regex HtmlTagsRegex(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.