Range thing.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public readonly record struct CalendarDisplayRequest(CalendarDisplayType DisplayType, DateOnly AnchorDate);
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public static class CalendarRangeResolver
|
||||
{
|
||||
public static VisibleDateRange Resolve(CalendarDisplayRequest request, CalendarSettings settings, DateOnly today)
|
||||
{
|
||||
var startDate = GetStartDate(request.DisplayType, request.AnchorDate, settings);
|
||||
var endDate = GetEndDate(request.DisplayType, request.AnchorDate, startDate, settings);
|
||||
var dayCount = endDate.DayNumber - startDate.DayNumber + 1;
|
||||
var dates = Enumerable.Range(0, dayCount)
|
||||
.Select(offset => startDate.AddDays(offset))
|
||||
.ToArray();
|
||||
|
||||
return new VisibleDateRange(
|
||||
request.DisplayType,
|
||||
request.AnchorDate,
|
||||
startDate,
|
||||
endDate,
|
||||
request.AnchorDate,
|
||||
dayCount,
|
||||
today >= startDate && today <= endDate,
|
||||
startDate.Year == endDate.Year && startDate.Month == endDate.Month,
|
||||
dates);
|
||||
}
|
||||
|
||||
public static VisibleDateRange ChangeDisplayType(VisibleDateRange currentRange, CalendarDisplayType targetDisplayType, CalendarSettings settings, DateOnly today)
|
||||
{
|
||||
if (currentRange.DisplayType == targetDisplayType)
|
||||
{
|
||||
return currentRange;
|
||||
}
|
||||
|
||||
var anchorDate = currentRange.AnchorDate;
|
||||
|
||||
if (currentRange.DisplayType == CalendarDisplayType.Month)
|
||||
{
|
||||
anchorDate = currentRange.Contains(today) ? today : currentRange.StartDate;
|
||||
}
|
||||
|
||||
return Resolve(new CalendarDisplayRequest(targetDisplayType, anchorDate), settings, today);
|
||||
}
|
||||
|
||||
public static VisibleDateRange Navigate(VisibleDateRange currentRange, int direction, CalendarSettings settings, DateOnly today)
|
||||
{
|
||||
if (direction == 0)
|
||||
{
|
||||
return currentRange;
|
||||
}
|
||||
|
||||
var normalizedDirection = Math.Sign(direction);
|
||||
var anchorDate = currentRange.DisplayType switch
|
||||
{
|
||||
CalendarDisplayType.Day => currentRange.AnchorDate.AddDays(normalizedDirection),
|
||||
CalendarDisplayType.Week => currentRange.AnchorDate.AddDays(7 * normalizedDirection),
|
||||
CalendarDisplayType.WorkWeek => currentRange.AnchorDate.AddDays(7 * normalizedDirection),
|
||||
CalendarDisplayType.Month => currentRange.AnchorDate.AddMonths(normalizedDirection),
|
||||
_ => currentRange.AnchorDate
|
||||
};
|
||||
|
||||
return Resolve(new CalendarDisplayRequest(currentRange.DisplayType, anchorDate), settings, today);
|
||||
}
|
||||
|
||||
private static DateOnly GetStartDate(CalendarDisplayType displayType, DateOnly anchorDate, CalendarSettings settings)
|
||||
{
|
||||
return displayType switch
|
||||
{
|
||||
CalendarDisplayType.Day => anchorDate,
|
||||
CalendarDisplayType.Week => GetStartOfWeek(anchorDate, settings.FirstDayOfWeek),
|
||||
CalendarDisplayType.WorkWeek => GetStartOfWorkWeek(anchorDate, settings),
|
||||
CalendarDisplayType.Month => new DateOnly(anchorDate.Year, anchorDate.Month, 1),
|
||||
_ => anchorDate
|
||||
};
|
||||
}
|
||||
|
||||
private static DateOnly GetEndDate(CalendarDisplayType displayType, DateOnly anchorDate, DateOnly startDate, CalendarSettings settings)
|
||||
{
|
||||
return displayType switch
|
||||
{
|
||||
CalendarDisplayType.Day => anchorDate,
|
||||
CalendarDisplayType.Week => startDate.AddDays(6),
|
||||
CalendarDisplayType.WorkWeek => startDate.AddDays(settings.WorkWeekDayCount - 1),
|
||||
CalendarDisplayType.Month => new DateOnly(anchorDate.Year, anchorDate.Month, DateTime.DaysInMonth(anchorDate.Year, anchorDate.Month)),
|
||||
_ => anchorDate
|
||||
};
|
||||
}
|
||||
|
||||
private static DateOnly GetStartOfWeek(DateOnly date, DayOfWeek firstDayOfWeek)
|
||||
{
|
||||
var offset = ((int)date.DayOfWeek - (int)firstDayOfWeek + 7) % 7;
|
||||
return date.AddDays(-offset);
|
||||
}
|
||||
|
||||
private static DateOnly GetStartOfWorkWeek(DateOnly anchorDate, CalendarSettings settings)
|
||||
{
|
||||
var startOfWeek = GetStartOfWeek(anchorDate, settings.FirstDayOfWeek);
|
||||
var offsetToWorkWeekStart = settings.GetWeekOffset(settings.WorkWeekStart);
|
||||
return startOfWeek.AddDays(offsetToWorkWeekStart);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public sealed class CalendarRangeTextFormatter : ICalendarRangeTextFormatter
|
||||
{
|
||||
public string Format(VisibleDateRange range, IDateContextProvider dateContextProvider)
|
||||
{
|
||||
var culture = dateContextProvider.Culture;
|
||||
var startText = FormatDate(range.StartDate, culture);
|
||||
|
||||
if (range.DisplayType == CalendarDisplayType.Day)
|
||||
{
|
||||
return startText;
|
||||
}
|
||||
|
||||
return $"{startText} - {FormatDate(range.EndDate, culture)}";
|
||||
}
|
||||
|
||||
private static string FormatDate(DateOnly date, CultureInfo culture)
|
||||
=> date.ToString(culture.DateTimeFormat.ShortDatePattern, culture);
|
||||
}
|
||||
@@ -7,12 +7,33 @@ namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public record CalendarSettings(DayOfWeek FirstDayOfWeek,
|
||||
List<DayOfWeek> WorkingDays,
|
||||
DayOfWeek WorkWeekStart,
|
||||
DayOfWeek WorkWeekEnd,
|
||||
TimeSpan WorkingHourStart,
|
||||
TimeSpan WorkingHourEnd,
|
||||
double HourHeight,
|
||||
DayHeaderDisplayType DayHeaderDisplayType,
|
||||
CultureInfo CultureInfo)
|
||||
{
|
||||
public int WorkWeekDayCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var startOffset = GetWeekOffset(WorkWeekStart);
|
||||
var endOffset = GetWeekOffset(WorkWeekEnd);
|
||||
|
||||
if (endOffset < startOffset)
|
||||
{
|
||||
endOffset += 7;
|
||||
}
|
||||
|
||||
return (endOffset - startOffset) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetWeekOffset(DayOfWeek dayOfWeek)
|
||||
=> ((int)dayOfWeek - (int)FirstDayOfWeek + 7) % 7;
|
||||
|
||||
public TimeSpan? GetTimeSpan(string selectedTime)
|
||||
{
|
||||
// Regardless of the format, we need to parse the time to a TimeSpan.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public interface ICalendarRangeTextFormatter
|
||||
{
|
||||
string Format(VisibleDateRange range, IDateContextProvider dateContextProvider);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public interface IDateContextProvider
|
||||
{
|
||||
CultureInfo Culture { get; }
|
||||
TimeZoneInfo TimeZone { get; }
|
||||
DateOnly GetToday();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public sealed class SystemDateContextProvider : IDateContextProvider
|
||||
{
|
||||
public CultureInfo Culture => CultureInfo.CurrentCulture;
|
||||
|
||||
public TimeZoneInfo TimeZone => TimeZoneInfo.Local;
|
||||
|
||||
public DateOnly GetToday()
|
||||
{
|
||||
var localNow = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, TimeZone);
|
||||
return DateOnly.FromDateTime(localNow.DateTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public sealed record VisibleDateRange(
|
||||
CalendarDisplayType DisplayType,
|
||||
DateOnly AnchorDate,
|
||||
DateOnly StartDate,
|
||||
DateOnly EndDate,
|
||||
DateOnly PrimaryDate,
|
||||
int DayCount,
|
||||
bool ContainsToday,
|
||||
bool SpansSingleMonth,
|
||||
IReadOnlyList<DateOnly> Dates)
|
||||
{
|
||||
public DateRange ToDateRangeExclusive()
|
||||
=> new(StartDate.ToDateTime(TimeOnly.MinValue), EndDate.AddDays(1).ToDateTime(TimeOnly.MinValue));
|
||||
|
||||
public ITimePeriod ToTimePeriod()
|
||||
=> new TimeRange(StartDate.ToDateTime(TimeOnly.MinValue), EndDate.AddDays(1).ToDateTime(TimeOnly.MinValue));
|
||||
|
||||
public bool Contains(DateOnly date)
|
||||
=> date >= StartDate && date <= EndDate;
|
||||
|
||||
public bool Contains(DateTime date)
|
||||
=> Contains(DateOnly.FromDateTime(date));
|
||||
|
||||
public static VisibleDateRange FromDateRange(CalendarDisplayType displayType, DateRange dateRange, DateOnly anchorDate, DateOnly today)
|
||||
{
|
||||
var startDate = DateOnly.FromDateTime(dateRange.StartDate);
|
||||
var endDate = DateOnly.FromDateTime(dateRange.EndDate.AddDays(-1));
|
||||
var dayCount = endDate.DayNumber - startDate.DayNumber + 1;
|
||||
var dates = Enumerable.Range(0, dayCount)
|
||||
.Select(offset => startDate.AddDays(offset))
|
||||
.ToArray();
|
||||
|
||||
return new VisibleDateRange(
|
||||
displayType,
|
||||
anchorDate,
|
||||
startDate,
|
||||
endDate,
|
||||
anchorDate,
|
||||
dayCount,
|
||||
today >= startDate && today <= endDate,
|
||||
startDate.Year == endDate.Year && startDate.Month == endDate.Month,
|
||||
dates);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public static class SettingsNavigationInfoProvider
|
||||
Translator.WinoAccount_SettingsSection_Title,
|
||||
Translator.WinoAccount_SettingsSection_Description,
|
||||
"\uE77B",
|
||||
searchKeywords: Translator.SettingsSearch_WinoAccount_Keywords),
|
||||
searchKeywords: string.Empty),
|
||||
new(null, Translator.SettingsOptions_GeneralSection, string.Empty, "\uE713", isSeparator: true),
|
||||
new(WinoPage.AppPreferencesPage,
|
||||
Translator.SettingsAppPreferences_Title,
|
||||
|
||||
Reference in New Issue
Block a user