Skip to content

Commit

Permalink
initial draft of the post page, mostly copy paste
Browse files Browse the repository at this point in the history
  • Loading branch information
NielsPilgaard committed Aug 30, 2024
1 parent 7797602 commit 1bf2dc8
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 0 deletions.
76 changes: 76 additions & 0 deletions src/web/Jordnaer/Features/GroupPosts/PostService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using Jordnaer.Database;
using Jordnaer.Shared;
using Microsoft.EntityFrameworkCore;
using OneOf;
using OneOf.Types;

namespace Jordnaer.Features.GroupPosts;

public interface IGroupPostService
{
Task<OneOf<PostDto, NotFound>> GetPostAsync(Guid postId,
CancellationToken cancellationToken = default);

Task<OneOf<Success, Error<string>>> CreatePostAsync(GroupPost post,
CancellationToken cancellationToken = default);

Task<OneOf<Success, Error<string>>> DeletePostAsync(Guid postId,
CancellationToken cancellationToken = default);
}
// TODO: This is just a copy of PostService, make it Group specific
public class GroupPostService(IDbContextFactory<JordnaerDbContext> contextFactory) : IGroupPostService
{
public async Task<OneOf<PostDto, NotFound>> GetPostAsync(Guid postId,
CancellationToken cancellationToken = default)
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);

var post = await context.Posts
.Where(x => x.Id == postId)
.Select(x => x.ToPostDto())
.FirstOrDefaultAsync(cancellationToken);

return post is null
? new NotFound()
: post;
}

public async Task<OneOf<Success, Error<string>>> CreatePostAsync(GroupPost post,
CancellationToken cancellationToken = default)
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);

if (await context.Posts
.AsNoTracking()
.AnyAsync(x => x.Id == post.Id &&
x.UserProfileId == post.UserProfileId,
cancellationToken))
{
return new Error<string>("Opslaget eksisterer allerede.");
}

context.GroupPosts.Add(post);

await context.SaveChangesAsync(cancellationToken);

return new Success();
}

public async Task<OneOf<Success, Error<string>>> DeletePostAsync(Guid postId,
CancellationToken cancellationToken = default)
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);

var post = await context.Posts.FindAsync([postId], cancellationToken);
if (post is null)
{
return new Error<string>("Opslaget blev ikke fundet.");
}

await context.Posts
.Where(x => x.Id == postId)
.ExecuteDeleteAsync(cancellationToken);

return new Success();
}
}
41 changes: 41 additions & 0 deletions src/web/Jordnaer/Features/Posts/CreatePostComponent.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@inject CurrentUser CurrentUser
@inject IPostService PostService
@inject IPostSearchService PostSearchService
@inject NavigationManager Navigation
@inject IJSRuntime JsRuntime
@inject ISnackbar Snackbar

<MudPaper Class="pa-4">
<MudText Typo="Typo.h4">Del et opslag</MudText>
<MudTextField @bind-Value="newPostContent" Placeholder="Hvad har du på tankerne?" Lines="4" FullWidth />
<MudButton Variant="Variant.Filled" Color="Color.Success" OnClick="CreatePost">Send</MudButton>
</MudPaper>
@code {
private string? newPostContent;

private async Task CreatePost()
{
if (!string.IsNullOrWhiteSpace(newPostContent))
{
var post = new Post
{
Id = Guid.NewGuid(),
Text = newPostContent,
CreatedUtc = DateTime.UtcNow,
UserProfileId = CurrentUser.Id,

Check warning on line 25 in src/web/Jordnaer/Features/Posts/CreatePostComponent.razor

View workflow job for this annotation

GitHub Actions / test

Possible null reference assignment.
// Add other necessary properties here
};

var result = await PostService.CreatePostAsync(post);

result.Switch(
success =>
{
Snackbar.Add("Opslaget blev oprettet.", Severity.Success);
newPostContent = string.Empty;
},
error => Snackbar.Add(error.Value, Severity.Error)
);
}
}
}
70 changes: 70 additions & 0 deletions src/web/Jordnaer/Features/Posts/PostSearchForm.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
@inject NavigationManager Navigation
@inject IJSRuntime JsRuntime

<MudContainer MaxWidth="MaxWidth.Small">

<MudPaper Elevation="3" Class="pa-10 mt-5">
<p class="font-open-sans-light" style="color: @JordnaerPalette.RedHeader; font-size: 20px;">
Find Opslag
</p>

<MudDivider Class="mb-5 mt-1" />

<EditForm OnValidSubmit="@OnValidSubmit" Model="Filter">
<DataAnnotationsValidator />

<MudGrid Justify="Justify.SpaceAround" Spacing="6">

<MudItem xs="12">
<MudTextField @bind-Value="Filter.Contents" Placeholder="Søg efter indlæg" Label="Søg på indlæg" Clearable />
</MudItem>

<MudItem xs="12" sm="11" md="10" lg="9" xl="8" Class="mt-8">
<MudButtonGroup OverrideStyles="false" Style="width: 100%;">
<MudButton FullWidth
Variant="Variant.Filled"
Color="Color.Success"
ButtonType="ButtonType.Submit">
<MudIcon Icon="@Icons.Material.Filled.Search" />
</MudButton>
<MudButton OnClick="ClearFilter"
Color="Color.Transparent"
Variant="Variant.Filled"
ButtonType="ButtonType.Reset">
<MudIcon Icon="@Icons.Material.Filled.Clear" />
</MudButton>
</MudButtonGroup>
</MudItem>
</MudGrid>
</EditForm>
</MudPaper>
</MudContainer>

