Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversion of WinForms app to a portable browser-based version #69

Draft
wants to merge 26 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
27f30e5
Initial conversion of WinForms app to Blazor
atifaziz Jan 10, 2020
ebc891e
Add BlazorTester to NCrontab repository
Swimburger Aug 19, 2020
fcd3393
add back accidentally removed global json and readme
Swimburger Aug 19, 2020
aec150b
undo unnecessary sln changes
Swimburger Aug 19, 2020
6699ed6
Update to Blazor WebAssembly RTM
atifaziz Aug 20, 2020
f9cedf8
Use apt-get instead of apt in CI build
atifaziz Aug 21, 2020
77fa954
Make installs silent during CI build
atifaziz Aug 21, 2020
4bdf25f
Restore original casing of package Ids
atifaziz Aug 21, 2020
ba26f81
Remove obsolete conditional compilation
atifaziz Aug 21, 2020
324baa2
Merge branch 'master' into blazor-viewer
atifaziz Aug 21, 2020
74e3f24
Use .NET Core SDK 3.1.300
atifaziz Aug 21, 2020
d8c8377
Build release config too
atifaziz Aug 21, 2020
cf37dae
Initial evaluation on init
atifaziz Aug 21, 2020
4223fd3
Remove unused nav menu
atifaziz Aug 21, 2020
948aa03
Simple single page setup
atifaziz Aug 21, 2020
ba4041a
Merge pull request #68 from Swimburger/blazor-viewer
atifaziz Aug 3, 2024
671d6e2
Merge branch 'master' into blazor-viewer
atifaziz Aug 2, 2024
1fe718f
Merge remote-tracking branch 'origin/blazor-viewer' into blazor-viewer
atifaziz Aug 3, 2024
19d8509
Restore build script for Windows
atifaziz Aug 3, 2024
ce55aa7
Rename "NCrontabWebViewer" to "NCrontabViewer"
atifaziz Aug 3, 2024
402a1ed
Consolidate "NCrontabTester" and "NCrontabViewer"
atifaziz Aug 3, 2024
4c71118
Undo unrelated changes
atifaziz Aug 3, 2024
f716577
Discard unused lambda arg
atifaziz Aug 3, 2024
eafc829
Remove redundant disabling of CA2007
atifaziz Aug 3, 2024
e2c1e1e
Merge branch 'master' into blazor-viewer
atifaziz Aug 3, 2024
28ea3af
Merge branch 'master' into blazor-viewer
atifaziz Aug 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions NCrontabViewer/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,5 @@
# CA1303: Do not pass literals as localized parameters
dotnet_diagnostic.CA1303.severity = suggestion

[*Form*.cs]

# IDE0021: Use expression body for constructor
dotnet_diagnostic.IDE0021.severity = suggestion
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = suggestion
208 changes: 208 additions & 0 deletions NCrontabViewer/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
@page "/"
@using System.Globalization
@using System.Text
@using NCrontab;
@using Microsoft.AspNetCore.WebUtilities
@inject NavigationManager NavigationManager
@inject IJSRuntime JSRuntime
<div class="content px-4">

<span>
<label>Start:</label>
<input type="date" @bind="StartTime"/>
</span>

<span>
<label>End:</label>
<input type="date" @bind="EndTime"/>
</span>

<span>
<label>Expression:</label>
<input @bind-value="Expression" @bind-value:event="oninput"/>
</span>

<button @onclick="CopyLink" type="button" class="btn btn-light">Copy Link</button>

<h2>Occurrences (@_occurrences.Count@(_hasMoreOccurrences ? "+" : null))</h2>

@if (_occurrences.Any())
{
<ol id="occurrences">
@foreach (var occurrence in _occurrences)
{
<li><span class="occurrence">@occurrence</span></li>
}
</ol>
}
else if (_parseError is Exception e)
{
<div class="alert alert-danger" role="alert">
@e.Message
</div>
}

</div>

