Added TimeZone Functionality

This commit is contained in:
t.ruspekhofer 2022-03-10 22:34:47 +01:00
parent c298f4d20e
commit c215ed058f
9 changed files with 129 additions and 48 deletions

View file

@ -40,6 +40,7 @@ namespace Lieb.Data
GuildWars2Account bloodseeker = new GuildWars2Account() { AccountName = "Bloodseeker.2043" }; GuildWars2Account bloodseeker = new GuildWars2Account() { AccountName = "Bloodseeker.2043" };
var users = new LiebUser[] var users = new LiebUser[]
{ {
//new LiebUser{DiscordUserId=0, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} },
new LiebUser{DiscordUserId=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} }, new LiebUser{DiscordUserId=194863625477816321, Name="Sarah", Birthday=DateTime.Parse("1992-01-15"), GuildWars2Accounts = new List<GuildWars2Account>(){ linaith, sarah} },
#if DEBUG #if DEBUG
new LiebUser{DiscordUserId=1, Name="Lisa", GuildWars2Accounts = new List<GuildWars2Account>(){ hierpiepts}}, new LiebUser{DiscordUserId=1, Name="Lisa", GuildWars2Accounts = new List<GuildWars2Account>(){ hierpiepts}},
@ -96,9 +97,9 @@ namespace Lieb.Data
Guild = "LIEB", Guild = "LIEB",
Organizer = "Sarah", Organizer = "Sarah",
RaidType = RaidType.RandomWithBoons, RaidType = RaidType.RandomWithBoons,
Date = DateTime.Now.Date, StartTimeUTC = DateTime.UtcNow,
StartTime = DateTime.Now, EndTimeUTC = DateTime.UtcNow.AddHours(2),
EndTime = DateTime.Now.AddHours(2), FreeForAllTimeUTC = DateTime.UtcNow.AddHours(-2),
VoiceChat = "ts.lieb.games", VoiceChat = "ts.lieb.games",
Roles = new [] { ele, scourge} Roles = new [] { ele, scourge}
}; };
@ -115,7 +116,7 @@ namespace Lieb.Data
context.RaidSignUps.AddRange(signUps); context.RaidSignUps.AddRange(signUps);
context.SaveChanges(); context.SaveChanges();
GuildWars2Build healTempest = new GuildWars2Build() { BuildName = "HealTempest", Class = GuildWars2Class.Elementalist, EliteSpecialization = EliteSpecialization.Tempest, Heal = 5, Might = 10 }; GuildWars2Build healTempest = new GuildWars2Build() { BuildName = "HealTempest", Class = GuildWars2Class.Elementalist, EliteSpecialization = EliteSpecialization.Tempest, Heal = 5, Might = 5 };
GuildWars2Build condiScourge = new GuildWars2Build() { BuildName = "CondiScourge", Class = GuildWars2Class.Necromancer, EliteSpecialization = EliteSpecialization.Scourge }; GuildWars2Build condiScourge = new GuildWars2Build() { BuildName = "CondiScourge", Class = GuildWars2Class.Necromancer, EliteSpecialization = EliteSpecialization.Scourge };
GuildWars2Build quickBrand = new GuildWars2Build() { BuildName = "QuickBrand", Class = GuildWars2Class.Guard, EliteSpecialization = EliteSpecialization.Firebrand, Heal = 5, Quickness = 5 }; GuildWars2Build quickBrand = new GuildWars2Build() { BuildName = "QuickBrand", Class = GuildWars2Class.Guard, EliteSpecialization = EliteSpecialization.Firebrand, Heal = 5, Quickness = 5 };
GuildWars2Build alacregate = new GuildWars2Build() { BuildName = "Alacregate", Class = GuildWars2Class.Revenant, EliteSpecialization = EliteSpecialization.Renegade, Alacrity = 5 }; GuildWars2Build alacregate = new GuildWars2Build() { BuildName = "Alacregate", Class = GuildWars2Class.Revenant, EliteSpecialization = EliteSpecialization.Renegade, Alacrity = 5 };

View file

