Skip to content

Commit dc75d63

Browse files
committed
fix: variant support
1 parent c94b44f commit dc75d63

File tree

15 files changed

+256
-55
lines changed

15 files changed

+256
-55
lines changed

src/jcdcdev.Umbraco.ReadingTime.sln

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
77
ProjectSection(SolutionItems) = preProject
88
..\LICENSE = ..\LICENSE
99
..\.github\README.md = ..\.github\README.md
10-
..\umbraco-marketplace.json = ..\umbraco-marketplace.json
1110
EndProjectSection
1211
EndProject
1312
Global
@@ -20,10 +19,6 @@ Global
2019
{53BFA9E1-2748-4FEE-B3CB-98172460795D}.Debug|Any CPU.Build.0 = Debug|Any CPU
2120
{53BFA9E1-2748-4FEE-B3CB-98172460795D}.Release|Any CPU.ActiveCfg = Release|Any CPU
2221
{53BFA9E1-2748-4FEE-B3CB-98172460795D}.Release|Any CPU.Build.0 = Release|Any CPU
23-
{CEDC8D8C-9CD4-4708-8F79-D1AA83A71735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24-
{CEDC8D8C-9CD4-4708-8F79-D1AA83A71735}.Debug|Any CPU.Build.0 = Debug|Any CPU
25-
{CEDC8D8C-9CD4-4708-8F79-D1AA83A71735}.Release|Any CPU.ActiveCfg = Release|Any CPU
26-
{CEDC8D8C-9CD4-4708-8F79-D1AA83A71735}.Release|Any CPU.Build.0 = Release|Any CPU
2722
{7912C4E8-5B1F-4171-A63A-214BBB103778}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
2823
{7912C4E8-5B1F-4171-A63A-214BBB103778}.Debug|Any CPU.Build.0 = Debug|Any CPU
2924
{7912C4E8-5B1F-4171-A63A-214BBB103778}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -48,12 +43,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github", "Github", "{5830A8
4843
ProjectSection(SolutionItems) = preProject
4944
..\.github\workflows\release.yml = ..\.github\workflows\release.yml
5045
..\.github\workflows\build.yml = ..\.github\workflows\build.yml
46+
..\.github\dependabot.yml = ..\.github\dependabot.yml
5147
EndProjectSection
5248
EndProject
5349
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSite.10", "TestSite.10\TestSite.10.csproj", "{53BFA9E1-2748-4FEE-B3CB-98172460795D}"
5450
EndProject
55-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSite.11", "TestSite.11\TestSite.11.csproj", "{CEDC8D8C-9CD4-4708-8F79-D1AA83A71735}"
56-
EndProject
5751
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSite.12", "TestSite.12\TestSite.12.csproj", "{7912C4E8-5B1F-4171-A63A-214BBB103778}"
5852
EndProject
5953
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSite.13", "TestSite.13\TestSite.13.csproj", "{DB758ACE-77D4-4ED6-997A-B71AA5747E5E}"

src/jcdcdev.Umbraco.ReadingTime/Core/Constants.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ public static class Constants
77

88
public static class Package
99
{
10-
public static string Name = "jcdcdev.Umbraco.ReadingTime";
10+
public const string Name = "jcdcdev.Umbraco.ReadingTime";
1111
}
12-
}
12+
13+
public static class LocalisationKeys
14+
{
15+
public const string SaveAndPublishToGenerateReadingTime = "saveAndPublishToGenerateReadingTime";
16+
public const string Area = "readingTime";
17+
public const string WordsPerMinutesDescription = "wordsPerMinutesDescription";
18+
public const string WordsPerMinutesName = "wordsPerMinutes";
19+
public const string MinUnit = "minUnit";
20+
public const string MaxUnit = "maxUnit";
21+
public const string? MinUnitDescription = "minUnitDescription";
22+
public const string? MaxUnitDescription = "maxUnitDescription";
23+
}
24+
25+
public static class Configuration
26+
{
27+
public const string Wpm = "wpm";
28+
public const string MinUnit = "minUnit";
29+
public const string MaxUnit = "maxUnit";
30+
}
31+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using jcdcdev.Umbraco.ReadingTime.Core.Models;
3+
4+
namespace jcdcdev.Umbraco.ReadingTime.Core.Extensions;
5+
6+
public static class ReadingTimeValueModelExtensions
7+
{
8+
public static bool IsValid([NotNullWhen(true)] this ReadingTimeValueModel? model) => model is not null && model.ReadingTime > TimeSpan.Zero;
9+
}

src/jcdcdev.Umbraco.ReadingTime/Core/Extensions/StringExtensions.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
using Umbraco.Extensions;
1+
using System.Globalization;
2+
using Humanizer;
3+
using Humanizer.Localisation;
4+
using jcdcdev.Umbraco.ReadingTime.Core.PropertyEditors;
5+
using Umbraco.Extensions;
26

37
namespace jcdcdev.Umbraco.ReadingTime.Core.Extensions;
48

@@ -15,4 +19,16 @@ public static TimeSpan GetReadingTime(this string input, int wpm = 200)
1519
public static int CountWords(this string input) => input.Split(new[] { ' ', '\t', '\n' }, StringSplitOptions.RemoveEmptyEntries).Length;
1620

1721
private static double CalculateReadingTime(int wordCount, int wordsPerMinute) => (double)wordCount / Math.Max(wordsPerMinute, 1);
18-
}
22+
23+
public static string DisplayTime(this TimeSpan? time, TimeUnit minUnit, TimeUnit maxUnit, string? culture = null)
24+
{
25+
var cc = CultureInfo.CurrentCulture;
26+
if (!string.IsNullOrWhiteSpace(culture))
27+
{
28+
cc = new CultureInfo(culture);
29+
}
30+
31+
var output = ReadingTimeConfiguration.GetReadingTime(time, minUnit);
32+
return output.Humanize(culture: cc, maxUnit: maxUnit, minUnit: minUnit);
33+
}
34+
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
namespace jcdcdev.Umbraco.ReadingTime.Core.Models;
1+
using jcdcdev.Umbraco.ReadingTime.Infrastructure.Persistence;
2+
using Umbraco.Extensions;
3+
4+
namespace jcdcdev.Umbraco.ReadingTime.Core.Models;
25

