Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//This file is part of AudioCuesheetEditor.

//AudioCuesheetEditor is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//AudioCuesheetEditor is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with Foobar. If not, see
//<http: //www.gnu.org/licenses />.
using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.AudioCuesheet;
using AudioCuesheetEditor.Model.Options;
using AudioCuesheetEditor.Services.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace AudioCuesheetEditor.Tests.Services.UI
{
[TestClass()]
public class ApplicationOptionsTimeSpanParserTests
{
[TestMethod]
public async Task TimespanTextChanged_ValidInput_SetsPropertyCorrectly()
{
// Arrange
var options = new ApplicationOptions()
{
TimeSpanFormat = new()
{
Scheme = "Minutes:Seconds"
}
};
var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>();
mockOptionsProvider
.Setup(p => p.GetOptions<ApplicationOptions>())
.ReturnsAsync(options);
var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object);
await Task.Delay(50);
var track = new Track();
// Act
await parser.TimespanTextChanged(track, x => x.Begin, "92:12");
// Assert
Assert.AreEqual(new TimeSpan(1, 32, 12), track.Begin);
}

[TestMethod]
public async Task TimespanTextChanged_InvalidInput_SetsNull()
{
var options = new ApplicationOptions()
{
TimeSpanFormat = new()
{
Scheme = "Minutes:Seconds"
}
};
var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>();
mockOptionsProvider
.Setup(p => p.GetOptions<ApplicationOptions>())
.ReturnsAsync(options);
var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object);
await Task.Delay(50);
var track = new Track();
// Act
await parser.TimespanTextChanged(track, x => x.End, "not a time");
// Assert
Assert.IsNull(track.End);
}

[TestMethod()]
public async Task GetTimespanFormatted_ValidFormat_ReturnsCorrectString()
{
// Arrange
var options = new ApplicationOptions()
{
DisplayTimeSpanFormat = @"hh\:mm\:ss"
};
var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>();
mockOptionsProvider
.Setup(p => p.GetOptions<ApplicationOptions>())
.ReturnsAsync(options);
var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object);
await Task.Delay(50);
// Act
var result = parser.GetTimespanFormatted(new TimeSpan(0, 1, 30, 27, 200, 103));
// Assert
Assert.AreEqual("01:30:27", result);
}

[TestMethod()]
public async Task GetTimespanFormatted_InvalidFormat_FallbackToDefault()
{
// Arrange
var options = new ApplicationOptions()
{
DisplayTimeSpanFormat = "INVALID_FORMAT"
};
var mockOptionsProvider = new Mock<ILocalStorageOptionsProvider>();
mockOptionsProvider
.Setup(p => p.GetOptions<ApplicationOptions>())
.ReturnsAsync(options);
var parser = new ApplicationOptionsTimeSpanParser(mockOptionsProvider.Object);
await Task.Delay(50);
// Act
var result = parser.GetTimespanFormatted(new TimeSpan(0, 1, 30, 27, 200, 103));
// Assert
Assert.AreEqual("01:30:27.2001030", result);
}
}
}
1 change: 1 addition & 0 deletions AudioCuesheetEditor/Model/Options/ApplicationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public String? ProjectFilename
public TimeSpanFormat ImportTimeSpanFormat { get; set; } = new();
public uint RecordCountdownTimer { get; set; } = 5;
public Boolean FixedTracksTableHeader { get; set; } = false;
public String? DisplayTimeSpanFormat { get; set; }

public override ValidationResult Validate(string property)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ public async Task TimespanTextChanged<T, TProperty>(T entity, Expression<Func<T,
}
}

public string? GetTimespanFormatted(TimeSpan? timeSpan)
{
string? formatted = null;
if (timeSpan.HasValue)
{
try
{
formatted = timeSpan.Value.ToString(applicationOptions?.DisplayTimeSpanFormat);
}
catch (FormatException)
{
formatted = timeSpan.Value.ToString();
}
}
return formatted;
}

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
Expand Down
12 changes: 6 additions & 6 deletions AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ along with Foobar. If not, see
</MudButtonGroup>

