From c95154e14237aeb6278e4766ddb98a47f587b86a Mon Sep 17 00:00:00 2001 From: andy840119 Date: Sun, 17 Dec 2023 21:54:10 +0800 Subject: [PATCH 1/8] Add missing ToString(). --- .../Online/API/Requests/Responses/APIChangelogBuild.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs index 1ade120a1..aedda0524 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -83,4 +83,6 @@ public class VersionNavigation /// public APIChangelogBuild? Previous { get; set; } } + + public override string ToString() => $"Karaoke! {DisplayVersion}"; } From fc30768312c4321e19caf123adc7cca86194f8f9 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Tue, 19 Dec 2023 00:17:57 +0800 Subject: [PATCH 2/8] Rename from sidebar to index. Should be better to use the name "APIChangelogIndex" in osu.game. --- .../{APIChangelogSidebar.cs => APIChangelogIndex.cs} | 4 ++-- .../Overlays/Changelog/Sidebar/ChangelogSidebar.cs | 10 +++++----- .../Overlays/Changelog/Sidebar/YearsPanel.cs | 4 ++-- .../Overlays/KaraokeChangelogOverlay.cs | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/{APIChangelogSidebar.cs => APIChangelogIndex.cs} (71%) diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogSidebar.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs similarity index 71% rename from osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogSidebar.cs rename to osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs index 576270db6..5fbe94b38 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogSidebar.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs @@ -6,9 +6,9 @@ namespace osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; -public class APIChangelogSidebar +public class APIChangelogIndex { - public IEnumerable Changelogs { get; set; } = Array.Empty(); + public List Builds { get; set; } = new(); public int[] Years { get; set; } = Array.Empty(); } diff --git a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/ChangelogSidebar.cs b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/ChangelogSidebar.cs index b112f00ea..877b4dafd 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/ChangelogSidebar.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/ChangelogSidebar.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Karaoke.Overlays.Changelog.Sidebar; public partial class ChangelogSidebar : CompositeDrawable { [Cached] - public readonly Bindable Metadata = new(); + public readonly Bindable Metadata = new(); [Cached] public readonly Bindable Year = new(); @@ -109,19 +109,19 @@ protected override void LoadComplete() Year.BindValueChanged(e => onMetadataChanged(Metadata.Value, e.NewValue), true); } - private void onMetadataChanged(APIChangelogSidebar? metadata, int targetYear) + private void onMetadataChanged(APIChangelogIndex? metadata, int targetYear) { changelogsFlow.Clear(); if (metadata == null) return; - var allPosts = metadata.Changelogs; + var builds = metadata.Builds; - if (allPosts.Any() != true) + if (builds.Any() != true) return; - var lookup = metadata.Changelogs.ToLookup(post => post.PublishedAt.Year); + var lookup = builds.ToLookup(post => post.PublishedAt.Year); var posts = lookup[targetYear].ToList(); changelogsFlow.Add(new ChangelogSection(targetYear, posts)); } diff --git a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/YearsPanel.cs b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/YearsPanel.cs index 7246b1901..374626c67 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/YearsPanel.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/Sidebar/YearsPanel.cs @@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Karaoke.Overlays.Changelog.Sidebar; public partial class YearsPanel : CompositeDrawable { - private readonly Bindable metadata = new(); + private readonly Bindable metadata = new(); private FillFlowContainer yearsFlow = null!; [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColours, Bindable metadata) + private void load(OverlayColourProvider overlayColours, Bindable metadata) { this.metadata.BindTo(metadata); diff --git a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs index 3d0d92dfb..ba2545bcd 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs @@ -169,9 +169,9 @@ protected override void PopIn() int[] years = builds.Select(x => x.PublishedAt.Year).Distinct().ToArray(); sidebar.Year.Value = years.Max(); - sidebar.Metadata.Value = new APIChangelogSidebar + sidebar.Metadata.Value = new APIChangelogIndex { - Changelogs = builds, + Builds = builds, Years = years, }; }); From 31c4ac11051c25b4b9210a48bf7a7616a0d52a19 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Tue, 19 Dec 2023 22:55:45 +0800 Subject: [PATCH 3/8] Implement the base class for able to query the info in the github client. --- .../Online/API/Requests/GithubAPIRequest.cs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubAPIRequest.cs diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubAPIRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubAPIRequest.cs new file mode 100644 index 000000000..536e2cdf9 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubAPIRequest.cs @@ -0,0 +1,84 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading.Tasks; +using Octokit; +using osu.Game.Online.API; + +namespace osu.Game.Rulesets.Karaoke.Online.API.Requests; + +public abstract class GithubAPIRequest where T : class +{ + protected virtual GitHubClient CreateGitHubClient() => new(new ProductHeaderValue(organizationName)); + + /// + /// Invoked on successful completion of an API request. + /// This will be scheduled to the API's internal scheduler (run on update thread automatically). + /// + public event APISuccessHandler? Success; + + /// + /// Invoked on failure to complete an API request. + /// This will be scheduled to the API's internal scheduler (run on update thread automatically). + /// + public event APIFailureHandler? Failure; + + private readonly object completionStateLock = new(); + + /// + /// The state of this request, from an outside perspective. + /// This is used to ensure correct notification events are fired. + /// + public APIRequestCompletionState CompletionState { get; private set; } + + private readonly string organizationName; + + protected GithubAPIRequest(string organizationName) + { + this.organizationName = organizationName; + } + + public async Task Perform() + { + var client = CreateGitHubClient(); + + try + { + var response = await Perform(client).ConfigureAwait(false); + triggerSuccess(response); + } + catch (Exception e) + { + triggerFailure(e); + } + } + + protected abstract Task Perform(IGitHubClient client); + + private void triggerSuccess(T response) + { + lock (completionStateLock) + { + if (CompletionState != APIRequestCompletionState.Waiting) + return; + + CompletionState = APIRequestCompletionState.Completed; + } + + Success?.Invoke(response); + } + + private void triggerFailure(Exception e) + { + lock (completionStateLock) + { + if (CompletionState != APIRequestCompletionState.Waiting) + return; + + CompletionState = APIRequestCompletionState.Failed; + } + + Failure?.Invoke(e); + } +} From bd5e37accab1ee84d467ffaf578bf7fef90d69fc Mon Sep 17 00:00:00 2001 From: andy840119 Date: Tue, 19 Dec 2023 23:10:53 +0800 Subject: [PATCH 4/8] Implement the base api request for get the changelog content. Also, this class provide the static method for able to get changelog content. --- .../API/Requests/GithubChangeLogAPIRequest.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs new file mode 100644 index 000000000..02025d268 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs @@ -0,0 +1,50 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using Octokit; +using osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; + +namespace osu.Game.Rulesets.Karaoke.Online.API.Requests; + +public abstract class GithubChangeLogAPIRequest : GithubAPIRequest where T : class +{ + protected const string ORGANIZATION_NAME = "karaoke-dev"; + protected const string PROJECT_NAME = $"{ORGANIZATION_NAME}.github.io"; + protected const string CHANGELOG_PATH = "content/changelog"; + + protected GithubChangeLogAPIRequest() + : base(ORGANIZATION_NAME) + { + } + + protected static async Task GetChangelogContent(IGitHubClient client, string version) + { + string changeLogPath = $"{CHANGELOG_PATH}/{version}/index.md"; + byte[]? content = await client + .Repository + .Content + .GetRawContent(ORGANIZATION_NAME, PROJECT_NAME, changeLogPath) + .ConfigureAwait(false); + + // convert the content to a string + return System.Text.Encoding.UTF8.GetString(content); + } + + protected static APIChangelogBuild CreateBuildWithContent(APIChangelogBuild originBuild, string content) + { + return new APIChangelogBuild(originBuild.OrganizationName, originBuild.ProjectName) + { + RootUrl = originBuild.RootUrl, + Path = originBuild.Path, + Version = originBuild.Version, + Content = content, + Versions = + { + Previous = originBuild.Versions.Previous, + Next = originBuild.Versions.Next, + }, + PublishedAt = originBuild.PublishedAt, + }; + } +} From 13980930ddf6644b4802e155f17ebcd3176caaa7 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Mon, 18 Dec 2023 21:15:29 +0800 Subject: [PATCH 5/8] Implement the GetChangelogRequest to get the changelog index. --- .../API/Requests/GetChangelogRequest.cs | 81 +++++++++++++++++ .../Overlays/KaraokeChangelogOverlay.cs | 89 ++++++------------- 2 files changed, 107 insertions(+), 63 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs new file mode 100644 index 000000000..9de14eb92 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs @@ -0,0 +1,81 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Octokit; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Karaoke.Extensions; +using osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; + +namespace osu.Game.Rulesets.Karaoke.Online.API.Requests; + +public class GetChangelogRequest : GithubChangeLogAPIRequest +{ + protected override async Task Perform(IGitHubClient client) + { + var builds = await getAllBuilds(client).ConfigureAwait(false); + int[] years = generateYears(builds); + + return new APIChangelogIndex + { + Years = years, + Builds = builds, + }; + } + + private static async Task> getAllBuilds(IGitHubClient client) + { + var reposAscending = await client + .Repository + .Content + .GetAllContents(ORGANIZATION_NAME, PROJECT_NAME, CHANGELOG_PATH) + .ConfigureAwait(false); + + var builds = reposAscending + .Reverse() + .Where(x => x.Type == ContentType.Dir) + .Select(createBuild) + .ToList(); + + // adjust the mapping of previous/next versions by hand. + foreach (var build in builds) + { + build.Versions.Previous = builds.GetPrevious(build); + build.Versions.Next = builds.GetNext(build); + } + + return builds; + } + + private static APIChangelogBuild createBuild(RepositoryContent content) + { + return new APIChangelogBuild(ORGANIZATION_NAME, PROJECT_NAME) + { + RootUrl = content.HtmlUrl, + Path = content.Path, + DisplayVersion = content.Name, + PublishedAt = getPublishDateFromName(content.Name), + }; + + static DateTimeOffset getPublishDateFromName(string name) + { + var regex = new Regex("(?[-0-9]+).(?[-0-9]{2})(?[-0-9]{2})"); + var result = regex.Match(name); + if (!result.Success) + return DateTimeOffset.MaxValue; + + int year = result.GetGroupValue("year"); + int month = result.GetGroupValue("month"); + int day = result.GetGroupValue("day"); + + return new DateTimeOffset(new DateTime(year, month, day)); + } + } + + private static int[] generateYears(IEnumerable builds) + => builds.Select(x => x.PublishedAt.Year).Distinct().ToArray(); +} diff --git a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs index ba2545bcd..734bdd7ff 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs @@ -2,23 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Octokit; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using osu.Game.Overlays; -using osu.Game.Rulesets.Karaoke.Extensions; +using osu.Game.Rulesets.Karaoke.Online.API.Requests; using osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; using osu.Game.Rulesets.Karaoke.Overlays.Changelog; using osu.Game.Rulesets.Karaoke.Overlays.Changelog.Sidebar; @@ -39,7 +35,7 @@ public partial class KaraokeChangelogOverlay : OnlineOverlay private Sample? sampleBack; - private readonly List builds = new(); + private APIChangelogIndex? index; private readonly string organizationName; private readonly string branchName; @@ -94,10 +90,16 @@ private void load(AudioManager audio) Current.BindValueChanged(e => { if (e.NewValue != null) + { loadContent(new ChangelogSingleBuild(e.NewValue)); + } + else if (index != null) + { + loadContent(new ChangelogListing(index.Builds)); + } else { - loadContent(new ChangelogListing(builds)); + // todo: should show oops content. } }); } @@ -167,13 +169,11 @@ protected override void PopIn() { Current.TriggerChange(); - int[] years = builds.Select(x => x.PublishedAt.Year).Distinct().ToArray(); - sidebar.Year.Value = years.Max(); - sidebar.Metadata.Value = new APIChangelogIndex - { - Builds = builds, - Years = years, - }; + if (index == null) + return; + + sidebar.Year.Value = index.Years.Max(); + sidebar.Metadata.Value = index; }); } } @@ -192,61 +192,24 @@ private Task fetchListing() { var tcs = new TaskCompletionSource(); - var client = new GitHubClient(new ProductHeaderValue(organizationName)); + var req = new GetChangelogRequest(); - try + req.Success += res => Schedule(() => { - var reposAscending = await client.Repository.Content.GetAllContentsByRef(organizationName, projectName, "content/changelog", branchName).ConfigureAwait(false); - - if (reposAscending.Any()) - { - builds.Clear(); - builds.AddRange(reposAscending.Reverse().Where(x => x.Type == ContentType.Dir).Select(x => new APIChangelogBuild(organizationName, projectName, branchName) - { - RootUrl = x.HtmlUrl, - Path = x.Path, - DisplayVersion = x.Name, - PublishedAt = getPublishDateFromName(x.Name), - })); - - foreach (var build in builds) - { - build.Versions.Previous = builds.GetPrevious(build); - build.Versions.Next = builds.GetNext(build); - } + index = res; + tcs.SetResult(true); + }); - tcs.SetResult(true); - } - else - { - tcs.SetResult(false); - } - } - catch (RateLimitExceededException) - { - // todo: maybe show something? - } - catch (Exception) + req.Failure += e => { - // todo: maybe show something? - } - - await tcs.Task.ConfigureAwait(false); - }); + initialFetchTask = null; + tcs.SetException(e); + }; - static DateTimeOffset getPublishDateFromName(string name) - { - var regex = new Regex("(?[-0-9]+).(?[-0-9]{2})(?[-0-9]{2})"); - var result = regex.Match(name); - if (!result.Success) - return DateTimeOffset.MaxValue; - - int year = result.GetGroupValue("year"); - int month = result.GetGroupValue("month"); - int day = result.GetGroupValue("day"); + await req.Perform().ConfigureAwait(false); - return new DateTimeOffset(new DateTime(year, month, day)); - } + return tcs.Task; + }).Unwrap(); } private CancellationTokenSource? loadContentCancellation; From c584dfdc32487b1d15b5db0a53f6809348c941b0 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Wed, 20 Dec 2023 00:00:33 +0800 Subject: [PATCH 6/8] Implement the GetChangelogBuildRequest for able to get the APIChangelogBuild with content. --- ...tSceneKaraokeChangeLogMarkdownContainer.cs | 1 + .../API/Requests/GetChangelogBuildRequest.cs | 24 ++++++++++ .../API/Requests/GetChangelogRequest.cs | 2 +- .../Requests/Responses/APIChangelogBuild.cs | 14 +++++- .../Changelog/ChangeLogMarkdownContainer.cs | 14 +----- .../Changelog/ChangelogSingleBuild.cs | 45 +++++++++++++------ 6 files changed, 71 insertions(+), 29 deletions(-) create mode 100644 osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogBuildRequest.cs diff --git a/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs b/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs index 94d3dd396..602fa2159 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs @@ -28,6 +28,7 @@ public void SetUp() => Schedule(() => { Path = "content/changelog/2020.0620", RootUrl = "https://github.com/karaoke-dev/karaoke-dev.github.io/tree/master/content/changelog/2020.0620", + Content = "---\ntitle: \"2020.0620\"\ndate: 2020-06-20\n---\n\n## Achievement\n\n- Might support convert `UTAU`/`SynthV` project into karaoke beatmap in future. [karaoke](#100#101@andy840119)", }; Children = new Drawable[] diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogBuildRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogBuildRequest.cs new file mode 100644 index 000000000..b8d495959 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogBuildRequest.cs @@ -0,0 +1,24 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; +using Octokit; +using osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; + +namespace osu.Game.Rulesets.Karaoke.Online.API.Requests; + +public class GetChangelogBuildRequest : GithubChangeLogAPIRequest +{ + private readonly APIChangelogBuild originBuild; + + public GetChangelogBuildRequest(APIChangelogBuild originBuild) + { + this.originBuild = originBuild; + } + + protected override async Task Perform(IGitHubClient client) + { + string contentString = await GetChangelogContent(client, originBuild.Version).ConfigureAwait(false); + return CreateBuildWithContent(originBuild, contentString); + } +} diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs index 9de14eb92..b336d8b47 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs @@ -57,7 +57,7 @@ private static APIChangelogBuild createBuild(RepositoryContent content) { RootUrl = content.HtmlUrl, Path = content.Path, - DisplayVersion = content.Name, + Version = content.Name, PublishedAt = getPublishDateFromName(content.Name), }; diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs index aedda0524..aa979fb4a 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -56,10 +56,22 @@ public APIChangelogBuild(string organization, string project, string branch = "m /// public string ReadmeDownloadUrl => $"{DocumentUrl}index.md"; + /// + /// Version number + /// + /// 2023.0123 + /// 2023.1111 + public string Version { get; set; } = null!; + /// /// Display version /// - public string DisplayVersion { get; set; } = null!; + public string DisplayVersion => Version; + + /// + /// Might be preview or detail markdown content. + /// + public string Content { get; set; } = null!; /// /// Version diff --git a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangeLogMarkdownContainer.cs b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangeLogMarkdownContainer.cs index 299db5051..66d094fab 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangeLogMarkdownContainer.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangeLogMarkdownContainer.cs @@ -4,10 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Http; using System.Text.RegularExpressions; using Markdig.Syntax.Inlines; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Layout; @@ -25,17 +23,7 @@ public ChangeLogMarkdownContainer(APIChangelogBuild build) { DocumentUrl = build.DocumentUrl; RootUrl = build.RootUrl; - - using var httpClient = new HttpClient(); - - try - { - Text = httpClient.GetStringAsync(build.ReadmeDownloadUrl).GetResultSafely(); - } - catch (Exception) - { - Text = "Oops, seems there's something wrong with network."; - } + Text = build.Content; } public override OsuMarkdownTextFlowContainer CreateTextFlow() => new ChangeLogMarkdownTextFlowContainer(); diff --git a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogSingleBuild.cs b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogSingleBuild.cs index 8e045c2ee..c4cdef3d7 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogSingleBuild.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogSingleBuild.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -11,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; +using osu.Game.Rulesets.Karaoke.Online.API.Requests; using osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; namespace osu.Game.Rulesets.Karaoke.Overlays.Changelog; @@ -20,32 +22,47 @@ namespace osu.Game.Rulesets.Karaoke.Overlays.Changelog; /// public partial class ChangelogSingleBuild : ChangelogContent { - private readonly APIChangelogBuild build; + private readonly APIChangelogBuild originBuild; public ChangelogSingleBuild(APIChangelogBuild build) { - this.build = build; + originBuild = build; } [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { - // todo: get result from here - - if (true) + Task.Run(async () => { - Children = new Drawable[] + var tcs = new TaskCompletionSource(); + + var req = new GetChangelogBuildRequest(originBuild); + req.Success += build => { - new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, - new Box + Schedule(() => { - RelativeSizeAxes = Axes.X, - Height = 2, - Colour = colourProvider.Background6, - Margin = new MarginPadding { Top = 30 }, - }, + Children = new Drawable[] + { + new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild }, + new Box + { + RelativeSizeAxes = Axes.X, + Height = 2, + Colour = colourProvider.Background6, + Margin = new MarginPadding { Top = 30 }, + }, + }; + }); }; - } + req.Failure += _ => + { + // todo: maybe display some + }; + + await req.Perform().ConfigureAwait(false); + + return tcs.Task; + }).Unwrap(); } public partial class ChangelogBuildWithNavigation : ChangelogBuild From e5436dbb91a87012d7c943c5fea7d18e9d09cd11 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Wed, 20 Dec 2023 00:35:24 +0800 Subject: [PATCH 7/8] GetChangelogRequest should make the preview build. --- .../Online/API/Requests/GetChangelogRequest.cs | 8 ++++++++ .../Online/API/Requests/Responses/APIChangelogIndex.cs | 2 ++ .../Overlays/Changelog/ChangelogListing.cs | 4 ++-- .../Overlays/KaraokeChangelogOverlay.cs | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs index b336d8b47..fec44379d 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs @@ -18,12 +18,14 @@ public class GetChangelogRequest : GithubChangeLogAPIRequest protected override async Task Perform(IGitHubClient client) { var builds = await getAllBuilds(client).ConfigureAwait(false); + var previewBuilds = (await Task.WhenAll(builds.Take(5).Select(x => createPreviewBuild(client, x))).ConfigureAwait(false)).ToList(); int[] years = generateYears(builds); return new APIChangelogIndex { Years = years, Builds = builds, + PreviewBuilds = previewBuilds, }; } @@ -76,6 +78,12 @@ static DateTimeOffset getPublishDateFromName(string name) } } + private static async Task createPreviewBuild(IGitHubClient client, APIChangelogBuild originBuild) + { + string contentString = await GetChangelogContent(client, originBuild.Version).ConfigureAwait(false); + return CreateBuildWithContent(originBuild, contentString); + } + private static int[] generateYears(IEnumerable builds) => builds.Select(x => x.PublishedAt.Year).Distinct().ToArray(); } diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs index 5fbe94b38..bec2284f5 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogIndex.cs @@ -10,5 +10,7 @@ public class APIChangelogIndex { public List Builds { get; set; } = new(); + public List PreviewBuilds { get; set; } = new(); + public int[] Years { get; set; } = Array.Empty(); } diff --git a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogListing.cs b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogListing.cs index a59f2208e..02d209c05 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogListing.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/Changelog/ChangelogListing.cs @@ -20,9 +20,9 @@ public partial class ChangelogListing : ChangelogContent { private readonly IReadOnlyList entries; - public ChangelogListing(IEnumerable entries) + public ChangelogListing(List entries) { - this.entries = entries.Take(4).ToList(); + this.entries = entries; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs index 734bdd7ff..ade40e6cb 100644 --- a/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs +++ b/osu.Game.Rulesets.Karaoke/Overlays/KaraokeChangelogOverlay.cs @@ -95,7 +95,7 @@ private void load(AudioManager audio) } else if (index != null) { - loadContent(new ChangelogListing(index.Builds)); + loadContent(new ChangelogListing(index.PreviewBuilds)); } else { From 379a748b6d4a96a1b8f95f085e7064681e8eada4 Mon Sep 17 00:00:00 2001 From: andy840119 Date: Thu, 21 Dec 2023 22:09:18 +0800 Subject: [PATCH 8/8] Remove some un-need properties. --- ...tSceneKaraokeChangeLogMarkdownContainer.cs | 4 +- .../API/Requests/GetChangelogRequest.cs | 7 ++- .../API/Requests/GithubChangeLogAPIRequest.cs | 5 +- .../Requests/Responses/APIChangelogBuild.cs | 46 ++----------------- 4 files changed, 14 insertions(+), 48 deletions(-) diff --git a/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs b/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs index 602fa2159..518d6b764 100644 --- a/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs +++ b/osu.Game.Rulesets.Karaoke.Tests/Overlays/TestSceneKaraokeChangeLogMarkdownContainer.cs @@ -24,9 +24,9 @@ public partial class TestSceneKaraokeChangeLogMarkdownContainer : OsuTestScene [SetUp] public void SetUp() => Schedule(() => { - var build = new APIChangelogBuild("karaoke-dev", "karaoke-dev.github.io") + var build = new APIChangelogBuild { - Path = "content/changelog/2020.0620", + DocumentUrl = "https://raw.githubusercontent.com/karaoke-dev/karaoke-dev.github.io/master/content/changelog/2020.0620", RootUrl = "https://github.com/karaoke-dev/karaoke-dev.github.io/tree/master/content/changelog/2020.0620", Content = "---\ntitle: \"2020.0620\"\ndate: 2020-06-20\n---\n\n## Achievement\n\n- Might support convert `UTAU`/`SynthV` project into karaoke beatmap in future. [karaoke](#100#101@andy840119)", }; diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs index fec44379d..5c41fff43 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GetChangelogRequest.cs @@ -55,10 +55,10 @@ private static async Task> getAllBuilds(IGitHubClient cl private static APIChangelogBuild createBuild(RepositoryContent content) { - return new APIChangelogBuild(ORGANIZATION_NAME, PROJECT_NAME) + return new APIChangelogBuild { + DocumentUrl = getDocumentUrl(content.Path), RootUrl = content.HtmlUrl, - Path = content.Path, Version = content.Name, PublishedAt = getPublishDateFromName(content.Name), }; @@ -76,6 +76,9 @@ static DateTimeOffset getPublishDateFromName(string name) return new DateTimeOffset(new DateTime(year, month, day)); } + + string getDocumentUrl(string path) + => $"https://raw.githubusercontent.com/{ORGANIZATION_NAME}/{PROJECT_NAME}/{BRANCH_NAME}/{path}/"; } private static async Task createPreviewBuild(IGitHubClient client, APIChangelogBuild originBuild) diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs index 02025d268..a56b7f0ba 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/GithubChangeLogAPIRequest.cs @@ -11,6 +11,7 @@ public abstract class GithubChangeLogAPIRequest : GithubAPIRequest where T { protected const string ORGANIZATION_NAME = "karaoke-dev"; protected const string PROJECT_NAME = $"{ORGANIZATION_NAME}.github.io"; + protected const string BRANCH_NAME = "master"; protected const string CHANGELOG_PATH = "content/changelog"; protected GithubChangeLogAPIRequest() @@ -33,10 +34,10 @@ protected static async Task GetChangelogContent(IGitHubClient client, st protected static APIChangelogBuild CreateBuildWithContent(APIChangelogBuild originBuild, string content) { - return new APIChangelogBuild(originBuild.OrganizationName, originBuild.ProjectName) + return new APIChangelogBuild { + DocumentUrl = originBuild.DocumentUrl, RootUrl = originBuild.RootUrl, - Path = originBuild.Path, Version = originBuild.Version, Content = content, Versions = diff --git a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs index aa979fb4a..9773c4a71 100644 --- a/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs +++ b/osu.Game.Rulesets.Karaoke/Online/API/Requests/Responses/APIChangelogBuild.cs @@ -7,55 +7,16 @@ namespace osu.Game.Rulesets.Karaoke.Online.API.Requests.Responses; public class APIChangelogBuild { - /// - /// - /// - /// Account or organization name - /// Project name - /// Branch name - public APIChangelogBuild(string organization, string project, string branch = "master") - { - OrganizationName = organization; - ProjectName = project; - Branch = branch; - Versions = new VersionNavigation(); - } - - /// - /// Organization name - /// - public string OrganizationName { get; } - - /// - /// Project name - /// - public string ProjectName { get; } - - /// - /// Branch name - /// - public string Branch { get; } - /// /// The URL of the loaded document. /// - public string DocumentUrl => $"https://raw.githubusercontent.com/{OrganizationName}/{ProjectName}/{Branch}/{Path}/"; + public string DocumentUrl { get; set; } = null!; /// /// The base URL for all root-relative links. /// public string RootUrl { get; set; } = null!; - /// - /// Path of the project - /// - public string Path { get; set; } = null!; - - /// - /// Path to download readme url - /// - public string ReadmeDownloadUrl => $"{DocumentUrl}index.md"; - /// /// Version number /// @@ -70,13 +31,14 @@ public APIChangelogBuild(string organization, string project, string branch = "m /// /// Might be preview or detail markdown content. + /// And the content is markdown format. /// - public string Content { get; set; } = null!; + public string? Content { get; set; } /// /// Version /// - public VersionNavigation Versions { get; } + public VersionNavigation Versions { get; } = new(); /// /// Created date.