36
public class ContentReadingTimeModel
47
{
5-
public List<ReadingTimeValueModel?> Data { get; init; } = new();
8+
public List<ReadingTimeVariantModel?> Data { get; init; } = new();
69
public int Id { get; init; }
710
public Guid Key { get; init; }
8-
public ReadingTimeValueModel? Value(string? culture = null) => Data.FirstOrDefault(x => x?.Culture == culture);
9-
}
11+
public ReadingTimeVariantModel? Value(string? culture = null) => Data.FirstOrDefault(x => x?.Culture.InvariantEquals(culture) ?? false);
12+
}
Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
1-
namespace jcdcdev.Umbraco.ReadingTime.Core.Models;
1+
using System.Globalization;
2+
using Humanizer;
3+
using Humanizer.Localisation;
4+
using jcdcdev.Umbraco.ReadingTime.Core.Extensions;
5+
6+
namespace jcdcdev.Umbraco.ReadingTime.Core.Models;
27

38
public class ReadingTimeValueModel
49
{
5-
public string? Culture { get; set; }
6-
public TimeSpan? ReadingTime { get; set; }
7-
8-
public string DisplayTime()
10+
public ReadingTimeValueModel(TimeSpan? readingTime, TimeUnit minUnit, TimeUnit maxUnit, string? culture)
911
{
10-
var minutes = Math.Ceiling(ReadingTime.GetValueOrDefault().TotalMinutes);
11-
if (minutes < 1)
12-
{
13-
minutes = 1;
14-
}
12+
MinUnit = minUnit;
13+
MaxUnit = maxUnit;
14+
ReadingTime = readingTime;
15+
Culture = culture;
16+
}
1517

16-
return $"{minutes} {(minutes > 1 ? "minutes" : "minute")}";
18+
public string? Culture { get; }
19+
public TimeSpan? ReadingTime { get; }
20+
public TimeUnit MinUnit { get; }
21+
public TimeUnit MaxUnit { get; }
22+
23+
public string DisplayTime(TimeUnit? minUnit = null, TimeUnit? maxUnit = null)
24+
{
25+
var min = minUnit ?? MinUnit;
26+
var max = maxUnit ?? MaxUnit;
27+
return ReadingTime.DisplayTime(minUnit: min, maxUnit: max, Culture);
1728
}
18-
}
29+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace jcdcdev.Umbraco.ReadingTime.Core.Models;
4+
5+
public class ReadingTimeVariantModel
6+
{
7+
[DataMember(Name = "culture")] public string? Culture { get; set; }
8+
[DataMember(Name = "readingTime")] public TimeSpan? ReadingTime { get; set; }
9+
}
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
1-
using Umbraco.Cms.Core.PropertyEditors;
1+
using Humanizer.Localisation;
2+
using Umbraco.Cms.Core.PropertyEditors;
23