<MudDataGrid Items="Cuesheet?.Sections" ReadOnly="false" Bordered EditTrigger="DataGridEditTrigger.OnRowClick" EditMode="DataGridEditMode.Cell" ColumnResizeMode="ResizeMode.Column"
MultiSelection SelectOnRowClick="false" @bind-SelectedItems="selectedSections">
MultiSelection SelectOnRowClick="false" @bind-SelectedItems="selectedSections">
<Columns>
<SelectColumn />
<PropertyColumn Property="x => x.Begin" Title="@_localizer["Begin"]">
<EditTemplate>
<MudTextField Value="@context.Item.Begin.ToString()" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<CuesheetSection, TimeSpan?>(context.Item, x => x.Begin, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(CuesheetSection.Begin)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(CuesheetSection.Begin))" />
<MudTextField Value="@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.Begin)" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<CuesheetSection, TimeSpan?>(context.Item, x => x.Begin, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(CuesheetSection.Begin)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(CuesheetSection.Begin))" />
</EditTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.End" Title="@_localizer["End"]">
<EditTemplate>
<MudTextField Value="@context.Item.End.ToString()" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<CuesheetSection, TimeSpan?>(context.Item, x => x.End, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(CuesheetSection.End)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(CuesheetSection.End))" />
<MudTextField Value="@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.End)" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<CuesheetSection, TimeSpan?>(context.Item, x => x.End, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(CuesheetSection.End)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(CuesheetSection.End))" />
</EditTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.Artist" Title="@_localizer["Cuesheet artist"]">
Expand All @@ -73,7 +73,7 @@ MultiSelection SelectOnRowClick="false" @bind-SelectedItems="selectedSections">
<PropertyColumn Property="x => x.AudiofileName" Title="@_localizer["Audio file"]">
<EditTemplate>
<FileInput Label="@_localizer["Audiofile"]" FileName="@context.Item.AudiofileName" OnFileSelected="(file) => AudiofileSelected(context.Item, file)" Error="@GetValidationErrorMessage(context.Item, nameof(CuesheetSection.AudiofileName))" Filter="@String.Join(",", Audiofile.AudioCodecs.Select(x => x.MimeType))"
DisplayMenu="false" />
DisplayMenu="false" />
</EditTemplate>
</PropertyColumn>
</Columns>
Expand Down
9 changes: 9 additions & 0 deletions AudioCuesheetEditor/Shared/Dialogs/OptionsDialog.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
<data name="Days" xml:space="preserve">
<value>Tage</value>
</data>
<data name="Display" xml:space="preserve">
<value>Anzeige</value>
</data>
<data name="Hours" xml:space="preserve">
<value>Stunden</value>
</data>
Expand All @@ -141,7 +144,13 @@
<data name="Seconds" xml:space="preserve">
<value>Sekunden</value>
</data>
<data name="Time display format" xml:space="preserve">
<value>Anzeigeformat Zeit</value>
</data>
<data name="Time input format" xml:space="preserve">
<value>Eingabeformat Zeit</value>
</data>
<data name="Uses .NET format, check help for more information" xml:space="preserve">
<value>Benutzt .NET format, bitte prüfen sie die Hilfe für mehr Informationen</value>
</data>
</root>
17 changes: 13 additions & 4 deletions AudioCuesheetEditor/Shared/Dialogs/OptionsDialog.razor
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,23 @@ along with Foobar. If not, see

<MudDialog>
<DialogContent>
<MudText Typo="Typo.h6"><b>@_localizer["Input"]</b></MudText>
<MudSwitch T="Boolean?" Value="ApplicationOptions?.LinkTracks" ValueChanged="(newValue) => LocalStorageOptionsProvider.SaveOptionsValue<ApplicationOptions>(x => x.LinkTracks, newValue)" Label="@_localizer["Automatically link tracks"]" Color="Color.Primary" />
<MudText Typo="Typo.subtitle1"><b>@_localizer["Input"]</b></MudText>
<MudSwitch T="Boolean?" Value="ApplicationOptions?.LinkTracks" ValueChanged="(newValue) => LocalStorageOptionsProvider.SaveOptionsValue<ApplicationOptions>(x => x.LinkTracks, newValue)"
Label="@_localizer["Automatically link tracks"]" Color="Color.Primary" />
<MudStack Row AlignItems="AlignItems.Baseline">
<MudTextField T="string" @ref="timeInputFormatTextField" Label="@_localizer["Time input format"]" Text="@ApplicationOptions?.TimeSpanFormat?.Scheme" TextChanged="TimeInputFormatChangedAsync"
Clearable Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))) == false"
ErrorText="@GetValidationErrorMessage(ApplicationOptions?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))" Variant="Variant.Outlined" />
Clearable Error="String.IsNullOrEmpty(GetValidationErrorMessage(ApplicationOptions?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))) == false"
ErrorText="@GetValidationErrorMessage(ApplicationOptions?.TimeSpanFormat, nameof(TimeSpanFormat.Scheme))" Variant="Variant.Outlined" />
<MudMenu Label="@_localizer["Add placeholder"]" Color="Color.Primary" Variant="Variant.Filled" EndIcon="@Icons.Material.Outlined.KeyboardArrowDown" Class="mt-1" Style="height: 56px;">
@foreach(var scheme in TimeSpanFormat.AvailableTimespanScheme)
{
<MudMenuItem OnClick="() => AppendPlaceholderToTimeInputFormatTextField(scheme)">@_localizer[scheme]</MudMenuItem>
}
</MudMenu>
</MudStack>
<MudText Typo="Typo.subtitle1"><b>@_localizer["Display"]</b></MudText>
<MudTextField T="string" Label="@_localizer["Time display format"]" Text="@ApplicationOptions?.DisplayTimeSpanFormat" TextChanged="DisplayTimeSpanFormatChangedAsync"
Clearable Variant="Variant.Outlined" HelperText="@_localizer["Uses .NET format, check help for more information"]" />
</DialogContent>
</MudDialog>

