();
+
foreach (var (name, parameters, oldValue) in RawProperties)
{
- sb.Append(name);
- if (parameters != null)
- sb.Append($";{parameters}");
-
var value = name switch
{
"SUMMARY" => _escape(Summary),
+ "DESCRIPTION" => _escape(Description),
+ "DTSTART" => FormatDate(parameters, DtStart),
+ "DTEND" => FormatDate(parameters, DtEnd),
_ => oldValue
};
- sb.Append($":{value}\r\n");
+ if (value != null)
+ {
+ sb.Append(name);
+ if (parameters != null)
+ sb.Append($";{parameters}");
+ sb.Append($":{value}\r\n");
+ }
+
+ seenNames.Add(name);
}
+ // If one of the event's properties did not exist yet, add it to the end.
+ if (!seenNames.Contains("SUMMARY") && !string.IsNullOrWhiteSpace(Summary))
+ sb.Append($"SUMMARY:{_escape(Summary)}\r\n");
+ if (!seenNames.Contains("DESCRIPTION") && !string.IsNullOrWhiteSpace(Description))
+ sb.Append($"DESCRIPTION:{_escape(Description)}\r\n");
+ if (!seenNames.Contains("DTSTART") && DtStart != null)
+ sb.Append($"DTSTART:{DtStart?.ToUniversalTime().ToString(Calendar.DateTimeFormat)}\r\n");
+ if (!seenNames.Contains("DTEND") && !seenNames.Contains("DURATION") && CalculatedEnd != null)
+ sb.Append($"DTEND:{DtStart?.ToUniversalTime().ToString(Calendar.DateTimeFormat)}\r\n");
+
sb.Append("END:VEVENT\r\n");
sb.Append("END:VCALENDAR\r\n");
return sb.ToString();
}
+
+ private static string FormatDate(string parameters, DateTime? dateTime)
+ {
+ if (dateTime == null)
+ return null;
+
+ DateTimeZone timezone;
+ if (parameters != null)
+ {
+ // If there's parameters, see if we can find a timezone, or default to UTC.
+ var parameterList = parameters.Split('=');
+ var timezoneIana =
+ Array.Find(parameterList, s => s.StartsWith("TZID="))?[5..] ?? "UTC";
+ timezone = DateTimeZoneProviders.Tzdb[timezoneIana];
+ }
+ else
+ {
+ // If there's no parameters, default to UTC.
+ timezone = DateTimeZone.Utc;
+ }
+
+ // Convert the datetime to the used timezone, to avoid changing it.
+ return Instant
+ .FromDateTimeUtc(((DateTime) dateTime).ToUniversalTime())
+ .InZone(timezone)
+ .ToString(Calendar.DateTimeFormat, CultureInfo.InvariantCulture);
+ }
}
}
\ No newline at end of file
diff --git a/BlazorApp/Pages/Event.razor b/BlazorApp/Pages/Event.razor
index 0d39f1c..d697a81 100644
--- a/BlazorApp/Pages/Event.razor
+++ b/BlazorApp/Pages/Event.razor
@@ -1,4 +1,5 @@
@page "/calendars/{calendarId:int}/events/{eventUid}"
+@using System.Globalization
@inject Data.CalendarService _calendarService
@inject NavigationManager _navigationManager
@@ -14,41 +15,69 @@
}
else
{
- @if (!_editTitle)
+ Event
+
+
+
Summary:
+ @if (_editing != Editing.Summary)
+ {
+
_editing = Editing.Summary">
+ @_event.Summary
+
+ }
+ else
+ {
+
+
+
+
+ }
+
+
+
+ Start:
+ @if (_editing != Editing.DtStart)
+ {
+ _editing = Editing.DtStart">
+ @(_event.DtStart?.ToString(DtFormat) ?? "Unknown")
+
+ }
+ else
+ {
+
+
+ }
+
+
+
+ End:
+ @if (_editing != Editing.CalculatedEnd)
+ {
+ _editing = Editing.CalculatedEnd">
+ @(_event.CalculatedEnd?.ToString(DtFormat) ?? "Unknown")
+
+ }
+ else
+ {
+
+
+ }
+
+
+ @if (_editing != Editing.Description)
{
-
- _editTitle = true">@_event.Summary
-
+ _editing = Editing.Description">
+ @(_event.Description ?? "No description")
+
}
else
- {
-
-
- }
-
-
- ETag: @_event.ETag
-
-
- @if (_event.DtStart != null)
{
- Start: @_event.DtStart
+
+
}
- @if (_event.CalculatedEnd != null)
- {
-
- End: @_event.CalculatedEnd
-
- }
-
- @if (_event.Description != null)
- {
- @_event.Description
- }
-
@if (!_confirmDelete)
{
@@ -67,13 +96,24 @@ else
[Parameter]
public string EventUid { get; set; }
+ const string DtFormat = "dd-MM-yyyy HH:mm:ss";
+
private Data.Calendar _calendar;
private Data.Event _event;
private bool _notFound;
- private bool _editTitle;
private bool _confirmDelete;
+ private enum Editing
+ {
+ Summary,
+ DtStart,
+ CalculatedEnd,
+ Description,
+ }
+
+ private Editing? _editing;
+
protected override async Task OnInitializedAsync()
{
_calendar = await Data.CalendarService.GetCalendarById(CalendarId);
@@ -84,6 +124,7 @@ else
private async Task UpdateEvent()
{
_event = await _calendar.UpdateEvent(_event);
+ _editing = null;
}
private async Task DeleteEvent()
diff --git a/BlazorApp/Pages/Event.razor.css b/BlazorApp/Pages/Event.razor.css
index 1f39632..b97bd55 100644
--- a/BlazorApp/Pages/Event.razor.css
+++ b/BlazorApp/Pages/Event.razor.css
@@ -1,3 +1,3 @@
-.event-summary > span:hover {
+.property-editable:hover {
border: 1px solid rgba(0, 0, 0, 0.2);
}
\ No newline at end of file
diff --git a/BlazorApp/Startup.cs b/BlazorApp/Startup.cs
index 13c689f..637a8ed 100644
--- a/BlazorApp/Startup.cs
+++ b/BlazorApp/Startup.cs
@@ -20,7 +20,7 @@ namespace BlazorApp
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
- services.AddRazorPages();
+ services.AddRazorPages().AddRazorRuntimeCompilation();
services.AddServerSideBlazor();
services.AddSingleton();
}