34
namespace jcdcdev.Umbraco.ReadingTime.Core.PropertyEditors;
45

56
public class ReadingTimeConfiguration
67
{
7-
[ConfigurationField("wpm", "Words per minute", "number", Description = "The average number of words per minute a person can read (studies suggest 150-250)")]
8+
public const int DefaultMinTimeUnit = (int)TimeUnit.Minute;
9+
public const int DefaultMaxTimeUnit = (int)TimeUnit.Minute;
10+
11+
[ConfigurationField(Constants.Configuration.Wpm, Constants.LocalisationKeys.WordsPerMinutesName, "number", Description = Constants.LocalisationKeys.WordsPerMinutesDescription)]
812
public int WordsPerMinute { get; set; }
9-
}
13+
14+
[ConfigurationField(Constants.Configuration.MinUnit, Constants.LocalisationKeys.MinUnit, "dropdown", Description = Constants.LocalisationKeys.MinUnitDescription)]
15+
public int MinUnit { get; set; } = DefaultMinTimeUnit;
16+
17+
[ConfigurationField(Constants.Configuration.MaxUnit, Constants.LocalisationKeys.MaxUnit, "dropdown", Description = Constants.LocalisationKeys.MaxUnitDescription)]
18+
public int MaxUnit { get; set; } = DefaultMaxTimeUnit;
19+
20+
public TimeUnit Min => (TimeUnit)MinUnit;
21+
public TimeUnit Max => (TimeUnit)MaxUnit;
22+
23+
public static TimeSpan GetReadingTime(TimeSpan? readingTime, TimeUnit min) => min switch
24+
{
25+
TimeUnit.Second => readingTime.GetValueOrDefault(TimeSpan.FromSeconds(1)),
26+
TimeUnit.Minute => readingTime.GetValueOrDefault(TimeSpan.FromMinutes(1)),
27+
TimeUnit.Hour => readingTime.GetValueOrDefault(TimeSpan.FromHours(1)),
28+
TimeUnit.Day => readingTime.GetValueOrDefault(TimeSpan.FromDays(1)),
29+
_ => readingTime.GetValueOrDefault(TimeSpan.FromMinutes(1))
30+
};
31+
}
Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
using jcdcdev.Umbraco.ReadingTime.Core.Models;
1+
using System.Runtime.Serialization;
2+
using Humanizer;
3+
using Humanizer.Localisation;
4+
using jcdcdev.Umbraco.ReadingTime.Core.Models;
5+
using Microsoft.Extensions.Logging;
26
using Microsoft.Extensions.Options;
7+
using Newtonsoft.Json;
38
using Umbraco.Cms.Core.IO;
49
using Umbraco.Cms.Core.PropertyEditors;
510
using Umbraco.Cms.Core.Services;
11+
using Umbraco.Extensions;
612

713
namespace jcdcdev.Umbraco.ReadingTime.Core.PropertyEditors;
814

@@ -12,18 +18,78 @@ public class ReadingTimeDataEditor : DataEditor
1218
private readonly IEditorConfigurationParser _editorConfigurationParser;
1319
private readonly IIOHelper _ioHelper;
1420
private readonly ReadingTimeOptions _options;
21+
private readonly ILocalizedTextService _localizedTextService;
22+
private ILogger _logger;
1523

1624
public ReadingTimeDataEditor(
1725
IDataValueEditorFactory dataValueEditorFactory,
1826
IOptions<ReadingTimeOptions> options,
1927
IIOHelper ioHelper,
2028
IEditorConfigurationParser editorConfigurationParser,
29+
ILocalizedTextService localizedTextService,
30+
ILogger<ReadingTimeDataEditor> logger,
2131
EditorType type = EditorType.PropertyValue) : base(dataValueEditorFactory, type)
2232
{
2333
_ioHelper = ioHelper;
2434
_editorConfigurationParser = editorConfigurationParser;
35+
_localizedTextService = localizedTextService;
36+
_logger = logger;
2537
_options = options.Value;
2638
}
2739