@ -65,14 +65,16 @@ namespace Lieb.Data
.FirstOrDefaultAsync(r => r.RaidId == raid.RaidId); .FirstOrDefaultAsync(r => r.RaidId == raid.RaidId);
raidToChange.Title = raid.Title; raidToChange.Title = raid.Title;
raidToChange.Description = raid.Description; raidToChange.Description = raid.Description;
raidToChange.Date = raid.Date; raidToChange.StartTimeUTC = raid.StartTimeUTC;
raidToChange.StartTime = raid.StartTime; raidToChange.EndTimeUTC = raid.EndTimeUTC;
raidToChange.EndTime = raid.EndTime; raidToChange.TimeZone = raid.TimeZone;
raidToChange.Organizer = raid.Organizer; raidToChange.Organizer = raid.Organizer;
raidToChange.Guild = raid.Guild; raidToChange.Guild = raid.Guild;
raidToChange.VoiceChat = raid.VoiceChat; raidToChange.VoiceChat = raid.VoiceChat;
raidToChange.RaidType = raid.RaidType; raidToChange.RaidType = raid.RaidType;
raidToChange.Frequency = raid.Frequency; raidToChange.Frequency = raid.Frequency;
raidToChange.RequiredRole = raid.RequiredRole;
raidToChange.FreeForAllTimeUTC = raid.FreeForAllTimeUTC;
raidToChange.DiscordMessageId = raid.DiscordMessageId; raidToChange.DiscordMessageId = raid.DiscordMessageId;
raidToChange.DiscordChannelId = raid.DiscordChannelId; raidToChange.DiscordChannelId = raid.DiscordChannelId;
raidToChange.DiscordGuildId = raid.DiscordGuildId; raidToChange.DiscordGuildId = raid.DiscordGuildId;
@ -315,13 +317,6 @@ namespace Lieb.Data
return false; return false;
} }
DateTime freeForAllTime = raid.FreeForAllDate.Date + raid.FreeForAllTime.TimeOfDay;
if (!string.IsNullOrEmpty(raid.RequiredRole) && !user.RoleAssignments.Where(a => a.LiebRole.RoleName == raid.RequiredRole).Any() || freeForAllTime > DateTimeOffset.Now)
{
errorMessage = $"The raid is still locked for {raid.RequiredRole}.";
return false;
}
if (user.GuildWars2Accounts.Count == 0) if (user.GuildWars2Accounts.Count == 0)
{ {
errorMessage = "No Guild Wars 2 account found."; errorMessage = "No Guild Wars 2 account found.";
@ -334,6 +329,12 @@ namespace Lieb.Data
return false; return false;
} }
if (!string.IsNullOrEmpty(raid.RequiredRole) && !user.RoleAssignments.Where(a => a.LiebRole.RoleName == raid.RequiredRole).Any() || raid.FreeForAllTimeUTC.UtcDateTime > DateTimeOffset.UtcNow)
{
errorMessage = $"The raid is still locked for {raid.RequiredRole}.";
return false;
}
return true; return true;
} }
} }

View file

@ -0,0 +1,44 @@
using Microsoft.JSInterop;
namespace Lieb.Data
{
public class TimeZoneService
{
private readonly IJSRuntime _jsRuntime;
private TimeSpan? _userOffset;
private int _offsetInMinutes;
public TimeZoneService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async ValueTask<DateTimeOffset> GetLocalDateTime(DateTimeOffset dateTime)
{
if (_userOffset == null)
{
_offsetInMinutes = await _jsRuntime.InvokeAsync<int>("GetTimezoneValue");
_userOffset = TimeSpan.FromMinutes(-_offsetInMinutes);
}
return dateTime.ToOffset(_userOffset.Value);
}
public async ValueTask<DateTimeOffset> GetUTCDateTime(DateTimeOffset dateTime)
{
if (_userOffset == null)
{
_offsetInMinutes = await _jsRuntime.InvokeAsync<int>("GetTimezoneValue");
_userOffset = TimeSpan.FromMinutes(-_offsetInMinutes);
}
return new DateTimeOffset(dateTime.DateTime.AddMinutes(_offsetInMinutes), new TimeSpan(0));
}
public async ValueTask<string> GetUserTimeZone()
{
return await _jsRuntime.InvokeAsync<string>("GetTimezone");
}
}
}

View file

@ -23,13 +23,12 @@ namespace Lieb.Models.GuildWars2.Raid
public string Description { get; set; } = String.Empty; public string Description { get; set; } = String.Empty;
[Required] [Required]
public DateTime Date { get; set; } = DateTime.Now; public DateTimeOffset StartTimeUTC { get; set; } = DateTime.Now;
[Required] [Required]
public DateTimeOffset StartTime { get; set; } public DateTimeOffset EndTimeUTC { get; set; }
[Required] public string TimeZone { get; set; } = String.Empty;
public DateTimeOffset EndTime { get; set; }
[Required] [Required]
[StringLength(50, ErrorMessage = "Organizer too long (50 character limit).")] [StringLength(50, ErrorMessage = "Organizer too long (50 character limit).")]
@ -52,9 +51,7 @@ namespace Lieb.Models.GuildWars2.Raid
public string RequiredRole { get; set; } = String.Empty; public string RequiredRole { get; set; } = String.Empty;
public DateTime FreeForAllDate { get; set; } = DateTime.Now; public DateTimeOffset FreeForAllTimeUTC { get; set; }
public DateTimeOffset FreeForAllTime { get; set; }
//role name, number of spots //role name, number of spots
public ICollection<PlannedRaidRole> Roles { get; set; } = new HashSet<PlannedRaidRole>(); public ICollection<PlannedRaidRole> Roles { get; set; } = new HashSet<PlannedRaidRole>();

