Convert to Blazor (#6)

Convert existing progress to Blazor on client and server
This commit was merged in pull request #6.
This commit is contained in:
2020-12-18 21:46:28 -05:00
committed by GitHub
parent 2f84821d11
commit e0b3fa8759
97 changed files with 2592 additions and 31063 deletions

View File

@@ -0,0 +1,13 @@
namespace JobsJobsJobs.Shared.Api
{
/// <summary>
/// A successful log on; returns JWT, citizen ID, and display name
/// </summary>
public record LogOnSuccess(string Jwt, string Id, string Name)
{
/// <summary>
/// The ID return value as a citizen ID
/// </summary>
public CitizenId CitizenId => CitizenId.Parse(Id);
}
}

View File

@@ -0,0 +1,15 @@
using NodaTime;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A user of Jobs, Jobs, Jobs
/// </summary>
public record Citizen(
CitizenId Id,
string NaUser,
string DisplayName,
string ProfileUrl,
Instant JoinedOn,
Instant LastSeenOn);
}

View File

@@ -0,0 +1,26 @@
using System.Threading.Tasks;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// The ID of a user (a citizen of Gitmo Nation)
/// </summary>
public record CitizenId(ShortId Id)
{
/// <summary>
/// Create a new citizen ID
/// </summary>
/// <returns>A new citizen ID</returns>
public static async Task<CitizenId> Create() => new CitizenId(await ShortId.Create());
/// <summary>
/// Attempt to create a citizen ID from a string
/// </summary>
/// <param name="id">The prospective ID</param>
/// <returns>The citizen ID</returns>
/// <exception cref="System.FormatException">If the string is not a valid citizen ID</exception>
public static CitizenId Parse(string id) => new CitizenId(ShortId.Parse(id));
public override string ToString() => Id.ToString();
}
}

View File

@@ -0,0 +1,7 @@
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A continent
/// </summary>
public record Continent(ContinentId Id, string Name);
}

View File

@@ -0,0 +1,24 @@
using System.Threading.Tasks;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// The ID of a continent
/// </summary>
public record ContinentId(ShortId Id)
{
/// <summary>
/// Create a new continent ID
/// </summary>
/// <returns>A new continent ID</returns>
public static async Task<ContinentId> Create() => new ContinentId(await ShortId.Create());
/// <summary>
/// Attempt to create a continent ID from a string
/// </summary>
/// <param name="id">The prospective ID</param>
/// <returns>The continent ID</returns>
/// <exception cref="System.FormatException">If the string is not a valid continent ID</exception>
public static ContinentId Parse(string id) => new ContinentId(ShortId.Parse(id));
}
}

View File

@@ -0,0 +1,21 @@
using Markdig;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A string of Markdown text
/// </summary>
public record MarkdownString(string Text)
{
/// <summary>
/// The Markdown conversion pipeline (enables all advanced features)
/// </summary>
private readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
/// <summary>
/// Convert this Markdown string to HTML
/// </summary>
/// <returns>This Markdown string as HTML</returns>
public string ToHtml() => Markdown.ToHtml(Text, Pipeline);
}
}

View File

@@ -0,0 +1,25 @@
using NodaTime;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A job seeker profile
/// </summary>
public record Profile(
CitizenId Id,
bool SeekingEmployment,
bool IsPublic,
ContinentId ContinentId,
string Region,
bool RemoteWork,
bool FullTime,
MarkdownString Biography,
Instant LastUpdatedOn,
MarkdownString? Experience)
{
/// <summary>
/// Navigation property for continent
/// </summary>
public Continent? Continent { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A short ID
/// </summary>
public record ShortId(string Id)
{
/// <summary>
/// Validate the format of the short ID
/// </summary>
private static readonly Regex ValidShortId =
new Regex("^[a-z0-9_-]{12}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>
/// Create a new short ID
/// </summary>
/// <returns>A new short ID</returns>
public static async Task<ShortId> Create() => new ShortId(await Nanoid.Nanoid.GenerateAsync(size: 12));
/// <summary>
/// Try to parse a string of text into a short ID
/// </summary>
/// <param name="text">The text of the prospective short ID</param>
/// <returns>The short ID</returns>
/// <exception cref="FormatException">If the format is not valid</exception>
public static ShortId Parse(string text)
{
if (text.Length == 12 && ValidShortId.IsMatch(text)) return new ShortId(text);
throw new FormatException($"The string {text} is not a valid short ID");
}
public override string ToString() => Id;
}
}

View File

@@ -0,0 +1,7 @@
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A skill the job seeker possesses
/// </summary>
public record Skill(SkillId Id, CitizenId CitizenId, string Description, string? Notes);
}

View File

@@ -0,0 +1,16 @@
using System.Threading.Tasks;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// The ID of a skill
/// </summary>
public record SkillId(ShortId Id)
{
/// <summary>
/// Create a new skill ID
/// </summary>
/// <returns>A new skill ID</returns>
public static async Task<SkillId> Create() => new SkillId(await ShortId.Create());
}
}

View File

@@ -0,0 +1,14 @@
using NodaTime;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A record of success finding employment
/// </summary>
public record Success(
SuccessId Id,
CitizenId CitizenId,
Instant RecordedOn,
bool FromHere,
MarkdownString? Story);
}

View File

@@ -0,0 +1,16 @@
using System.Threading.Tasks;
namespace JobsJobsJobs.Shared
{
/// <summary>
/// The ID of a success report
/// </summary>
public record SuccessId(ShortId Id)
{
/// <summary>
/// Create a new success report ID
/// </summary>
/// <returns>A new success report ID</returns>
public static async Task<SuccessId> Create() => new SuccessId(await ShortId.Create());
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Markdig" Version="0.22.1" />
<PackageReference Include="Nanoid" Version="2.1.0" />
<PackageReference Include="NodaTime" Version="3.0.3" />
</ItemGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,64 @@
namespace JobsJobsJobs.Shared
{
/// <summary>
/// A result with two different possibilities
/// </summary>
/// <typeparam name="TOk">The type of the Ok result</typeparam>
public struct Result<TOk>
{
private readonly TOk? _okValue;
/// <summary>
/// Is this an Ok result?
/// </summary>
public bool IsOk { get; init; }
/// <summary>
/// Is this an Error result?
/// </summary>
public bool IsError
{
get => !IsOk;
}
/// <summary>
/// The Ok value
/// </summary>
public TOk Ok
{
get => _okValue!;
}
/// <summary>
/// The error value
/// </summary>
public string Error { get; set; }
/// <summary>
/// Constructor (inaccessible - use static creation methods)
/// </summary>
/// <param name="isOk">Whether this is an Ok result</param>
/// <param name="okValue">The value of the Ok result</param>
/// <param name="error">The error message of the Error result</param>
private Result(bool isOk, TOk? okValue = default, string error = "")
{
IsOk = isOk;
_okValue = okValue;
Error = error;
}
/// <summary>
/// Create an Ok result
/// </summary>
/// <param name="okValue">The value of the Ok result</param>
/// <returns>The Ok result</returns>
public static Result<TOk> AsOk(TOk okValue) => new Result<TOk>(true, okValue);
/// <summary>
/// Create an Error result
/// </summary>
/// <param name="error">The error message</param>
/// <returns>The Error result</returns>
public static Result<TOk> AsError(string error) => new Result<TOk>(false) { Error = error };
}
}