@code
{
DateTime _startTime;
DateTime _endTime;
string _expression = null!;
Exception? _parseError;
bool _hasMoreOccurrences;
readonly List<string> _occurrences = new List<string>();

DateTime StartTime
{
get => _startTime;
set
{
if (value == _startTime)
return;
_startTime = value;
Refresh();
}
}

DateTime EndTime
{
get => _endTime;
set
{
if (value == _endTime)
return;
_endTime = value;
Refresh();
}
}

string Expression
{
get => _expression;
set
{
if (value == _expression)
return;
_expression = value;
Refresh();
}
}

protected override Task OnInitializedAsync()
{
var uri = new Uri(NavigationManager.Uri);
var query = QueryHelpers.ParseQuery(uri.Query);
_expression = query.TryGetValue("expression", out var vs) && vs.First() is { } queryExpression
? queryExpression
: "* * * * *";
var today = DateTime.Today;
_startTime = today;
_endTime = today.AddDays(1);
Refresh();
return base.OnInitializedAsync();
}

void Refresh()
{
_hasMoreOccurrences = false;
_occurrences.Clear();

var fields = Expression.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var isSixPart = fields.Length == 6;

var (schedule, parseError) =
CrontabSchedule.TryParse(Expression,
new CrontabSchedule.ParseOptions { IncludingSeconds = isSixPart },
s => (s, (Exception?)null),
ep => ((CrontabSchedule?)null, ep()));

_parseError = parseError;

if (schedule != null)
DoCrontabbing(schedule, isSixPart);
}

void DoCrontabbing(CrontabSchedule schedule, bool includingSeconds)
{
var count = 0;
const int maxCount = 500;

foreach (var (_, s) in GetFormattedOccurrences(schedule, includingSeconds))
{
if (count + 1 > maxCount)
{
_hasMoreOccurrences = true;
break;
}

//_startTime = occurrence;
//_totalOccurrenceCount++;
count++;

_occurrences.Add(s);
}
}

IEnumerable<(DateTime, string)> GetFormattedOccurrences(CrontabSchedule schedule, bool includingSeconds)
{
var info = DateTimeFormatInfo.CurrentInfo;
var dayWidth = info.AbbreviatedDayNames.Max(s => s.Length);
var monthWidth = info.AbbreviatedMonthNames.Max(s => s.Length);
var timeComponent = includingSeconds ? "HH:mm:ss" : "HH:mm";
var timeFormat = $"{{0,-{dayWidth}:ddd}} {{0:dd}}, {{0,-{monthWidth}:MMM}} {{0:yyyy {timeComponent}}}";
var lastTimeString = new string('?', string.Format(timeFormat, DateTime.MinValue).Length);

var sb = new StringBuilder();

foreach (var occurrence in schedule.GetNextOccurrences(StartTime, EndTime))
{
sb.Length = 0;

var timeString = string.Format(timeFormat, occurrence);

sb.Append(timeString);
sb.Append(" | ");

var index = Diff(lastTimeString, timeString, 0, dayWidth, sb);
sb.Append(' ');
index = Diff(lastTimeString, timeString, index + 1, 2, sb);
sb.Append(", ");
index = Diff(lastTimeString, timeString, index + 2, monthWidth, sb);
sb.Append(' ');
index = Diff(lastTimeString, timeString, index + 1, 4, sb);
sb.Append(' ');
index = Diff(lastTimeString, timeString, index + 1, 2, sb);
sb.Append(':');
index = Diff(lastTimeString, timeString, index + 1, 2, sb);
if (includingSeconds)
{
sb.Append(':');
Diff(lastTimeString, timeString, index + 1, 2, sb);
}

lastTimeString = timeString;

yield return (occurrence, sb.ToString());
}

static int Diff(string oldString, string newString, int index, int length, StringBuilder builder)
{
if (string.CompareOrdinal(oldString, index, newString, index, length) == 0)
builder.Append('-', length);
else
builder.Append(newString, index, length);

return index + length;
}
}

void CopyLink()
{
var uri = new Uri(NavigationManager.Uri);
var query = QueryHelpers.ParseQuery(uri.Query);
query["expression"] = new Microsoft.Extensions.Primitives.StringValues(_expression);
var uriToCopy = QueryHelpers.AddQueryString(uri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped), "expression", _expression);
JSRuntime.InvokeVoidAsync("copyText", uriToCopy);
}
}
23 changes: 0 additions & 23 deletions NCrontabViewer/AssemblyInfo.cs

This file was deleted.

8 changes: 0 additions & 8 deletions NCrontabViewer/AssemblyInfo.g.cs

This file was deleted.

15 changes: 0 additions & 15 deletions NCrontabViewer/AssemblyInfo.g.tt

This file was deleted.

Loading