View file

@ -4,6 +4,7 @@
@using Lieb.Models.GuildWars2.Raid @using Lieb.Models.GuildWars2.Raid
@inject UserService UserService @inject UserService UserService
@inject RaidService RaidService @inject RaidService RaidService
@inject TimeZoneService TimeZoneService
@inject RaidRandomizerService RaidRandomizerService @inject RaidRandomizerService RaidRandomizerService
<body> <body>
@ -17,11 +18,11 @@
<div > <div >
<div class="times"> <div class="times">
<h5>Date</h5> <h5>Date</h5>
<p>@_raid.Date.ToLongDateString()</p> <p>@_startTime.DateTime.ToLongDateString()</p>
</div> </div>
<div class="times"> <div class="times">
<h5>Time</h5> <h5>Time</h5>
<p>from: @_raid.StartTime.LocalDateTime.ToShortTimeString() to: @_raid.EndTime.LocalDateTime.ToShortTimeString()</p> <p>from: @_startTime.LocalDateTime.ToShortTimeString() to: @_endTime.LocalDateTime.ToShortTimeString()</p>
</div> </div>
</div> </div>
@ -72,7 +73,6 @@
</body> </body>
@code { @code {
[Parameter] [Parameter]
public Raid _raid { get; set; } public Raid _raid { get; set; }
@ -83,9 +83,17 @@
string _errorMessage; string _errorMessage;
private DateTimeOffset _startTime;
private DateTimeOffset _endTime;
private DateTimeOffset _freeForAllTime;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_isRaidSignUpAllowed = _user != null && RaidService.IsRaidSignUpAllowed(_user.LiebUserId, _raid.RaidId, out string _errorMessage); _isRaidSignUpAllowed = _user != null && RaidService.IsRaidSignUpAllowed(_user.LiebUserId, _raid.RaidId, out string _errorMessage);
_startTime = await TimeZoneService.GetLocalDateTime(_raid.StartTimeUTC);
_endTime = await TimeZoneService.GetLocalDateTime(_raid.EndTimeUTC);
_freeForAllTime = await TimeZoneService.GetLocalDateTime(_raid.FreeForAllTimeUTC);
} }
async Task SignUpClicked(PlannedRaidRole role, LiebUser liebUser, bool isSignedUp, SignUpType signUpType) async Task SignUpClicked(PlannedRaidRole role, LiebUser liebUser, bool isSignedUp, SignUpType signUpType)

View file

