A Literary Clock in Blazor

« Previous article:   Next article: »
Putting quotes in your Powershell prompt. Blog Home What is (was) Beta Software?

This post is for day 13 of the 2022 C# Advent Calendar operated by Matthew Groves. Thanks for letting me participate!

When I last blogged (um.. four months ago…), we discussed putting time-related quotes into your PowerShell prompt (Click the left arrow at the top of the page to see that one), and I mentioned a Blazor version. It’s time to present that.

Some background: back in 2011, The Guardian built a list of literary quotes containing a time of day, from reader contributions, and made the lists publically available. They been re-used on a number of projects.

One such project is https://github.com/JohannesNE/literature-clock, which uses Javascript to present the appropriate quotes on a webpage. The repository also maintains a pipe delimited list of all the quotes, and has a program written in R to read that file and generate individual json files for each minute.

That’s all well & good, but I do C#, so I figured I’d port that in Blazer (while making the R program into a C# console app).

After that app runs, for each minute (well, for 958 of the 1440 minutes in a day), there’s a json file with a name in the form “HH_MM.json”, which holds an array of objects which look like this:

Not sure how fast the R version is, but on my PC, the C# version takes 13 seconds to produce the 958 files.

So, let’s look at the Blazor page, a piece at a time. We need to do something once every minute, so on start, we set up a timer, which calls the method which does the real work:

protected override  Task OnInitializedAsync()
{
	_timer = new Timer(obj => OnTimerCallback(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
	return Task.CompletedTask;
}
private void OnTimerCallback()
{
	_ = InvokeAsync(() =>
	{
		UpdateText(DateTime.Now);
	});
}

The reason we pass in the time will become clear in a minute. In UpdateText, we read the quote file for the passed time:

protected async  void UpdateText(DateTime time) 
{
	var strTime =time.ToString("HH_mm");
	Quote[] quotes = await Http.GetFromJsonAsync<Quote[]>($"times/{strTime}.json") ?? Array.Empty<Quote>();

But, since not every minute has a quote, if we don’t find any for the current minute, we’ll hold on to and use the previous minutes – or, if we don’t have any already (i.e., we just started), we just go backwards in time until we find one:

	if (quotes.Any())
		_quotes = quotes;
	else if (_quotes == null)
	{
		UpdateText(time.AddMinutes(-1));
		return;
	}

Then we just randomly pick a quote from the array

	_quote = _quotes[Rnd.Next(quotes.Length)];

And bind it to the page:

	<div id="main_text">
		<blockquote id="lit_quote">@_quote.Prefix<em>@_quote.Quote_time_case</em>@_quote.Suffix</blockquote>
		<cite>
			-
			<span id="book">@_quote.Title</span>,
			<span id="author">@_quote.Author</span>
		</cite>
	</div>

The javascript version includes a formula to adjust the font size to accomodate the widely differing quote lengths. This is easily transaled into C#:

	var quote_len = _quote.Quote_first.Length +
	                 _quote.Quote_time_case.Length +
	                 _quote.Quote_last.Length;

	_fontSize = 7.000864 - 0.01211676 * quote_len + 0.00001176814 * quote_len * quote_len - 1.969435e-9 * quote_len * quote_len * quote_len;

And Blazor:

    <style>
    	#lit_quote {
    		font-size: @(_fontSize)vw
    	}
    </style>

You can see the clock in action (along with two other online clocks of mine) here: https://noveltheory.com/clock/

The full project repository (which includes a folk of the original Javascript version) is here https://github.com/jamescurran/literature-clock.

Tags: