Skip to content

Commit

Permalink
Merge pull request #408 from NielsPilgaard/feature/group-page
Browse files Browse the repository at this point in the history
Feature/group page
  • Loading branch information
NielsPilgaard authored Aug 9, 2024
2 parents 0adb113 + ad98649 commit 428ce5b
Show file tree
Hide file tree
Showing 21 changed files with 425 additions and 294 deletions.
46 changes: 23 additions & 23 deletions src/shared/Jordnaer.Shared/Database/Group.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,36 @@ namespace Jordnaer.Shared;
[Index(nameof(Name), IsUnique = true)]
public class Group
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }

[Url]
public string? ProfilePictureUrl { get; set; }
[Url]
public string? ProfilePictureUrl { get; set; }

[MaxLength(500, ErrorMessage = "Adresse må højest være 500 karakterer langt.")]
public string? Address { get; set; }
[MaxLength(500, ErrorMessage = "Adresse må højest være 500 karakterer langt.")]
public string? Address { get; set; }

[DanishZipCode(ErrorMessage = "Post nummer skal være mellem 1000 og 9999")]
public int? ZipCode { get; set; }
[DanishZipCode(ErrorMessage = "Post nummer skal være mellem 1000 og 9999")]
public int? ZipCode { get; set; }

[MaxLength(100, ErrorMessage = "By navn må højest være 100 karakterer langt.")]
public string? City { get; set; }
[MaxLength(100, ErrorMessage = "By navn må højest være 100 karakterer langt.")]
public string? City { get; set; }

[MinLength(2, ErrorMessage = "Gruppe navn skal være mindst 2 karakterer langt.")]
[MaxLength(128, ErrorMessage = "Gruppens navn må højest være 128 karakterer lang.")]
public required string Name { get; set; }
[MinLength(2, ErrorMessage = "Gruppe navn skal være mindst 2 karakterer langt.")]
[MaxLength(128, ErrorMessage = "Gruppens navn må højest være 128 karakterer lang.")]
public required string Name { get; set; }

[Required(ErrorMessage = "Påkrævet.")]
[MaxLength(200, ErrorMessage = "Gruppens beskrivelse må højest være 200 karakterer lang.")]
public required string ShortDescription { get; set; }
[Required(ErrorMessage = "Påkrævet.")]
[MaxLength(200, ErrorMessage = "Gruppens beskrivelse må højest være 200 karakterer lang.")]
public required string ShortDescription { get; set; }

[MaxLength(4000, ErrorMessage = "Gruppens beskrivelse må højest være 4000 karakterer lang.")]
public string? Description { get; set; }
[MaxLength(4000, ErrorMessage = "Gruppens beskrivelse må højest være 4000 karakterer lang.")]
public string? Description { get; set; }

public List<UserProfile> Members { get; set; } = new();
public List<GroupMembership> Memberships { get; set; } = new();
public List<Category> Categories { get; set; } = new();
public List<UserProfile> Members { get; set; } = new();
public List<GroupMembership> Memberships { get; set; } = new();
public List<Category> Categories { get; set; } = new();

public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
}
17 changes: 10 additions & 7 deletions src/web/Jordnaer/Consumers/StartChatConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
using Jordnaer.Shared;
using MassTransit;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;

namespace Jordnaer.Consumers;

