ASP.NET Core 6.0 - Users With Comments
This article introduces the ASP.NET Core 6.0 - Users With Comments Project (UWCP) and an article series about the refactoring of comments on KenHaggerty.Com. I will assume you have downloaded the ASP.NET Core 6.0 - Users With Comments Project.
Users With Comments Project and Article Series
The ASP.NET Core 6.0 - Users With Comments Project (UWCP) implements public member profiles and a moderated comment workflow from user submission to admin publication. I started with the ASP.NET Core 6.0 - Users With Device 2FA Project (UWD2FAP) which implements WebAuthn, also known as FIDO2, instead of authenticator apps. The latest version of the UWCP is published at Preview. KenHaggerty. Com. I encourage you to register and submit a comment. Details, screenshots, and related articles can be found at ASP.NET Core 6.0 - Users With Comments Project. The details page includes the version change log.
- ASP.NET Core 6.0 - Users With Comments
- ASP.NET Core 6.0 - API Implementation
- ASP.NET Core 6.0 - API Authorization
- ASP.NET Core 6.0 - Member Profile
- ASP.NET Core 6.0 - Profile Image Control
- ASP.NET Core 6.0 - Comments Workflow
- ASP.NET Core 6.0 - Filtered Comments
- ASP.NET Core 6.0 - Member Listings
- ASP.NET Core 6.0 - Lazy Load On Scroll
Visual Studio 2022 (VS 2022) is required to develop .NET 6 and ASP.NET Core 6.0 applications. .NET 6 and ASP.NET Core 6.0 are Long Term Support (LTS) releases and will be supported until November 08, 2024. .NET 5 is a Current release and will be supported until May 08, 2022. .NET 3.1 is a LTS release and will be supported until December 3, 2022. See .NET and .NET Core release lifecycle.
I developed a comments feature for KenHaggerty.Com over three years ago, right after I had successfully published the first couple of articles. I implemented a required displayed name property for users and a related user image entity. The displayed name and user image were used when posting a comment. I developed a moderated workflow for comment submission by the user to admin publication. A topics feature which implemented comments became comments for projects. When I started to migrate comments from topics to projects, I realized I had too much code in too many places.
I usually make big changes when I refactor a feature, I try to apply new knowledge and lessons learned. I wanted the new comments feature to be easy to implement for multiple entity types. I cloned the UWD2FAP to the new ASP.NET Core 6.0 - Users With Comments Project (UWCP) and added a few mock entities to test and demonstrate portability. The article series describes the implementation of the new comments feature.
API Implementation
I didn't want comments to delay loading the entity content. I developed Comments, MemberProfile, and ProfileImage ApiControllers which allow loading comments from JavaScript after the page is loaded. This article describes implementing API support for an existing razor pages project.
API Authorization
Many API endpoints need restricted access. Some need access by the user and/or an authorized administrator. This article describes protecting access to various API endpoints.
Member Profile
I wanted a clear separation of public and private profile information. I developed a MemberProfile entity to store DisplayedName, ImageString, and About properties. I created a new MemberProfile for existing users with the DisplayedName from the users claims. Then I migrated existing user's image to MemberProfile. After implementation and testing, I deleted the DisplayedName claim and user image table.
Profile Image Control
I wanted to encourage users to use a profile image. I had already implemented Croppie to crop and resize a file image before it is uploaded and stored as a Data URL. I developed a ProfileImage API and a new Profile Image Control which adds options to use a webcam snapshot, or a stock image stored on the server.
Comments Workflow
I had comment management pages for entity, user, unapproved comments, users with comments, and entities with comments. I refactored the comment workflow with the Comments API two new partial views, a comments control partial and a comments filtered partial. All actions on comments originate from the control partial which is implemented per entity. All the other pages use the filtered partial. The filtered partial lists comments with links to the comment on the entity control panel.
Public Member Listing
I implemented an opt-in Public Member Listing. The Member Listing displays the user image and the About property from MemberProfile and a Member title composed from the DisplayedName and CreatedDate. When the MemberProfile is listed, the member's comments contain a link to the Member Listing.
Lazy Load On Scroll
I implemented lazy loading for long lists of comments and members. When a comment hash is set, the list is incrementally fetched until the hashed comment is found. I added a comments option to the Seeded Users feature to develop and test long lists.
Notes:
As I refactored the comments feature, I often asked myself, why did I do it that way. Most times I clarified the code. Sometimes I spent hours remembering why. I always end up learning a few new things.
Iterating a list of maybe disabled checkboxes.
Before
@for (var i = 0; i < Model.List.Count(); i++) { <div class="form-check"> @if (Model.List[i].Disabled) { <input type="checkbox" class="form-check-input" disabled /> } else { <input type="checkbox" class="form-check-input" /> } <label class="form-check-label" >Display in Member Listing</label> </div> }
Now
@for (var i = 0; i < Model.List.Count(); i++) { <div class="form-check"> <input type="checkbox" class="form-check-input" disabled=@(Model..List[i].Disabled ? "disabled" : null) /> <label class="form-check-label" >Display in Member Listing</label> </div> }
Image tag with src set by JavaScript.
Before
<img class="commenter-image align-self-start p-1" alt="User Image" src="#" />
Now
<img class="commenter-image align-self-start p-1" alt="User Image" src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" />
Read Aloud skips input value but reads button text.
Before
<input type="submit" value="Update" class="btn btn-primary" />
Now
<button type="submit" class="btn btn-primary" >Update</button>
Implementing nullable context.
Before
if (!User.Identity!.IsAuthenticated) return NotFound();
Now
if (!User.Identity?.IsAuthenticated ?? false) return NotFound();
Implementing JavaScript literals.
Before
fetchUrl += '?entityId=' + entityId + '&appUserId=' + appUserId + '&showHidden=' + showHidden;
Now
fetchUrl += `?entityId=${entityId}&appUserId=${appUserId}&showHidden=${showHidden}`;
Comments(0)