Expand All @@ -59,6 +63,11 @@ along with Foobar. If not, see
await LocalStorageOptionsProvider.SaveOptionsValue<ApplicationOptions>(x => x.TimeSpanFormat!, timeSpanFormat);
}

async Task DisplayTimeSpanFormatChangedAsync(string newValue)
{
await LocalStorageOptionsProvider.SaveOptionsValue<ApplicationOptions>(x => x.DisplayTimeSpanFormat, newValue);
}

void AppendPlaceholderToTimeInputFormatTextField(string placeholder)
{
timeInputFormatTextField?.SetText($"{timeInputFormatTextField.Text}{placeholder}");
Expand Down
9 changes: 9 additions & 0 deletions AudioCuesheetEditor/Shared/Dialogs/OptionsDialog.resx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
<data name="Days" xml:space="preserve">
<value>Days</value>
</data>
<data name="Display" xml:space="preserve">
<value>Display</value>
</data>
<data name="Hours" xml:space="preserve">
<value>Hours</value>
</data>
Expand All @@ -141,7 +144,13 @@
<data name="Seconds" xml:space="preserve">
<value>Seconds</value>
</data>
<data name="Time display format" xml:space="preserve">
<value>Time display format</value>
</data>
<data name="Time input format" xml:space="preserve">
<value>Time input format</value>
</data>
<data name="Uses .NET format, check help for more information" xml:space="preserve">
<value>Uses .NET format, check help for more information</value>
</data>
</root>
15 changes: 12 additions & 3 deletions AudioCuesheetEditor/Shared/TrackList/TrackList.razor
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,29 @@ along with Foobar. If not, see
</EditTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.Begin" Title="@_localizer["Begin"]" Editable="CurrentViewMode != ViewMode.RecordView" HeaderStyle="width: 1px;">
<CellTemplate>
@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.Begin)
</CellTemplate>
<EditTemplate>
<MudTextField Value="@context.Item.Begin.ToString()" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.Begin, value)"
<MudTextField Value="@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.Begin)" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.Begin, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(Track.Begin)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(Track.Begin))" />
</EditTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.End" Title="@_localizer["End"]" Editable="CurrentViewMode != ViewMode.RecordView" HeaderStyle="width: 1px;">
<CellTemplate>
@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.End)
</CellTemplate>
<EditTemplate>
<MudTextField Value="@context.Item.End.ToString()" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.End, value)"
<MudTextField Value="@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.End)" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.End, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(Track.End)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(Track.End))" />
</EditTemplate>
</PropertyColumn>
<PropertyColumn Property="x => x.Length" Title="@_localizer["Length"]" Editable="CurrentViewMode != ViewMode.RecordView" HeaderStyle="width: 1px;">
<CellTemplate>
@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.Length)
</CellTemplate>
<EditTemplate>
<MudTextField Value="@context.Item.Length.ToString()" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.Length, value)"
<MudTextField Value="@_applicationOptionsTimeSpanParser.GetTimespanFormatted(context.Item.Length)" ValueChanged="(string value) => _applicationOptionsTimeSpanParser.TimespanTextChanged<Track, TimeSpan?>(context.Item, x => x.Length, value)"
Error="!String.IsNullOrEmpty(GetValidationErrorMessage(context.Item, nameof(Track.Length)))" ErrorText="@GetValidationErrorMessage(context.Item, nameof(Track.Length))" />
</EditTemplate>
</PropertyColumn>
Expand Down