28-
protected override IConfigurationEditor CreateConfigurationEditor() => new ReadingTimeConfigurationEditor(_ioHelper, _editorConfigurationParser, _options);
29-
}
40+
protected override IConfigurationEditor CreateConfigurationEditor()
41+
{
42+
var config = new ReadingTimeConfigurationEditor(_ioHelper, _editorConfigurationParser, _options);
43+
44+
foreach (var field in config.Fields.Where(x => x.View == "dropdown"))
45+
{
46+
field.Config["prevalues"] = new List<DropDownPreValue>
47+
{
48+
new(GetName(TimeUnit.Second), (int)TimeUnit.Second),
49+
new(GetName(TimeUnit.Minute), (int)TimeUnit.Minute),
50+
new(GetName(TimeUnit.Hour), (int)TimeUnit.Hour),
51+
new(GetName(TimeUnit.Day), (int)TimeUnit.Day)
52+
};
53+
}
54+
55+
foreach (var field in config.Fields)
56+
{
57+
var descriptionKey = field.Description;
58+
var nameKey = field.Name;
59+
if (descriptionKey.IsNullOrWhiteSpace() || nameKey.IsNullOrWhiteSpace())
60+
{
61+
continue;
62+
}
63+
64+
field.Description = _localizedTextService.Localize(Constants.LocalisationKeys.Area, descriptionKey);
65+
field.Name = _localizedTextService.Localize(Constants.LocalisationKeys.Area, nameKey);
66+
}
67+
68+
return config;
69+
}
70+
71+
private string GetName(TimeUnit timeUnit)
72+
{
73+
try
74+
{
75+
return Resources.GetResource(ResourceKeys.TimeSpanHumanize.GetResourceKey(timeUnit, 2, true)).Remove(0, 4).ToFirstUpper();
76+
}
77+
catch (Exception ex)
78+
{
79+
_logger.LogError(ex, "Error getting name for time unit {TimeUnit}", timeUnit);
80+
return timeUnit.Humanize();
81+
}
82+
}
83+
}
84+
85+
public class DropDownPreValue
86+
{
87+
[JsonProperty("value")] public int Value;
88+
[JsonProperty("label")] public string Label;
89+
90+
public DropDownPreValue(string label, int value)
91+
{
92+
Label = label;
93+
Value = value;
94+
}
95+
}

src/jcdcdev.Umbraco.ReadingTime/Core/PropertyEditors/ReadingTimePropertyValueConverter.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using jcdcdev.Umbraco.ReadingTime.Core.Models;
1+
using Humanizer.Localisation;
2+
using jcdcdev.Umbraco.ReadingTime.Core.Models;
3+
using Microsoft.Extensions.Logging;
24
using Umbraco.Cms.Core.Models.PublishedContent;
35
using Umbraco.Cms.Core.PropertyEditors;
46

@@ -8,13 +10,16 @@ public class ReadingTimePropertyValueConverter : IPropertyValueConverter
810
{
911
private readonly IReadingTimeService _readingTimeService;
1012
private readonly IVariationContextAccessor _variationContextAccessor;
13+
private readonly ILogger _logger;
1114

1215
public ReadingTimePropertyValueConverter(
1316
IReadingTimeService readingTimeService,
14-
IVariationContextAccessor variationContextAccessor)
17+
IVariationContextAccessor variationContextAccessor,
18+
ILogger<ReadingTimePropertyValueConverter> logger)
1519
{
1620
_readingTimeService = readingTimeService;
1721
_variationContextAccessor = variationContextAccessor;
22+
_logger = logger;
1823
}
1924

2025
public object? ConvertIntermediateToObject(
@@ -31,7 +36,25 @@ public ReadingTimePropertyValueConverter(
3136

3237
var model = _readingTimeService.GetAsync(key).GetAwaiter().GetResult();
3338
var culture = _variationContextAccessor.VariationContext?.Culture;
34-
return model?.Value(culture) ?? model?.Value();
39+
var config = propertyType.DataType.ConfigurationAs<ReadingTimeConfiguration>();
40+
if (config is null)
41+
{
42+
_logger.LogError("ReadingTime configuration is missing.");
43+
return null;
44+
}
45+
46+
var output = model?.Value(culture) ?? model?.Value();
47+
48+
if (output is null)
49+
{
50+
return null;
51+
}
52+
53+
return new ReadingTimeValueModel(
54+
output.ReadingTime,
55+
config.Min,
56+
config.Max,
57+
output.Culture);
3558
}
3659

3760
public object? ConvertIntermediateToXPath(
@@ -62,4 +85,4 @@ public bool IsConverter(IPublishedPropertyType propertyType) =>
6285
PropertyValueLevel.Object => value is ReadingTimeValueModel,
6386
_ => throw new NotSupportedException($"Invalid level: {level}.")
6487
};
65-
}
88+
}

0 commit comments

Comments
 (0)