Skip to content

Commit 045decc

Browse files
authored
Migrate frontend to Blazor (#992)
* Initial blazor implementation * Remove template files * Add developmentSettings to gitignore and allow wwwroot in blazor project * Add MudBlazor to _Host.cshtml * Initial landing page implementation * Migrate Modix startup to Modix.Web Add Authorization policies based on AuthorizationClaim enum Add crude implementation of DiscordUserService * Implement navbar with separate views depending on authorization state * Initial implementation of UserLookup page * Initial implementation of Commands page * Initial implementation of Stats page * Initial implementation of Tags page * Initial implementation of Promotions page * Run AuthorizationService.OnAuthenticatedAsync in MainLayout instead ClaimsTransformation uses a different service scope from the blazor circuit - which leads to AuthorizationService properties not being set properly for the authenticated user Also remove redundant authorization setup in startup * Initial implementation of Logs/Infractions page * Use dropdown for 'Type' in infractions grid * Remove unnecessary comments * Move navbar from MainLayout to separate component * Change page titles * Get SocketGuild from SocketGuildUser instead * Use OnAfterRenderAsync instead to avoid duplicate DB calls * Implement guild selection Remove ClaimsTransformationService Overall improvements to navbar Move logic for adding claims to App.razor Read selected guild from cookie and pass it down the chain * Remove <AuthorizeView> etc in favor of Authorize attribute * Use OnAfterRenderAsync to avoid unnecessary execution of db calls etc * Use [Authorize] attribute to avoid rendering of the page whatsoever * Fix icon colors of navbar in the logged out state * Use middleware to assign claims rather than have it in App.razor The call to AuthorizationService.OnAuthenticatedAsync is still needed in App.razor to be able to set the values in the correct scope. * Move logout button to dropdown menu Small tidy up * Rename LocalStorageService -> CookieService * Show current user when first visiting UserLookup Slight improvements with nullability Remove commented code * Move models to Models folder * Propagate CurrentUserId down to components via SessionState to avoid reading from user claims constantly * Fix issue with filters not resetting properly * Initially select nothing in the dropdown for InfractionType (nothing was actually initially selected, it just appeared to be) * Force cookie to be created on root path * Continue with first found guild if no guild cookie was found * Case-insensitive filtering on Tags page * Improve feedback when Tag creation fails (or succeeds) * Allow null/empty comments for promotions Use MudSpacer instead of manual flex-grow * Disable editing comments for closed campaigns Update UI after editing comment Better feedback after editing comment * Initial implementation of Configuration (Claims) * Small touchup * Remove unnecessary stuff in MainLayout * Modify DesignatedRoleService API slightly to return the id of the created entity * Initial implementation of Configuration (Roles) * Slight improvement to styling of menu for Configuration and Logs * Modify DesignatedChannelService API slightly to return the id of the created entity * Make IndividualDesignation component generic * Initial implementation of Configuration (Channels) * Slight improvements to Channels/Roles configuration pages * Make Autocomplete component generic * Move ModixUser and RoleInformation into Common * Modify interface of PromotionsService to return the PromotionActionSummary after updating a comment Fix bug in Promotions UI where you could edit a comment and inadvertently have two active ones as a result * Implement possibility of passing query parameters to infractions page, such as ?id=<infraction_id> and ?subject=<username> Also redirect from /infractions to /logs/infractions * Add possibility of accepting/rejecting/forcing a campaign as well as link to the infractions page for the campaign subject * Simplify styling on Infractions page * Use iconbuttons instead for accept/reject * Re-use existing extension method for getting full username * Initial implementation of Logs/DeletedMessages page * Fix bug with filtering DeletedMessageEntity - When Batch is set, use that to filter on CreatedBy(Id) - Move AsExpandable() to sourceQuery * Set _currentContext before potential early bail Close dialog on error * Show role color if relevant in Tags grid * Enable query parameter for Tags page * Use generic version of DialogParameters when instantiating Dialogs * Persist infraction table settings to local storage Move cookie constants into separate class * Slight styling improvements to stats page * Fix/consolidate date string formatting * Responsive navmenu * Responsive commands page * Better styling for DeletedMessages * Better styling for Infractions page * Responsive styling for Configuration (Claims) * Responsive styling for UserLookup * Slightly improve styling for landing and promotions pages * Remove unnecessary elements from landing page * Change Primary color to match the vue website * Fix weird flickering issue when navigating via anchors * Use MudGrid instead of MudDrawer Improve styling on Commands page Fix element Ids not being navigable to because of having spaces in them * Rename DiscordUserService to DiscordHelper * Make it possible to switch between Vue and Blazor via config flag 'UseBlazor' Convert Startup based startup to minimal hosting model in Modix Make Modix.Web into a class library * Remove appsettings from Modix.Web * Add NoDefaultLaunchSettingsFile to avoid re-creating launchSettings.json * Remove LocalStorageService in favor of reading cookies upon first HTTP request to the site and propagating settings through SessionState instead * Remove unused package/project references * Add favicon * Use Roles instead of Policy authorization * Improve grid size for bigger screens * Implement comment creation box to show when the user has yet to vote on a campaign * Fit content to screen width on Tags page * Minor touchups * Remove unused css * Fix more nullability stuff Some of it might technically be impossible scenarios, but "meh" :) * Fixed AutoComplete after breaking it by not invoking the RenderFragment after fixing nullability * Disable button/comment box on promotion creation page if no "next rank" is available * Fix compilation issue * Upgrade packages to stable version to fix startup issue * Fix ItemTemplate issue causing preview to be empty * Remove TODO comments, add error message when fetching of campaign details fails * Make Title an optional parameter on the AutoComplete component and remove the usage of it on the UserLookup page * Remove background color from UserLookup * Apply small gap rule on small screen sizes (that aren't quite xs yet) to avoid overlapping between elements * Improved styling for toolbar on Infractions page * Improved styling for toolbar on Tags page * Fix css not being served from the correct folder * Improve styling for Promotions page on small resolutions * Update NuGet packages and change from deprecated APIs/properties * Remove UseStaticFiles call in favor of linking to static files correctly in the _Host file * Update Dockerfile to use .net8 instead of .net8-preview image * Update more NuGet packages to .NET pinned versions and cleanup some of the version references in projects * Use maxcpucount:1 in Dockerfile to avoid files being used by other processes error * Slight mobile styling tweaks to Infractions and Tags page * Reduce reliance on inline styling * Update default value for UseBlazor setting in deployment configuration files * Format code * Improved styling on CreatePromotion page on small resolutions * Improved styling for Configuration/Logs pages * Add margin to make it look a bit nicer when scrolling to the bottom of the page * Tweak styling for large resolutions on Stats and Logs pages
1 parent 6c35da2 commit 045decc

File tree

78 files changed

+4217
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4217
-272
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ bld/
2626
.vs/
2727
# Uncomment if you have tasks that create the project's static files in wwwroot
2828
wwwroot/
29+
!Modix.Web/wwwroot/
2930
dataprotection/
3031

3132
# VS Code
@@ -261,5 +262,5 @@ Modix/config
261262
/Modix/Properties/launchSettings.json
262263

263264
*.DotSettings
264-
Modix/developmentSettings\.json
265-
Modix/logs/*
265+
**/developmentSettings\.json
266+
**/logs/*

Directory.Build.targets

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,28 @@
1414
<ItemGroup>
1515
<PackageReference Update="Discord.Net" Version="3.11.0" />
1616

17-
<PackageReference Update="Microsoft.EntityFrameworkCore" Version="8.0.0-preview.4.*" />
18-
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.4.*"/>
19-
<PackageReference Update="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0-preview.4.*" />
20-
<PackageReference Update="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-preview.4" />
17+
<PackageReference Update="Microsoft.EntityFrameworkCore" Version="8.0.0" />
18+
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="8.0.0"/>
19+
<PackageReference Update="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0" />
20+
<PackageReference Update="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
2121

2222
<PackageReference Update="Nito.AsyncEx.Coordination" Version="5.1.2" />
2323

24-
<PackageReference Update="AspNet.Security.OAuth.Discord" Version="7.0.2" />
24+
<PackageReference Update="AspNet.Security.OAuth.Discord" Version="8.0.0" />
2525

26-
<PackageReference Update="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0-preview.6.*" />
27-
<PackageReference Update="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.0-preview.6.*" />
28-
<PackageReference Update="Microsoft.Extensions.Caching.Memory" Version="8.0.0-preview.6.*" />
29-
<PackageReference Update="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0-preview.6.*" />
30-
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="8.0.0-preview.6.*" />
31-
<PackageReference Update="Microsoft.Extensions.Hosting" Version="8.0.0-preview.6.*" />
32-
<PackageReference Update="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0-preview.6.*" />
33-
<PackageReference Update="Microsoft.Extensions.Http" Version="8.0.0-preview.6.*" />
34-
<PackageReference Update="Microsoft.Extensions.Http.Polly" Version="8.0.0-preview.6.*" />
35-
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0-preview.6.*" />
36-
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="8.0.0-preview.6.*" />
37-
<PackageReference Update="Microsoft.Extensions.Options" Version="8.0.0-preview.6.*" />
38-
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0-preview.6.*" />
26+
<PackageReference Update="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
27+
<PackageReference Update="Microsoft.AspNetCore.SpaServices.Extensions" Version="8.0.0" />
28+
<PackageReference Update="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
29+
<PackageReference Update="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
30+
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
31+
<PackageReference Update="Microsoft.Extensions.Hosting" Version="8.0.0" />
32+
<PackageReference Update="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
33+
<PackageReference Update="Microsoft.Extensions.Http" Version="8.0.0" />
34+
<PackageReference Update="Microsoft.Extensions.Http.Polly" Version="8.0.0" />
35+
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
36+
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
37+
<PackageReference Update="Microsoft.Extensions.Options" Version="8.0.0" />
38+
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
3939
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.*" />
4040

4141
<PackageReference Update="Newtonsoft.Json" Version="13.0.3" />
@@ -44,13 +44,12 @@
4444

4545
<PackageReference Update="LinqKit.Microsoft.EntityFrameworkCore" Version="7.1.4" />
4646

47-
<PackageReference Update="Serilog" Version="3.0.1" />
48-
<PackageReference Update="Serilog.AspNetCore" Version="7.0.0" />
49-
<PackageReference Update="Serilog.Expressions" Version="3.4.1" />
50-
<PackageReference Update="Serilog.Extensions.Logging" Version="7.0.0" />
51-
<PackageReference Update="Serilog.Sinks.Console" Version="4.1.0" />
47+
<PackageReference Update="Serilog" Version="3.1.1" />
48+
<PackageReference Update="Serilog.AspNetCore" Version="8.0.0" />
49+
<PackageReference Update="Serilog.Expressions" Version="4.0.0" />
50+
<PackageReference Update="Serilog.Sinks.Console" Version="5.0.1" />
5251
<PackageReference Update="Serilog.Sinks.File" Version="5.0.0" />
53-
<PackageReference Update="Serilog.Sinks.Seq" Version="5.2.2" />
52+
<PackageReference Update="Serilog.Sinks.Seq" Version="6.0.0" />
5453

5554
<PackageReference Update="Humanizer.Core" Version="2.14.1" />
5655

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
FROM mcr.microsoft.com/dotnet/aspnet:8.0-preview AS base
1+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
22
WORKDIR /app
33

4-
FROM mcr.microsoft.com/dotnet/sdk:8.0-preview AS dotnet-build-base
4+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS dotnet-build-base
55
WORKDIR /src
66
RUN printf 'Package: nodejs\nPin: origin deb.nodesource.com\nPin-Priority: 600\n' > /etc/apt/preferences.d/nodesource
77
RUN apt-get update && apt-get install curl -y \
@@ -15,13 +15,13 @@ RUN dotnet restore Modix.sln
1515
COPY . .
1616

1717
FROM dotnet-build-base AS dotnet-build
18-
RUN dotnet build -c Release --no-restore Modix.sln
18+
RUN dotnet build -maxcpucount:1 -c Release --no-restore Modix.sln
1919

2020
FROM dotnet-build as dotnet-test
2121
RUN dotnet test -c Release --no-build --no-restore Modix.sln
2222

2323
FROM dotnet-build AS publish
24-
RUN dotnet publish -c Release --no-build --no-restore -o /app Modix/Modix.csproj
24+
RUN dotnet publish -maxcpucount:1 -c Release --no-build --no-restore -o /app Modix/Modix.csproj
2525

2626
FROM base AS final
2727
COPY --from=publish /app .

Modix.Bot.Test/Modix.Bot.Test.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Discord.Net" Version="2.4.0" />
9+
<PackageReference Include="Discord.Net" />
1010
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1111
<PackageReference Include="Moq" />
1212
<PackageReference Include="NUnit"/>

Modix.Data.Test/Assertions/DbContextSequenceExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ private static ResettableSequenceValueGenerator<TProperty> GetGetResettableSeque
8686
.GetOrAdd(property, entity, valueGeneratorConstructor);
8787
}
8888

89-
private static readonly Dictionary<Type, Func<IProperty, IEntityType, ValueGenerator>> _valueGeneratorConstructorsByValueType
90-
= new Dictionary<Type, Func<IProperty, IEntityType, ValueGenerator>>()
89+
private static readonly Dictionary<Type, Func<IProperty, ITypeBase, ValueGenerator>> _valueGeneratorConstructorsByValueType
90+
= new Dictionary<Type, Func<IProperty, ITypeBase, ValueGenerator>>()
9191
{
9292
[typeof(long)] = (p, e) => new ResettableInt64SequenceValueGenerator()
9393
};

Modix.Data/Models/Core/ModixConfig.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
3-
namespace Modix.Data.Models.Core
1+
namespace Modix.Data.Models.Core
42
{
53
public class ModixConfig
64
{
@@ -31,5 +29,6 @@ public class ModixConfig
3129
public string WebsiteBaseUrl { get; set; } = "https://mod.gg";
3230

3331
public bool EnableStatsd { get; set; }
32+
public bool UseBlazor { get; set; }
3433
}
3534
}

Modix.Data/Models/Moderation/DeletedMessageSearchCriteria.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,16 @@ public static IQueryable<DeletedMessageEntity> FilterBy(this IQueryable<DeletedM
132132
x => ReusableQueries.StringContainsUser.Invoke(x.Author, criteria!.Author!),
133133
!string.IsNullOrWhiteSpace(criteria?.Author))
134134
.FilterBy(
135-
x => x.CreateAction.CreatedById == criteria!.CreatedById,
135+
x => x.Batch == null
136+
? x.CreateAction.CreatedById == criteria!.CreatedById
137+
: x.Batch.CreateAction.CreatedById == criteria!.CreatedById,
136138
criteria?.CreatedById != null)
137139
.FilterBy(
138-
x => ReusableQueries.StringContainsUser.Invoke(x.CreateAction.CreatedBy!, criteria!.CreatedBy!),
140+
x => ReusableQueries.StringContainsUser.Invoke(
141+
x.Batch == null
142+
? x.CreateAction.CreatedBy!
143+
: x.Batch.CreateAction.CreatedBy,
144+
criteria!.CreatedBy!),
139145
!string.IsNullOrWhiteSpace(criteria?.CreatedBy))
140146
.FilterBy(
141147
x => ReusableQueries.DbCaseInsensitiveContains.Invoke(x.Content, criteria!.Content!),

Modix.Data/Repositories/DeletedMessageRepository.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,12 @@ public async Task CreateAsync(
9393
public async Task<RecordsPage<DeletedMessageSummary>> SearchSummariesPagedAsync(
9494
DeletedMessageSearchCriteria searchCriteria, IEnumerable<SortingCriteria> sortingCriteria, PagingCriteria pagingCriteria)
9595
{
96-
var sourceQuery = ModixContext.Set<DeletedMessageEntity>().AsNoTracking();
96+
var sourceQuery = ModixContext.Set<DeletedMessageEntity>().AsNoTracking().AsExpandable();
9797

9898
var filteredQuery = sourceQuery
9999
.FilterBy(searchCriteria);
100100

101101
var pagedQuery = filteredQuery
102-
.AsExpandable()
103102
.Select(DeletedMessageSummary.FromEntityProjection)
104103
.SortBy(sortingCriteria, DeletedMessageSummary.SortablePropertyMap)
105104
.OrderThenBy(x => x.MessageId, SortDirection.Ascending)

Modix.Services.Test/Modix.Services.Test.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
9+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
1010
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
1111
<PackageReference Include="Microsoft.NET.Test.Sdk" />
1212
<PackageReference Include="Moq" />

Modix.Services/Core/DesignatedChannelService.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Threading;
45
using System.Threading.Tasks;
5-
66
using Discord;
7-
8-
using Serilog;
9-
107
using Modix.Data.Models.Core;
118
using Modix.Data.Repositories;
12-
using System.Threading;
9+
using Serilog;
1310

1411
namespace Modix.Services.Core
1512
{
@@ -25,7 +22,7 @@ public interface IDesignatedChannelService
2522
/// <param name="channel">The channel to be assigned.</param>
2623
/// <param name="type">The type of designation to be assigned.</param>
2724
/// <returns>A <see cref="Task"/> that will complete when the operation has completed.</returns>
28-
Task AddDesignatedChannelAsync(IGuild guild, IMessageChannel channel, DesignatedChannelType type);
25+
Task<long> AddDesignatedChannelAsync(IGuild guild, IMessageChannel channel, DesignatedChannelType type);
2926

3027
/// <summary>
3128
/// Unassigns a channel's previously given designation, for a given guild.
@@ -117,7 +114,7 @@ public DesignatedChannelService(IDesignatedChannelMappingRepository designatedCh
117114
}
118115

119116
/// <inheritdoc />
120-
public async Task AddDesignatedChannelAsync(IGuild guild, IMessageChannel logChannel, DesignatedChannelType type)
117+
public async Task<long> AddDesignatedChannelAsync(IGuild guild, IMessageChannel logChannel, DesignatedChannelType type)
121118
{
122119
AuthorizationService.RequireAuthenticatedUser();
123120
AuthorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingCreate);
@@ -135,7 +132,7 @@ public async Task AddDesignatedChannelAsync(IGuild guild, IMessageChannel logCha
135132
throw new InvalidOperationException($"{logChannel.Name} in {guild.Name} is already assigned to {type}");
136133
}
137134

138-
await DesignatedChannelMappingRepository.CreateAsync(new DesignatedChannelMappingCreationData()
135+
var id = await DesignatedChannelMappingRepository.CreateAsync(new DesignatedChannelMappingCreationData()
139136
{
140137
GuildId = guild.Id,
141138
ChannelId = logChannel.Id,
@@ -144,6 +141,8 @@ await DesignatedChannelMappingRepository.CreateAsync(new DesignatedChannelMappin
144141
});
145142

146143
transaction.Commit();
144+
145+
return id;
147146
}
148147
}
149148

@@ -205,11 +204,11 @@ public Task<bool> AnyDesignatedChannelAsync(ulong guildId, DesignatedChannelType
205204
/// <inheritdoc />
206205
public Task<IReadOnlyCollection<ulong>> GetDesignatedChannelIdsAsync(ulong guildId, DesignatedChannelType type)
207206
=> DesignatedChannelMappingRepository.SearchChannelIdsAsync(new DesignatedChannelMappingSearchCriteria()
208-
{
209-
GuildId = guildId,
210-
Type = type,
211-
IsDeleted = false
212-
});
207+
{
208+
GuildId = guildId,
209+
Type = type,
210+
IsDeleted = false
211+
});
213212

214213
/// <inheritdoc />
215214
public async Task<IReadOnlyCollection<IMessageChannel>> GetDesignatedChannelsAsync(IGuild guild, DesignatedChannelType type)

0 commit comments

Comments
 (0)