@ -4,6 +4,7 @@
@using Lieb.Models.GuildWars2.Raid @using Lieb.Models.GuildWars2.Raid
@using System.ComponentModel.DataAnnotations @using System.ComponentModel.DataAnnotations
@inject RaidService RaidService @inject RaidService RaidService
@inject TimeZoneService TimeZoneService
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
@ -13,7 +14,6 @@
<AuthorizeView Policy="@Constants.Roles.RaidLead" Context="authorizationContext"> <AuthorizeView Policy="@Constants.Roles.RaidLead" Context="authorizationContext">
<EditForm Model="@_raid" OnValidSubmit="@HandleValidSubmit"> <EditForm Model="@_raid" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
<ValidationSummary />
@{ @{
bool _isEdit = _raid.RaidId != 0; bool _isEdit = _raid.RaidId != 0;
} }
@ -44,20 +44,20 @@
<p> <p>
<label> <label>
Date: Date:
<InputDate @bind-Value="_raid.Date" /> <InputDate @bind-Value="_raidDate" />
</label> </label>
</p> </p>
<p> <p>
<label> <label>
Start Time: Start Time:
<input type="time" @bind="@_raid.StartTime" /> <input type="time" @bind="_startTime" />
</label> </label>
</p> </p>
<p> <p>
<label> <label>
End Time: End Time:
<input type="time" @bind="@_raid.EndTime" /> <input type="time" @bind="_endTime" />
</label> </label>
</p> </p>
@ -110,6 +110,8 @@
</p> </p>
} }
<ValidationSummary />
<label class="validation-message" >@_errosMessage</label>
<button type="submit">Submit</button> <button type="submit">Submit</button>
</EditForm> </EditForm>
@ -124,12 +126,26 @@
public Raid _raid; public Raid _raid;
private string _errosMessage = string.Empty;
private DateTimeOffset _raidDate = DateTime.Now.Date;
private DateTimeOffset _startTime;
private DateTimeOffset _endTime;
private DateTimeOffset _freeForAllDate = DateTime.Now.Date;
private DateTimeOffset _freeForAllTime;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
if(!string.IsNullOrEmpty(raidId) && int.TryParse(raidId, out int parsedId)) if(!string.IsNullOrEmpty(raidId) && int.TryParse(raidId, out int parsedId))
{ {
_raid = RaidService.GetRaid(parsedId); _raid = RaidService.GetRaid(parsedId);
_startTime = await TimeZoneService.GetLocalDateTime(_raid.StartTimeUTC);
_endTime = await TimeZoneService.GetLocalDateTime(_raid.EndTimeUTC);
_raidDate = _startTime.Date;
_freeForAllTime = await TimeZoneService.GetLocalDateTime(_raid.FreeForAllTimeUTC);
_freeForAllDate = _freeForAllTime.Date;
} }
else else
{ {
@ -160,8 +176,9 @@
private async Task HandleValidSubmit() private async Task HandleValidSubmit()
{ {
if(_raid.RaidType != RaidType.Planned && _raid.Roles.Count == 0) if(_raid.RaidType != RaidType.Planned)
{ {
_raid.Roles.Clear();
_raid.Roles.Add(new PlannedRaidRole() _raid.Roles.Add(new PlannedRaidRole()
{ {
Spots = 10, Spots = 10,
@ -170,6 +187,25 @@
}); });
} }
if(_raid.Roles.Count == 0)
{
_errosMessage = "Roles are needed for a raid.";
return;
}
_raid.TimeZone = await TimeZoneService.GetUserTimeZone();
_raid.StartTimeUTC = await TimeZoneService.GetUTCDateTime(_raidDate.Date + _startTime.TimeOfDay);
if(_startTime.TimeOfDay > _endTime.TimeOfDay)
{
_raid.EndTimeUTC = await TimeZoneService.GetUTCDateTime(_raidDate.Date + _endTime.TimeOfDay);
}
else
{
_raid.EndTimeUTC = await TimeZoneService.GetUTCDateTime(_raidDate.Date.AddDays(1) + _endTime.TimeOfDay);
}
_raid.FreeForAllTimeUTC = await TimeZoneService.GetUTCDateTime(_freeForAllDate.Date + _freeForAllTime.TimeOfDay);
await RaidService.AddOrEditRaid(_raid); await RaidService.AddOrEditRaid(_raid);
NavigationManager.NavigateTo("raidoverview"); NavigationManager.NavigateTo("raidoverview");
} }

View file

@ -7,3 +7,14 @@
@(await Html.RenderComponentAsync<App>(RenderMode.Server)) @(await Html.RenderComponentAsync<App>(RenderMode.Server))
<!--<component type="typeof(App)" render-mode="ServerPrerendered" />--> <!--<component type="typeof(App)" render-mode="ServerPrerendered" />-->
<script>
function GetTimezoneValue() {
// Returns the time difference in minutes between UTC time and local time.
return new Date().getTimezoneOffset();
}
</script>
<script>
function GetTimezone() {
return new Intl.DateTimeFormat().resolvedOptions().timeZone;
}
</script>

View file

@ -1,18 +0,0 @@
<environment names="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.17.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha384-rZfj/ogBloos6wzLGpPkkOr/gpkBNLZ6b6yLy4o+ok+t/SAKlL5mvXLr0OXNi1Hp">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.9/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha384-ifv0TYDWxBHzvAk2Z0n8R434FL1Rlv/Av18DXE43N/1rvHyOG4izKst0f2iSLdds">
</script>
</environment>

View file

@ -25,6 +25,7 @@ builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<GuildWars2AccountService>(); builder.Services.AddScoped<GuildWars2AccountService>();
builder.Services.AddScoped<GuildWars2BuildService>(); builder.Services.AddScoped<GuildWars2BuildService>();
builder.Services.AddScoped<RaidRandomizerService>(); builder.Services.AddScoped<RaidRandomizerService>();
builder.Services.AddScoped<TimeZoneService>();
builder.Services.AddAuthentication(opt => builder.Services.AddAuthentication(opt =>