@code
{
[Parameter, EditorRequired]
public required PostSearchFilter Filter { get; set; }

[Parameter, EditorRequired]
public EventCallback<PostSearchFilter> FilterChanged { get; set; }

[Parameter, EditorRequired]
public required EventCallback OnValidSubmit { get; set; }

private static readonly PostSearchFilter DefaultFilter = new();

private bool _recentlyClearedForm = false;

Check warning on line 56 in src/web/Jordnaer/Features/Posts/PostSearchForm.razor

View workflow job for this annotation

GitHub Actions / test

The field 'PostSearchForm._recentlyClearedForm' is assigned but its value is never used

private async Task ClearFilter()
{
Filter = new PostSearchFilter();
await FilterChanged.InvokeAsync(Filter);

var uriWithQuery = new Uri(Navigation.Uri);
var uriWithoutQuery = uriWithQuery.GetLeftPart(UriPartial.Path);

_recentlyClearedForm = true;

await JsRuntime.NavigateTo(uriWithoutQuery);
}
}
36 changes: 36 additions & 0 deletions src/web/Jordnaer/Features/Posts/PostSearchResultComponent.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@inject IJSRuntime JsRuntime
@inject NavigationManager Navigation


<MudGrid Justify="Justify.Center">
@foreach (var post in SearchResult.Posts)
{
<MudItem xs="12">
<MudCard Class="pa-3 my-3" Elevation="3">
<MudCardContent>
<MudText Typo="Typo.body1">@post.Text</MudText>
<MudText Typo="Typo.caption">@post.CreatedUtc.ToLocalTime().ToString("g")</MudText>
</MudCardContent>
</MudCard>
</MudItem>
}
</MudGrid>
<MudPagination Class="mt-5 d-flex justify-center"
BoundaryCount="0"
MiddleCount="3"
UserAttributes="@(new Dictionary<string, object> {["title"] = $"Viser {Filter.PageSize} ud af {SearchResult.TotalCount} resultater."})"
SelectedChanged="@SelectedPageChanged"
Variant="Variant.Filled"
Count="@(SearchResult.TotalCount / Filter.PageSize)"
Selected="Filter.PageNumber" />

@code {
[Parameter, EditorRequired]
public required PostSearchFilter Filter { get; set; }

[Parameter, EditorRequired]
public required PostSearchResult SearchResult { get; set; }

[Parameter, EditorRequired]
public EventCallback<int> SelectedPageChanged { get; set; }
}
75 changes: 75 additions & 0 deletions src/web/Jordnaer/Pages/Posts/Posts.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@page "/posts"

@inject IPostSearchService PostSearchService
@inject ISnackbar Snackbar

@attribute [Sitemap]

<MetadataComponent Title="Opslag"
Description="Læs og skriv opslag om stort og småt her" />

<MudLoading @bind-Loading="_isSearching" Darken Overlap>

<CreatePostComponent />

<MudDivider Class="my-4" />

<PostSearchForm OnValidSubmit="@Search" @bind-Filter="_filter" />

Check warning on line 17 in src/web/Jordnaer/Pages/Posts/Posts.razor

View workflow job for this annotation

GitHub Actions / test

Component 'PostSearchForm' expects a value for the parameter 'FilterChanged', but a value may not have been provided.

@if (!_hasSearched)
{
return;
}

@if (_searchResult.TotalCount == 0)
{
<MudPaper Elevation="3" Class="pa-10 mt-5">
<MudAlert Severity="Severity.Info" ContentAlignment="HorizontalAlignment.Center" Class="align-center">
<MudText Align="Align.Center" Typo="Typo.h6">Ingen opslag fundet.</MudText>
</MudAlert>
</MudPaper>
return;
}

<SponsorAd Class="mt-5"
ImageAlt="Reklame for Moon Creative"
MobileImagePath="images/ads/mooncreative_mobile.png"
DesktopImagePath="images/ads/mooncreative_mobile.png"
Link="https://www.mooncreative.dk/" />

<PostSearchResultComponent Filter="_filter" SearchResult="_searchResult" SelectedPageChanged="@OnSelectedPageChanged" />

</MudLoading>

@code {
private PostSearchFilter _filter = new();
private PostSearchResult _searchResult = new();

private bool _isSearching = false;
private bool _hasSearched = false;

private async Task Search()
{
_isSearching = true;

var result = await PostSearchService.GetPostsAsync(_filter);

result.Switch(
success =>
{
_searchResult = success;
Snackbar.Add($"{_searchResult.TotalCount} opslag fundet.", Severity.Success);
},
error => Snackbar.Add(error.Value, Severity.Error)
);

_hasSearched = true;
_isSearching = false;
}

private async Task OnSelectedPageChanged(int selectedPage)
{
_filter.PageNumber = selectedPage;
await Search();
}
}
3 changes: 3 additions & 0 deletions src/web/Jordnaer/_Imports.razor
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
@using Jordnaer.Features.Images
@using Jordnaer.Features.Membership
@using Jordnaer.Features.Metrics
@using Jordnaer.Features.Posts
@using Jordnaer.Features.PostSearch
@using Jordnaer.Features.GroupPosts
@using Jordnaer.Features.Profile
@using Jordnaer.Features.Search
@using Jordnaer.Features.SEO
Expand Down

0 comments on commit 1bf2dc8

Please sign in to comment.