public class StartChatConsumer : IConsumer<StartChat>
{
private readonly JordnaerDbContext _context;
private readonly IDbContextFactory<JordnaerDbContext> _contextFactory;
private readonly ILogger<StartChatConsumer> _logger;
private readonly IHubContext<ChatHub, IChatHub> _chatHub;
private readonly ChatNotificationService _chatNotificationService;

public StartChatConsumer(JordnaerDbContext context,
public StartChatConsumer(IDbContextFactory<JordnaerDbContext> contextFactory,
ILogger<StartChatConsumer> logger,
IHubContext<ChatHub, IChatHub> chatHub,
ChatNotificationService chatNotificationService)
{
_context = context;
_contextFactory = contextFactory;
_logger = logger;
_chatHub = chatHub;
_chatNotificationService = chatNotificationService;
Expand All @@ -30,7 +31,9 @@ public async Task Consume(ConsumeContext<StartChat> consumeContext)

var chat = consumeContext.Message;

_context.Chats.Add(new Chat
await using var context = await _contextFactory.CreateDbContextAsync(consumeContext.CancellationToken);

context.Chats.Add(new Chat
{
LastMessageSentUtc = chat.LastMessageSentUtc,
Id = chat.Id,
Expand All @@ -41,7 +44,7 @@ public async Task Consume(ConsumeContext<StartChat> consumeContext)

foreach (var recipient in chat.Recipients)
{
_context.UserChats.Add(new UserChat
context.UserChats.Add(new UserChat
{
ChatId = chat.Id,
UserProfileId = recipient.Id
Expand All @@ -52,7 +55,7 @@ public async Task Consume(ConsumeContext<StartChat> consumeContext)
{
foreach (var recipient in chat.Recipients.Where(recipient => recipient.Id != chat.InitiatorId))
{
_context.UnreadMessages.Add(new UnreadMessage
context.UnreadMessages.Add(new UnreadMessage
{
RecipientId = recipient.Id,
ChatId = chat.Id,
Expand All @@ -63,7 +66,7 @@ public async Task Consume(ConsumeContext<StartChat> consumeContext)

try
{
await _context.SaveChangesAsync(consumeContext.CancellationToken);
await context.SaveChangesAsync(consumeContext.CancellationToken);
await _chatHub.Clients.Users(chat.Recipients.Select(recipient => recipient.Id)).StartChat(chat);
}
catch (Exception exception)
Expand Down
31 changes: 18 additions & 13 deletions src/web/Jordnaer/Features/Ad/SponsorAd.razor
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
@using MudBlazor.Services

<MudContainer MaxWidth="MaxWidth.Small">
<div class="image-ad-wrapper">
<div class="image-with-label">
<MudHidden Breakpoint="Breakpoint.SmAndDown" Invert>
<MudImage Src="@MobileImagePath" Fluid="Fluid" Alt="@ImageAlt" Class="@Class" loading="lazy" />
</MudHidden>
<MudHidden Breakpoint="Breakpoint.SmAndDown">
<MudImage Src="@DesktopImagePath" Fluid="Fluid" Alt="@ImageAlt" Class="@Class" loading="lazy" />
</MudHidden>
<div class="image-ad-label">Sponsor reklame</div>
<MudLink Href="@Link" Target="_blank" Underline="Underline.None">
<div class="image-ad-wrapper">
<div class="image-with-label">
<MudHidden Breakpoint="Breakpoint.SmAndDown" Invert>
<MudImage Src="@MobileImagePath" Fluid="Fluid" Alt="@ImageAlt" Class="@Class" loading="lazy" />
</MudHidden>
<MudHidden Breakpoint="Breakpoint.SmAndDown">
<MudImage Src="@DesktopImagePath" Fluid="Fluid" Alt="@ImageAlt" Class="@Class" loading="lazy" />
</MudHidden>
<div class="image-ad-label">Sponsor reklame</div>
</div>
</div>
</div>
</MudLink>
</MudContainer>

@code
{
[Parameter]
public string? Class { get; set; }

[Parameter]
[Parameter, EditorRequired]
public required string Link { get; set; }

[Parameter, EditorRequired]
public required string MobileImagePath { get; set; }

[Parameter]
[Parameter, EditorRequired]
public required string DesktopImagePath { get; set; }

[Parameter]
[Parameter, EditorRequired]
public required string ImageAlt { get; set; }

[Parameter]
Expand Down
23 changes: 9 additions & 14 deletions src/web/Jordnaer/Features/Category/CategorySelector.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@inject ICategoryCache CategoryCache

<MudSelectExtended ItemCollection="_categories"
<MudSelectExtended ItemCollection="_allCategories"
T="string"
Label="Kategorier"
Placeholder="Vælg"
Expand All @@ -12,31 +12,26 @@
</MudSelectExtended>
@code
{
private List<string> _categories = [];
private List<string> _allCategories = [];

[SupplyParameterFromQuery(Name = "categories")]
public string[]? CategoriesFromQueryString { get; set; }

[Parameter]
public string[]? Categories { get; set; }

[Parameter]
public EventCallback<string[]?> CategoriesChanged { get; set; }

protected override async Task OnInitializedAsync()
{
var cachedCategories = await CategoryCache.GetOrCreateCategoriesAsync();
_categories = cachedCategories.Select(x => x.Name).ToList();

if (CategoriesFromQueryString is {Length: > 0})
{
await SelectedCategoriesChanged(CategoriesFromQueryString);
}
_allCategories = cachedCategories.Select(x => x.Name).ToList();
}

private async Task SelectedCategoriesChanged(IEnumerable<string?>? categories)
{
Categories = categories?.ToArray()!;
await CategoriesChanged.InvokeAsync(Categories);
Categories = (categories is null
? []
: categories.Where(x => x != null).ToArray())!;

await CategoriesChanged.InvokeAsync(Categories);
}
}
16 changes: 8 additions & 8 deletions src/web/Jordnaer/Features/Email/EmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Jordnaer.Features.Email;
public interface IEmailService
{
Task SendEmailFromContactForm(ContactForm contactForm, CancellationToken cancellationToken = default);
Task SendMembershipRequestEmails(Guid groupId, CancellationToken cancellationToken = default);
Task SendMembershipRequestEmails(string groupName, CancellationToken cancellationToken = default);
}

public sealed class EmailService(IPublishEndpoint publishEndpoint,
Expand Down Expand Up @@ -40,16 +40,16 @@ public async Task SendEmailFromContactForm(
}

public async Task SendMembershipRequestEmails(
Guid groupId,
string groupName,
CancellationToken cancellationToken = default)
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var membersThatCanApproveRequest = context
.GroupMemberships
.AsNoTracking()
.Where(x =>
x.GroupId == groupId &&
x.PermissionLevel.HasFlag(PermissionLevel.Admin))
x.Group.Name == groupName &&
x.PermissionLevel == PermissionLevel.Admin)
.Select(x => x.UserProfileId);

var emails = await context.Users
Expand All @@ -60,15 +60,15 @@ public async Task SendMembershipRequestEmails(

if (emails.Count is 0)
{
logger.LogError("No users found who can approve membership request for group {GroupId}.", groupId);
logger.LogError("No users found who can approve membership request for group {GroupName}.", groupName);
return;
}

logger.LogInformation("Found {MembersThatCanApproveRequestCount} users who can approve " +
"membership request for group {GroupId}. " +
"Sending an email to them.", emails.Count, groupId);
"membership request for group {GroupName}. " +
"Sending an email to them.", emails.Count, groupName);

var groupMembershipUrl = $"{navigationManager.BaseUri}/groups/{groupId}/memberships";
var groupMembershipUrl = $"{navigationManager.BaseUri}groups/{groupName}/memberships";

List<EmailAddress> toEmail = [emails.First()];
var email = new SendEmail
Expand Down
31 changes: 18 additions & 13 deletions src/web/Jordnaer/Features/GroupSearch/GroupCard.razor
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<MudCard Elevation="3">
<MudCardContent>
<MudImage ObjectPosition="ObjectPosition.Left"
Style="border-radius: 10%"
Src="@Group.ProfilePictureUrl"
Class="mb-3"
loading="lazy" />

@if (Group.ProfilePictureUrl is not null)
{
<MudImage ObjectPosition="ObjectPosition.Left"
Style="border-radius: 10%"
Src="@Group.ProfilePictureUrl"
Class="mb-3"
loading="lazy" />
}
<MudTextField Label="Navn"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.AlternateEmail"
Expand All @@ -20,24 +22,27 @@
T="string"
Text="@Group.DisplayLocation()" />

<MudTextField Label="Kort beskrivelse"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Description"
ReadOnly
T="string"
Text="@Group.ShortDescription" />
@if (!string.IsNullOrEmpty(Group.ShortDescription))
{
<MudTextField Label="Kort beskrivelse"
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Description"
ReadOnly
T="string"
Text="@Group.ShortDescription" />
}

@if (Group.Categories.Length > 0)
{
<MudChipField ChipColor="Color.Tertiary"
Class="hide-child-inputs"
Label="Kategorier"
Values="Group.Categories.ToList()"
T="string"
ReadOnly
ChipVariant="Variant.Filled"
Closeable="false"
Clearable="false"
Class="ms-0"
WrapChips />
}
else
Expand Down
12 changes: 7 additions & 5 deletions src/web/Jordnaer/Features/GroupSearch/GroupSearchForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
<MudGrid Justify="Justify.SpaceAround" Spacing="6">

<MudItem xs="8">
<ZipCodeAutoComplete
For="() => Filter.Location"
Location="@Filter.Location"
LocationChanged="LocationChanged"
RecentlyClearedForm="_recentlyClearedForm" />
<ZipCodeAutoComplete For="() => Filter.Location"
Location="@Filter.Location"
LocationChanged="LocationChanged"
DisableSmartCompletion="_disableSmartCompletionForZipCode" />
</MudItem>
<MudItem xs="4">
<MudNumericField For="() => Filter.WithinRadiusKilometers"
Expand Down Expand Up @@ -69,7 +68,10 @@
[Parameter]
public required EventCallback OnValidSubmit { get; set; }

private static readonly GroupSearchFilter DefaultFilter = new();

private bool _recentlyClearedForm = false;
private bool _disableSmartCompletionForZipCode => _recentlyClearedForm || Filter != DefaultFilter;

private async Task ClearFilter()
{
Expand Down
Loading

0 comments on commit 428ce5b

Please sign in to comment.