Optimize GameServer /announce and add website announcements (#810)

* Improve game server announce by using StringBuilder

* Implement web announcements (condensed commit)

* Implement discord webhook support

* Display a separate message if there are no announcements

* Fix announcement string unit tests

* Fix header admin button unit test

* Clarify announcement id variable name

* Increase webhook truncation limit to 250 chars

* Convert announce text to string when returning 200

* Fix announcement unit tests ... again

* Make announcement text input a textarea rather than a simple input

* Fix styling discrepancy

* Clarify submission button

* Improve announcement webhook & set default textarea row amount
This commit is contained in:
koko 2023-06-22 23:49:22 -04:00 committed by GitHub
commit 689ebd3791
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 256 additions and 20 deletions

View file

@ -0,0 +1,67 @@
@page "/announce"
@using LBPUnion.ProjectLighthouse.Localization.StringLists
@using LBPUnion.ProjectLighthouse.Types.Entities.Website
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.AnnouncePage
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = "Layouts/BaseLayout";
Model.Title = Model.Translate(GeneralStrings.Announcements);
}
@if (Model.User != null && Model.User.IsAdmin)
{
<div class="ui red segment">
<h3>Post New Announcement</h3>
@if (!string.IsNullOrWhiteSpace(Model.Error))
{
@await Html.PartialAsync("Partials/ErrorModalPartial", (Model.Translate(GeneralStrings.Error), Model.Error), ViewData)
}
<form id="form" method="POST" class="ui form center aligned" action="/announce">
@Html.AntiForgeryToken()
<div class="field">
<label style="text-align: left" for="title">Announcement Title</label>
<input type="text" name="title" id="title">
</div>
<div class="field">
<label style="text-align: left" for="content">Announcement Content</label>
<textarea name="content" id="content" spellcheck="false" rows="3"></textarea>
</div>
<button class="ui button green" type="submit" tabindex="0">Post Announcement</button>
</form>
</div>
}
@if (Model.Announcements.Any())
{
@foreach (WebsiteAnnouncementEntity announcement in Model.Announcements)
{
<div class="ui blue segment" style="position: relative;">
<h3>@announcement.Title</h3>
<p style="white-space: initial;">
@announcement.Content
</p>
@if (Model.User != null && Model.User.IsAdmin)
{
<form method="post">
@Html.AntiForgeryToken()
<button
asp-page-handler="delete"
asp-route-id="@announcement.AnnouncementId"
onclick="return confirm('Are you sure you want to delete this announcement?')"
class="ui red icon button"
style="position: absolute; right: 0.5em; top: 0.5em">
<i class="trash icon"></i>
</button>
</form>
}
</div>
}
}
else
{
<div class="ui blue segment" style="position: relative;">
<p>There are no announcements to display.</p>
</div>
}

View file

@ -0,0 +1,82 @@
#nullable enable
using LBPUnion.ProjectLighthouse.Configuration;
using LBPUnion.ProjectLighthouse.Database;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
using LBPUnion.ProjectLighthouse.Types.Entities.Website;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages;
public class AnnouncePage : BaseLayout
{
public AnnouncePage(DatabaseContext database) : base(database)
{ }
public List<WebsiteAnnouncementEntity> Announcements { get; set; } = new();
public string Error { get; set; } = "";
public async Task<IActionResult> OnGet()
{
this.Announcements = await this.Database.WebsiteAnnouncements
.OrderByDescending(a => a.AnnouncementId)
.ToListAsync();
return this.Page();
}
public async Task<IActionResult> OnPost([FromForm] string title, [FromForm] string content)
{
UserEntity? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.BadRequest();
if (!user.IsAdmin) return this.BadRequest();
if (string.IsNullOrWhiteSpace(title) || string.IsNullOrWhiteSpace(content))
{
this.Error = "Invalid form data, please ensure all fields are filled out.";
return this.Page();
}
WebsiteAnnouncementEntity announcement = new()
{
Title = title,
Content = content,
};
await this.Database.WebsiteAnnouncements.AddAsync(announcement);
await this.Database.SaveChangesAsync();
if (DiscordConfiguration.Instance.DiscordIntegrationEnabled)
{
string truncatedAnnouncement = content.Length > 250
? content[..250] + $"... [read more]({ServerConfiguration.Instance.ExternalUrl}/announce)"
: content;
await WebhookHelper.SendWebhook($":mega: {title}",
truncatedAnnouncement);
}
return this.RedirectToPage();
}
public async Task<IActionResult> OnPostDelete(int id)
{
UserEntity? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.BadRequest();
if (!user.IsAdmin) return this.BadRequest();
WebsiteAnnouncementEntity? announcement = await this.Database.WebsiteAnnouncements
.Where(a => a.AnnouncementId == id)
.FirstOrDefaultAsync();
if (announcement == null) return this.BadRequest();
this.Database.WebsiteAnnouncements.Remove(announcement);
await this.Database.SaveChangesAsync();
return this.RedirectToPage();
}
}

View file

@ -33,6 +33,8 @@ public class BaseLayout : PageModel
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderUsers, "/users/0", "user friends"));
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderPhotos, "/photos/0", "camera"));
this.NavigationItems.Add(new PageNavigationItem(BaseLayoutStrings.HeaderSlots, "/slots/0", "globe americas"));
this.NavigationItemsRight.Add(new PageNavigationItem(GeneralStrings.Announcements, "/announce", "bullhorn"));
}
public new UserEntity? User {