diff --git a/Content.Client/Changelog/ChangelogButton.cs b/Content.Client/Changelog/ChangelogButton.cs new file mode 100644 index 0000000000..d5d0a3526b --- /dev/null +++ b/Content.Client/Changelog/ChangelogButton.cs @@ -0,0 +1,53 @@ +using Content.Client.UserInterface.Stylesheets; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +namespace Content.Client.Changelog +{ + public sealed class ChangelogButton : Button + { + [Dependency] private readonly ChangelogManager _changelogManager = default!; + + public ChangelogButton() + { + IoCManager.InjectDependencies(this); + + OnPressed += OnOnPressed; + } + + protected override void EnteredTree() + { + base.EnteredTree(); + + _changelogManager.NewChangelogEntriesChanged += UpdateStuff; + UpdateStuff(); + } + + protected override void ExitedTree() + { + base.ExitedTree(); + + _changelogManager.NewChangelogEntriesChanged -= UpdateStuff; + } + + private void OnOnPressed(ButtonEventArgs obj) + { + new ChangelogWindow().OpenCentered(); + } + + private void UpdateStuff() + { + if (_changelogManager.NewChangelogEntries) + { + Text = Loc.GetString("changelog-button-new-entries"); + StyleClasses.Add(StyleBase.ButtonCaution); + } + else + { + Text = Loc.GetString("changelog-button"); + StyleClasses.Remove(StyleBase.ButtonCaution); + } + } + } +} diff --git a/Content.Client/Changelog/ChangelogManager.cs b/Content.Client/Changelog/ChangelogManager.cs new file mode 100644 index 0000000000..b6c5b7b04d --- /dev/null +++ b/Content.Client/Changelog/ChangelogManager.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Robust.Shared.ContentPack; +using Robust.Shared.IoC; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +#nullable enable + +namespace Content.Client.Changelog +{ + public sealed class ChangelogManager + { + // If you fork SS14, change this to have the changelog "last seen" date stored separately. + public const string ForkId = "Wizards"; + + [Dependency] private readonly IResourceManager _resource = default!; + + public bool NewChangelogEntries { get; private set; } + public int LastReadId { get; private set; } + public int MaxId { get; private set; } + + public event Action? NewChangelogEntriesChanged; + + /// + /// Ran when the user opens ("read") the changelog, + /// stores the new ID to disk and clears . + /// + /// + /// is NOT cleared + /// since that's used in the changelog menu to show the "since you last read" bar. + /// + public void SaveNewReadId() + { + NewChangelogEntries = false; + NewChangelogEntriesChanged?.Invoke(); + + using var file = _resource.UserData.Create(new ResourcePath($"/changelog_last_seen_{ForkId}")); + using var sw = new StreamWriter(file); + + sw.Write(MaxId.ToString()); + } + + public async void Initialize() + { + // Open changelog purely to compare to the last viewed date. + var changelog = await LoadChangelog(); + + if (changelog.Count == 0) + { + return; + } + + MaxId = changelog.Max(c => c.Id); + + var path = new ResourcePath($"/changelog_last_seen_{ForkId}"); + if (_resource.UserData.Exists(path)) + { + LastReadId = int.Parse(_resource.UserData.ReadAllText(path)); + } + + NewChangelogEntries = LastReadId < MaxId; + + NewChangelogEntriesChanged?.Invoke(); + } + + public Task> LoadChangelog() + { + return Task.Run(() => + { + var yamlData = _resource.ContentFileReadYaml(new ResourcePath("/Changelog/Changelog.yml")); + + if (yamlData.Documents.Count == 0) + return new List(); + + var serializer = YamlObjectSerializer.NewReader((YamlMappingNode) yamlData.Documents[0].RootNode); + + return serializer.ReadDataField>("Entries"); + }); + } + + + public sealed class ChangelogEntry : IExposeData + { + public int Id { get; private set; } + public string Author { get; private set; } = ""; + public DateTime Time { get; private set; } + public List Changes { get; private set; } = default!; + + void IExposeData.ExposeData(ObjectSerializer serializer) + { + Id = serializer.ReadDataField("id"); + Author = serializer.ReadDataField("author"); + Time = DateTime.Parse(serializer.ReadDataField("time"), null, DateTimeStyles.RoundtripKind); + Changes = serializer.ReadDataField>("changes"); + } + } + + public sealed class ChangelogChange : IExposeData + { + public ChangelogLineType Type { get; private set; } + public string Message { get; private set; } = ""; + + void IExposeData.ExposeData(ObjectSerializer serializer) + { + Type = serializer.ReadDataField("type"); + Message = serializer.ReadDataField("message"); + } + } + + public enum ChangelogLineType + { + Add, + Remove, + Fix, + Tweak, + } + } +} diff --git a/Content.Client/Changelog/ChangelogWindow.xaml b/Content.Client/Changelog/ChangelogWindow.xaml new file mode 100644 index 0000000000..047b4ce809 --- /dev/null +++ b/Content.Client/Changelog/ChangelogWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/Content.Client/Changelog/ChangelogWindow.xaml.cs b/Content.Client/Changelog/ChangelogWindow.xaml.cs new file mode 100644 index 0000000000..dce9dca690 --- /dev/null +++ b/Content.Client/Changelog/ChangelogWindow.xaml.cs @@ -0,0 +1,207 @@ +using System; +using System.Linq; +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using JetBrains.Annotations; +using Robust.Client.AutoGenerated; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Console; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Utility; +using static Content.Client.Changelog.ChangelogManager; + +namespace Content.Client.Changelog +{ + [GenerateTypedNameReferences] + public sealed partial class ChangelogWindow : BaseWindow + { + [Dependency] private readonly ChangelogManager _changelog = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + + public ChangelogWindow() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + Stylesheet = IoCManager.Resolve().SheetSpace; + CloseButton.OnPressed += _ => Close(); + } + + protected override void Opened() + { + base.Opened(); + + _changelog.SaveNewReadId(); + PopulateChangelog(); + } + + private async void PopulateChangelog() + { + // Changelog is not kept in memory so load it again. + var changelog = await _changelog.LoadChangelog(); + + var byDay = changelog + .GroupBy(e => e.Time.ToLocalTime().Date) + .OrderByDescending(c => c.Key); + + var hasRead = _changelog.MaxId <= _changelog.LastReadId; + foreach (var dayEntries in byDay) + { + var day = dayEntries.Key; + + var groupedEntries = dayEntries + .GroupBy(c => (c.Author, Read: c.Id <= _changelog.LastReadId)) + .OrderBy(c => c.Key.Read) + .ThenBy(c => c.Key.Author); + + string dayNice; + var today = DateTime.Today; + if (day == today) + dayNice = Loc.GetString("changelog-today"); + else if (day == today.AddDays(-1)) + dayNice = Loc.GetString("changelog-yesterday"); + else + dayNice = day.ToShortDateString(); + + ChangelogBody.AddChild(new Label + { + Text = dayNice, + StyleClasses = {"LabelHeading"}, + Margin = new Thickness(4, 6, 0, 0) + }); + + var first = true; + + foreach (var groupedEntry in groupedEntries) + { + var (author, read) = groupedEntry.Key; + + if (!first) + { + ChangelogBody.AddChild(new Control {Margin = new Thickness(4)}); + } + + if (read && !hasRead) + { + hasRead = true; + + var upArrow = + _resourceCache.GetTexture("/Textures/Interface/Changelog/up_arrow.svg.192dpi.png"); + + var readDivider = new VBoxContainer(); + + var hBox = new HBoxContainer + { + HorizontalAlignment = HAlignment.Center, + Children = + { + new TextureRect + { + Texture = upArrow, + ModulateSelfOverride = Color.FromHex("#888"), + TextureScale = (0.5f, 0.5f), + Margin = new Thickness(4, 3), + VerticalAlignment = VAlignment.Bottom + }, + new Label + { + Align = Label.AlignMode.Center, + Text = Loc.GetString("changelog-new-changes"), + FontColorOverride = Color.FromHex("#888"), + }, + new TextureRect + { + Texture = upArrow, + ModulateSelfOverride = Color.FromHex("#888"), + TextureScale = (0.5f, 0.5f), + Margin = new Thickness(4, 3), + VerticalAlignment = VAlignment.Bottom + } + } + }; + + readDivider.AddChild(hBox); + readDivider.AddChild(new PanelContainer {StyleClasses = {"LowDivider"}}); + ChangelogBody.AddChild(readDivider); + + if (first) + readDivider.SetPositionInParent(ChangelogBody.ChildCount - 2); + } + + first = false; + + var authorLabel = new RichTextLabel + { + Margin = new Thickness(6, 0, 0, 0), + }; + authorLabel.SetMessage( + FormattedMessage.FromMarkup(Loc.GetString("changelog-author-changed", ("author", author)))); + ChangelogBody.AddChild(authorLabel); + + foreach (var change in groupedEntry.SelectMany(c => c.Changes)) + { + var text = new RichTextLabel(); + text.SetMessage(FormattedMessage.FromMarkup(change.Message)); + ChangelogBody.AddChild(new HBoxContainer + { + Margin = new Thickness(14, 1, 10, 2), + Children = + { + GetIcon(change.Type), + text + } + }); + } + } + } + + var version = typeof(ChangelogWindow).Assembly.GetName().Version ?? new Version(1, 0); + VersionLabel.Text = Loc.GetString("changelog-version-tag", ("version", version.ToString())); + } + + private TextureRect GetIcon(ChangelogLineType type) + { + var (file, color) = type switch + { + ChangelogLineType.Add => ("plus.svg.192dpi.png", "#6ED18D"), + ChangelogLineType.Remove => ("minus.svg.192dpi.png", "#D16E6E"), + ChangelogLineType.Fix => ("bug.svg.192dpi.png", "#D1BA6E"), + ChangelogLineType.Tweak => ("wrench.svg.192dpi.png", "#6E96D1"), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + + return new TextureRect + { + Texture = _resourceCache.GetTexture(new ResourcePath($"/Textures/Interface/Changelog/{file}")), + VerticalAlignment = VAlignment.Top, + TextureScale = (0.5f, 0.5f), + Margin = new Thickness(2, 4, 6, 2), + ModulateSelfOverride = Color.FromHex(color) + }; + } + + protected override DragMode GetDragModeFor(Vector2 relativeMousePos) + { + return DragMode.Move; + } + } + + [UsedImplicitly] + public sealed class ChangelogCommand : IConsoleCommand + { + public string Command => "changelog"; + public string Description => "Opens the changelog"; + public string Help => "Usage: changelog"; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + new ChangelogWindow().OpenCentered(); + } + } +} diff --git a/Content.Client/ClientContentIoC.cs b/Content.Client/ClientContentIoC.cs index f80e61b2e3..1828775f23 100644 --- a/Content.Client/ClientContentIoC.cs +++ b/Content.Client/ClientContentIoC.cs @@ -1,4 +1,5 @@ using Content.Client.Administration; +using Content.Client.Changelog; using Content.Client.Chat; using Content.Client.Eui; using Content.Client.GameTicking; @@ -45,6 +46,7 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 965efc8798..d77639501b 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -1,5 +1,6 @@ using System; using Content.Client.Administration; +using Content.Client.Changelog; using Content.Client.Eui; using Content.Client.GameObjects.Components.Actor; using Content.Client.Input; @@ -98,6 +99,7 @@ namespace Content.Client IoCManager.Resolve().PlayerJoinedServer += SubscribePlayerAttachmentEvents; IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + IoCManager.Resolve().Initialize(); IoCManager.InjectDependencies(this); diff --git a/Content.Client/State/MainMenu.cs b/Content.Client/State/MainMenu.cs index cc55b96f24..34b3c93647 100644 --- a/Content.Client/State/MainMenu.cs +++ b/Content.Client/State/MainMenu.cs @@ -1,5 +1,6 @@ using System; using System.Text.RegularExpressions; +using Content.Client.Changelog; using Content.Client.UserInterface; using Robust.Client; using Robust.Client.ResourceManagement; @@ -309,6 +310,8 @@ namespace Content.Client.State vBox.AddChild(QuitButton); + vBox.AddChild(new ChangelogButton()); + VersionLabel = new Label { Text = "v0.1" diff --git a/Content.Client/UserInterface/ServerInfo.cs b/Content.Client/UserInterface/ServerInfo.cs index bb29cd6ded..81eb7f5da5 100644 --- a/Content.Client/UserInterface/ServerInfo.cs +++ b/Content.Client/UserInterface/ServerInfo.cs @@ -1,4 +1,5 @@ -using Robust.Client.UserInterface; +using Content.Client.Changelog; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -35,10 +36,17 @@ namespace Content.Client.UserInterface var creditsButton = new Button { Text = Loc.GetString("Credits") }; creditsButton.OnPressed += args => new CreditsWindow().Open(); + var changelogButton = new ChangelogButton + { + HorizontalExpand = true, + HorizontalAlignment = HAlignment.Right + }; + buttons.AddChild(discordButton); buttons.AddChild(websiteButton); buttons.AddChild(reportButton); buttons.AddChild(creditsButton); + buttons.AddChild(changelogButton); } public void SetInfoBlob(string markup) diff --git a/Content.Client/UserInterface/Stylesheets/StyleBase.cs b/Content.Client/UserInterface/Stylesheets/StyleBase.cs index 2da28b599e..3b827cf753 100644 --- a/Content.Client/UserInterface/Stylesheets/StyleBase.cs +++ b/Content.Client/UserInterface/Stylesheets/StyleBase.cs @@ -94,6 +94,36 @@ namespace Content.Client.UserInterface.Stylesheets }; BaseAngleRect.SetPatchMargin(StyleBox.Margin.All, 10); + var vScrollBarGrabberNormal = new StyleBoxFlat + { + BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginLeftOverride = 10, + ContentMarginTopOverride = 10 + }; + var vScrollBarGrabberHover = new StyleBoxFlat + { + BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginLeftOverride = 10, + ContentMarginTopOverride = 10 + }; + var vScrollBarGrabberGrabbed = new StyleBoxFlat + { + BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginLeftOverride = 10, + ContentMarginTopOverride = 10 + }; + + var hScrollBarGrabberNormal = new StyleBoxFlat + { + BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginTopOverride = 10 + }; + var hScrollBarGrabberHover = new StyleBoxFlat + { + BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginTopOverride = 10 + }; + var hScrollBarGrabberGrabbed = new StyleBoxFlat + { + BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginTopOverride = 10 + }; + + BaseRules = new[] { // Default font. @@ -138,6 +168,52 @@ namespace Content.Client.UserInterface.Stylesheets new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#753131")), }), + // Scroll bars + new StyleRule(new SelectorElement(typeof(VScrollBar), null, null, null), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + vScrollBarGrabberNormal), + }), + + new StyleRule( + new SelectorElement(typeof(VScrollBar), null, null, new[] {ScrollBar.StylePseudoClassHover}), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + vScrollBarGrabberHover), + }), + + new StyleRule( + new SelectorElement(typeof(VScrollBar), null, null, new[] {ScrollBar.StylePseudoClassGrabbed}), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + vScrollBarGrabberGrabbed), + }), + + new StyleRule(new SelectorElement(typeof(HScrollBar), null, null, null), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + hScrollBarGrabberNormal), + }), + + new StyleRule( + new SelectorElement(typeof(HScrollBar), null, null, new[] {ScrollBar.StylePseudoClassHover}), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + hScrollBarGrabberHover), + }), + + new StyleRule( + new SelectorElement(typeof(HScrollBar), null, null, new[] {ScrollBar.StylePseudoClassGrabbed}), + new[] + { + new StyleProperty(ScrollBar.StylePropertyGrabber, + hScrollBarGrabberGrabbed), + }), }; } } diff --git a/Content.Client/UserInterface/Stylesheets/StyleNano.cs b/Content.Client/UserInterface/Stylesheets/StyleNano.cs index 69b0b49a17..83186507b7 100644 --- a/Content.Client/UserInterface/Stylesheets/StyleNano.cs +++ b/Content.Client/UserInterface/Stylesheets/StyleNano.cs @@ -31,7 +31,6 @@ namespace Content.Client.UserInterface.Stylesheets public const string StyleClassActionSearchBox = "actionSearchBox"; public const string StyleClassActionMenuItemRevoked = "actionMenuItemRevoked"; - public const string StyleClassSliderRed = "Red"; public const string StyleClassSliderGreen = "Green"; public const string StyleClassSliderBlue = "Blue"; @@ -237,35 +236,6 @@ namespace Content.Client.UserInterface.Stylesheets var tabContainerBoxInactive = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 32)}; tabContainerBoxInactive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5); - var vScrollBarGrabberNormal = new StyleBoxFlat - { - BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginLeftOverride = 10, - ContentMarginTopOverride = 10 - }; - var vScrollBarGrabberHover = new StyleBoxFlat - { - BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginLeftOverride = 10, - ContentMarginTopOverride = 10 - }; - var vScrollBarGrabberGrabbed = new StyleBoxFlat - { - BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginLeftOverride = 10, - ContentMarginTopOverride = 10 - }; - - var hScrollBarGrabberNormal = new StyleBoxFlat - { - BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginTopOverride = 10 - }; - var hScrollBarGrabberHover = new StyleBoxFlat - { - BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginTopOverride = 10 - }; - var hScrollBarGrabberGrabbed = new StyleBoxFlat - { - BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginTopOverride = 10 - }; - var progressBarBackground = new StyleBoxFlat { BackgroundColor = new Color(0.25f, 0.25f, 0.25f) @@ -585,53 +555,6 @@ namespace Content.Client.UserInterface.Stylesheets new StyleProperty(TabContainer.StylePropertyTabStyleBoxInactive, tabContainerBoxInactive), }), - // Scroll bars - new StyleRule(new SelectorElement(typeof(VScrollBar), null, null, null), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - vScrollBarGrabberNormal), - }), - - new StyleRule( - new SelectorElement(typeof(VScrollBar), null, null, new[] {ScrollBar.StylePseudoClassHover}), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - vScrollBarGrabberHover), - }), - - new StyleRule( - new SelectorElement(typeof(VScrollBar), null, null, new[] {ScrollBar.StylePseudoClassGrabbed}), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - vScrollBarGrabberGrabbed), - }), - - new StyleRule(new SelectorElement(typeof(HScrollBar), null, null, null), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - hScrollBarGrabberNormal), - }), - - new StyleRule( - new SelectorElement(typeof(HScrollBar), null, null, new[] {ScrollBar.StylePseudoClassHover}), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - hScrollBarGrabberHover), - }), - - new StyleRule( - new SelectorElement(typeof(HScrollBar), null, null, new[] {ScrollBar.StylePseudoClassGrabbed}), - new[] - { - new StyleProperty(ScrollBar.StylePropertyGrabber, - hScrollBarGrabberGrabbed), - }), - // ProgressBar new StyleRule(new SelectorElement(typeof(ProgressBar), null, null, null), new[] diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Resources/Changelog/Parts/parts_here.txt b/Resources/Changelog/Parts/parts_here.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Resources/Locale/en-US/ui/changelog.ftl b/Resources/Locale/en-US/ui/changelog.ftl new file mode 100644 index 0000000000..620cc361f1 --- /dev/null +++ b/Resources/Locale/en-US/ui/changelog.ftl @@ -0,0 +1,11 @@ +### Stuff for the changelog window. + +changelog-window-title = Changelog +changelog-author-changed = [color=#EEE]{ $author }[/color] changed: +changelog-today = Today +changelog-yesterday = Yesterday +changelog-new-changes = new changes +changelog-version-tag = version v{ $version } + +changelog-button = Changelog +changelog-button-new-entries = Changelog (new!) diff --git a/Resources/Textures/Interface/Changelog/bug.svg b/Resources/Textures/Interface/Changelog/bug.svg new file mode 100644 index 0000000000..04a0a14288 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/bug.svg @@ -0,0 +1,109 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png b/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png new file mode 100644 index 0000000000..6ff60d45d7 Binary files /dev/null and b/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png differ diff --git a/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png.yml b/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/bug.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Interface/Changelog/minus.svg b/Resources/Textures/Interface/Changelog/minus.svg new file mode 100644 index 0000000000..e29e613ee2 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/minus.svg @@ -0,0 +1,73 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png b/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png new file mode 100644 index 0000000000..b4637590db Binary files /dev/null and b/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png differ diff --git a/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png.yml b/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/minus.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Interface/Changelog/plus.svg b/Resources/Textures/Interface/Changelog/plus.svg new file mode 100644 index 0000000000..3721c9a091 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/plus.svg @@ -0,0 +1,83 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png b/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png new file mode 100644 index 0000000000..b6c8ae4908 Binary files /dev/null and b/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png differ diff --git a/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png.yml b/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/plus.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Interface/Changelog/up_arrow.svg b/Resources/Textures/Interface/Changelog/up_arrow.svg new file mode 100644 index 0000000000..fe9df9d332 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/up_arrow.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png b/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png new file mode 100644 index 0000000000..2ffc58a793 Binary files /dev/null and b/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png differ diff --git a/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png.yml b/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/up_arrow.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Interface/Changelog/wrench.svg b/Resources/Textures/Interface/Changelog/wrench.svg new file mode 100644 index 0000000000..853630849d --- /dev/null +++ b/Resources/Textures/Interface/Changelog/wrench.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png b/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png new file mode 100644 index 0000000000..d6e617a42e Binary files /dev/null and b/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png differ diff --git a/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png.yml b/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/wrench.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Tools/update_changelog.py b/Tools/update_changelog.py new file mode 100755 index 0000000000..f46d2c7618 --- /dev/null +++ b/Tools/update_changelog.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +import sys +import os +from typing import List, Any +import yaml +import argparse +import datetime + +MAX_ENTRIES = 500 + +HEADER_RE = r"(?::cl:|🆑) *\r?\n(.+)$" +ENTRY_RE = r"^ *[*-]? *(\S[^\n\r]+)\r?$" + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("changelog_file") + parser.add_argument("parts_dir") + + args = parser.parse_args() + + with open(args.changelog_file, "r", encoding="utf-8-sig") as f: + current_data = yaml.safe_load(f) + + entries_list: List[Any] + if current_data is None: + entries_list = [] + else: + entries_list = current_data["Entries"] + + max_id = max(map(lambda e: e["id"], entries_list), default=0) + + for partname in os.listdir(args.parts_dir): + if not partname.endswith(".yml"): + continue + + partpath = os.path.join(args.parts_dir, partname) + print(partpath) + + partyaml = yaml.safe_load(open(partpath, "r", encoding="utf-8-sig")) + + author = partyaml["author"] + time = partyaml.get( + "time", datetime.datetime.now(datetime.timezone.utc).isoformat() + ) + changes = partyaml["changes"] + max_id += 1 + new_id = max_id + + entries_list.append( + {"author": author, "time": time, "changes": changes, "id": new_id} + ) + + os.remove(partpath) + + print(f"Have {len(entries_list)} changelog entries") + + entries_list.sort(key=lambda e: e["id"]) + + overflow = len(entries_list) - MAX_ENTRIES + if overflow > 0: + print(f"Removing {overflow} old entries.") + entries_list = entries_list[overflow:] + + with open(args.changelog_file, "w") as f: + yaml.safe_dump({"Entries": entries_list}, f) + + +main()