mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 18:42:56 +08:00
Merge branch 'master' into fix-slow-load-test-failure
This commit is contained in:
commit
1a0ace8d58
@ -1,7 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Propose a feature you would like to see in the game!
|
||||
---
|
||||
**Describe the new feature:**
|
||||
|
||||
**Proposal designs of the feature:**
|
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,12 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Suggestions or feature request
|
||||
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
- name: Help
|
||||
url: https://github.com/ppy/osu/discussions/categories/q-a
|
||||
about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section!
|
||||
- name: osu!stable issues
|
||||
url: https://github.com/ppy/osu-stable-issues
|
||||
about: For issues regarding osu!stable (not osu!lazer), open them here.
|
||||
about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports.
|
||||
|
||||
|
@ -7,13 +7,14 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking, IHasMainCirclePiece
|
||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, IHasMainCirclePiece
|
||||
{
|
||||
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
|
||||
|
||||
@ -111,7 +112,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
|
||||
Position = HitObject.RepeatIndex % 2 == 0 ? end : start;
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
||||
if (Slider != null)
|
||||
Position = Slider.CurvePositionAt(HitObject.RepeatIndex % 2 == 0 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host);
|
||||
|
||||
await osu.CollectionManager.Import(new MemoryStream());
|
||||
await importCollectionsFromStream(osu, new MemoryStream());
|
||||
|
||||
Assert.That(osu.CollectionManager.Collections.Count, Is.Zero);
|
||||
}
|
||||
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host);
|
||||
|
||||
await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db"));
|
||||
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
|
||||
|
||||
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2));
|
||||
|
||||
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host, true);
|
||||
|
||||
await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db"));
|
||||
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
|
||||
|
||||
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(2));
|
||||
|
||||
@ -110,7 +110,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
await osu.CollectionManager.Import(ms);
|
||||
await importCollectionsFromStream(osu, ms);
|
||||
}
|
||||
|
||||
Assert.That(host.UpdateThread.Running, Is.True);
|
||||
@ -134,7 +134,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host, true);
|
||||
|
||||
await osu.CollectionManager.Import(TestResources.OpenResource("Collections/collections.db"));
|
||||
await importCollectionsFromStream(osu, TestResources.OpenResource("Collections/collections.db"));
|
||||
|
||||
// Move first beatmap from second collection into the first.
|
||||
osu.CollectionManager.Collections[0].Beatmaps.Add(osu.CollectionManager.Collections[1].Beatmaps[0]);
|
||||
@ -169,5 +169,12 @@ namespace osu.Game.Tests.Collections.IO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task importCollectionsFromStream(TestOsuGameBase osu, Stream stream)
|
||||
{
|
||||
// intentionally spin this up on a separate task to avoid disposal deadlocks.
|
||||
// see https://github.com/EventStore/EventStore/issues/1179
|
||||
await Task.Run(() => osu.CollectionManager.Import(stream).Wait());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
CreateTest(null);
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space));
|
||||
AddAssert("score shown", () => Player.IsScoreShown);
|
||||
AddUntilStep("wait for score shown", () => Player.IsScoreShown);
|
||||
AddUntilStep("time less than storyboard duration", () => Player.GameplayClockContainer.GameplayClock.CurrentTime < currentStoryboardDuration);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -73,8 +73,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
for (int i = 0; i < users; i++)
|
||||
spectatorClient.StartPlay(i, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
|
||||
|
||||
Client.CurrentMatchPlayingUserIds.Clear();
|
||||
Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers);
|
||||
spectatorClient.Schedule(() =>
|
||||
{
|
||||
Client.CurrentMatchPlayingUserIds.Clear();
|
||||
Client.CurrentMatchPlayingUserIds.AddRange(spectatorClient.PlayingUsers);
|
||||
});
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -91,6 +94,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
|
||||
AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
175
osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs
Normal file
175
osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers.Markdown;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Wiki.Markdown;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneWikiMarkdownContainer : OsuTestScene
|
||||
{
|
||||
private TestMarkdownContainer markdownContainer;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange);
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = overlayColour.Background5,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new BasicScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(20),
|
||||
Child = markdownContainer = new TestMarkdownContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestLink()
|
||||
{
|
||||
AddStep("set current path", () => markdownContainer.CurrentPath = "Article_styling_criteria/");
|
||||
|
||||
AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)");
|
||||
AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_Page");
|
||||
|
||||
AddStep("set '../FAQ''", () => markdownContainer.Text = "[FAQ](../FAQ)");
|
||||
AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/FAQ");
|
||||
|
||||
AddStep("set './Writing''", () => markdownContainer.Text = "[wiki writing guidline](./Writing)");
|
||||
AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Writing");
|
||||
|
||||
AddStep("set 'Formatting''", () => markdownContainer.Text = "[wiki formatting guidline](Formatting)");
|
||||
AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/Formatting");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOutdatedNoticeBox()
|
||||
{
|
||||
AddStep("Add outdated yaml header", () =>
|
||||
{
|
||||
markdownContainer.Text = @"---
|
||||
outdated: true
|
||||
---";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNeedsCleanupNoticeBox()
|
||||
{
|
||||
AddStep("Add needs cleanup yaml header", () =>
|
||||
{
|
||||
markdownContainer.Text = @"---
|
||||
needs_cleanup: true
|
||||
---";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOnlyShowOutdatedNoticeBox()
|
||||
{
|
||||
AddStep("Add outdated and needs cleanup yaml", () =>
|
||||
{
|
||||
markdownContainer.Text = @"---
|
||||
outdated: true
|
||||
needs_cleanup: true
|
||||
---";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAbsoluteImage()
|
||||
{
|
||||
AddStep("Add absolute image", () =>
|
||||
{
|
||||
markdownContainer.DocumentUrl = "https://dev.ppy.sh";
|
||||
markdownContainer.Text = "![intro](/wiki/Interface/img/intro-screen.jpg)";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRelativeImage()
|
||||
{
|
||||
AddStep("Add relative image", () =>
|
||||
{
|
||||
markdownContainer.DocumentUrl = "https://dev.ppy.sh";
|
||||
markdownContainer.CurrentPath = "Interface/";
|
||||
markdownContainer.Text = "![intro](img/intro-screen.jpg)";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBlockImage()
|
||||
{
|
||||
AddStep("Add paragraph with block image", () =>
|
||||
{
|
||||
markdownContainer.DocumentUrl = "https://dev.ppy.sh";
|
||||
markdownContainer.CurrentPath = "Interface/";
|
||||
markdownContainer.Text = @"Line before image
|
||||
|
||||
![play menu](img/play-menu.jpg ""Main Menu in osu!"")
|
||||
|
||||
Line after image";
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInlineImage()
|
||||
{
|
||||
AddStep("Add inline image", () =>
|
||||
{
|
||||
markdownContainer.DocumentUrl = "https://dev.ppy.sh";
|
||||
markdownContainer.Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!";
|
||||
});
|
||||
}
|
||||
|
||||
private class TestMarkdownContainer : WikiMarkdownContainer
|
||||
{
|
||||
public LinkInline Link;
|
||||
|
||||
public new string DocumentUrl
|
||||
{
|
||||
set => base.DocumentUrl = value;
|
||||
}
|
||||
|
||||
public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer
|
||||
{
|
||||
UrlAdded = link => Link = link,
|
||||
};
|
||||
|
||||
private class TestMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
||||
{
|
||||
public Action<LinkInline> UrlAdded;
|
||||
|
||||
protected override void AddLinkText(string text, LinkInline linkInline)
|
||||
{
|
||||
base.AddLinkText(text, linkInline);
|
||||
|
||||
UrlAdded?.Invoke(linkInline);
|
||||
}
|
||||
|
||||
protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -786,9 +786,12 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
}
|
||||
}
|
||||
|
||||
private void checkVisibleItemCount(bool diff, int count) =>
|
||||
AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () =>
|
||||
private void checkVisibleItemCount(bool diff, int count)
|
||||
{
|
||||
// until step required as we are querying against alive items, which are loaded asynchronously inside DrawableCarouselBeatmapSet.
|
||||
AddUntilStep($"{count} {(diff ? "diffs" : "sets")} visible", () =>
|
||||
carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count);
|
||||
}
|
||||
|
||||
private void checkNoSelection() => AddAssert("Selection is null", () => currentSelection == null);
|
||||
|
||||
|
@ -58,8 +58,13 @@ namespace osu.Game.Collections
|
||||
|
||||
if (storage.Exists(database_name))
|
||||
{
|
||||
List<BeatmapCollection> beatmapCollections;
|
||||
|
||||
using (var stream = storage.GetStream(database_name))
|
||||
importCollections(readCollections(stream));
|
||||
beatmapCollections = readCollections(stream);
|
||||
|
||||
// intentionally fire-and-forget async.
|
||||
importCollections(beatmapCollections);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
|
||||
@ -20,26 +19,16 @@ namespace osu.Game.Overlays.News.Displays
|
||||
/// </summary>
|
||||
public class ArticleListing : CompositeDrawable
|
||||
{
|
||||
public Action<APINewsSidebar> SidebarMetadataUpdated;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private readonly Action fetchMorePosts;
|
||||
|
||||
private FillFlowContainer content;
|
||||
private ShowMoreButton showMore;
|
||||
|
||||
private GetNewsRequest request;
|
||||
private Cursor lastCursor;
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private readonly int? year;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiate a listing for the specified year.
|
||||
/// </summary>
|
||||
/// <param name="year">The year to load articles from. If null, will show the most recent articles.</param>
|
||||
public ArticleListing(int? year = null)
|
||||
public ArticleListing(Action fetchMorePosts)
|
||||
{
|
||||
this.year = year;
|
||||
this.fetchMorePosts = fetchMorePosts;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -47,6 +36,7 @@ namespace osu.Game.Overlays.News.Displays
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Vertical = 20,
|
||||
@ -75,53 +65,25 @@ namespace osu.Game.Overlays.News.Displays
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Top = 15
|
||||
},
|
||||
Action = performFetch,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Action = fetchMorePosts,
|
||||
Alpha = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
performFetch();
|
||||
}
|
||||
|
||||
private void performFetch()
|
||||
{
|
||||
request?.Cancel();
|
||||
|
||||
request = new GetNewsRequest(year, lastCursor);
|
||||
request.Success += response => Schedule(() => onSuccess(response));
|
||||
api.PerformAsync(request);
|
||||
}
|
||||
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private void onSuccess(GetNewsResponse response)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
// only needs to be updated on the initial load, as the content won't change during pagination.
|
||||
if (lastCursor == null)
|
||||
SidebarMetadataUpdated?.Invoke(response.SidebarMetadata);
|
||||
|
||||
// store cursor for next pagination request.
|
||||
lastCursor = response.Cursor;
|
||||
|
||||
LoadComponentsAsync(response.NewsPosts.Select(p => new NewsCard(p)).ToList(), loaded =>
|
||||
public void AddPosts(IEnumerable<APINewsPost> posts, bool morePostsAvailable) => Schedule(() =>
|
||||
LoadComponentsAsync(posts.Select(p => new NewsCard(p)).ToList(), loaded =>
|
||||
{
|
||||
content.AddRange(loaded);
|
||||
|
||||
showMore.IsLoading = false;
|
||||
showMore.Alpha = response.Cursor != null ? 1 : 0;
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
showMore.Alpha = morePostsAvailable ? 1 : 0;
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token)
|
||||
);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
request?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.News;
|
||||
using osu.Game.Overlays.News.Displays;
|
||||
using osu.Game.Overlays.News.Sidebar;
|
||||
@ -14,13 +15,21 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public class NewsOverlay : OnlineOverlay<NewsHeader>
|
||||
{
|
||||
private readonly Bindable<string> article = new Bindable<string>(null);
|
||||
private readonly Bindable<string> article = new Bindable<string>();
|
||||
|
||||
private readonly Container sidebarContainer;
|
||||
private readonly NewsSidebar sidebar;
|
||||
|
||||
private readonly Container content;
|
||||
|
||||
private GetNewsRequest request;
|
||||
|
||||
private Cursor lastCursor;
|
||||
|
||||
/// <summary>
|
||||
/// The year currently being displayed. If null, the main listing is being displayed.
|
||||
/// </summary>
|
||||
private int? displayedYear;
|
||||
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private bool displayUpdateRequired = true;
|
||||
@ -65,7 +74,13 @@ namespace osu.Game.Overlays
|
||||
base.LoadComplete();
|
||||
|
||||
// should not be run until first pop-in to avoid requesting data before user views.
|
||||
article.BindValueChanged(onArticleChanged);
|
||||
article.BindValueChanged(a =>
|
||||
{
|
||||
if (a.NewValue == null)
|
||||
loadListing();
|
||||
else
|
||||
loadArticle(a.NewValue);
|
||||
});
|
||||
}
|
||||
|
||||
protected override NewsHeader CreateHeader() => new NewsHeader { ShowFrontPage = ShowFrontPage };
|
||||
@ -95,7 +110,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
public void ShowYear(int year)
|
||||
{
|
||||
loadFrontPage(year);
|
||||
loadListing(year);
|
||||
Show();
|
||||
}
|
||||
|
||||
@ -108,7 +123,11 @@ namespace osu.Game.Overlays
|
||||
protected void LoadDisplay(Drawable display)
|
||||
{
|
||||
ScrollFlow.ScrollToStart();
|
||||
LoadComponentAsync(display, loaded => content.Child = loaded, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
LoadComponentAsync(display, loaded =>
|
||||
{
|
||||
content.Child = loaded;
|
||||
Loading.Hide();
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
@ -118,48 +137,65 @@ namespace osu.Game.Overlays
|
||||
sidebarContainer.Y = Math.Clamp(ScrollFlow.Current - Header.DrawHeight, 0, Math.Max(ScrollFlow.ScrollContent.DrawHeight - DrawHeight - Header.DrawHeight, 0));
|
||||
}
|
||||
|
||||
private void onArticleChanged(ValueChangedEvent<string> article)
|
||||
private void loadListing(int? year = null)
|
||||
{
|
||||
if (article.NewValue == null)
|
||||
loadFrontPage();
|
||||
else
|
||||
loadArticle(article.NewValue);
|
||||
}
|
||||
|
||||
private void loadFrontPage(int? year = null)
|
||||
{
|
||||
beginLoading();
|
||||
|
||||
Header.SetFrontPage();
|
||||
|
||||
var page = new ArticleListing(year);
|
||||
page.SidebarMetadataUpdated += metadata => Schedule(() =>
|
||||
displayedYear = year;
|
||||
lastCursor = null;
|
||||
|
||||
beginLoading(true);
|
||||
|
||||
request = new GetNewsRequest(displayedYear);
|
||||
request.Success += response => Schedule(() =>
|
||||
{
|
||||
sidebar.Metadata.Value = metadata;
|
||||
Loading.Hide();
|
||||
lastCursor = response.Cursor;
|
||||
sidebar.Metadata.Value = response.SidebarMetadata;
|
||||
|
||||
var listing = new ArticleListing(getMorePosts);
|
||||
listing.AddPosts(response.NewsPosts, response.Cursor != null);
|
||||
LoadDisplay(listing);
|
||||
});
|
||||
LoadDisplay(page);
|
||||
|
||||
API.PerformAsync(request);
|
||||
}
|
||||
|
||||
private void getMorePosts()
|
||||
{
|
||||
beginLoading(false);
|
||||
|
||||
request = new GetNewsRequest(displayedYear, lastCursor);
|
||||
request.Success += response => Schedule(() =>
|
||||
{
|
||||
lastCursor = response.Cursor;
|
||||
if (content.Child is ArticleListing listing)
|
||||
listing.AddPosts(response.NewsPosts, response.Cursor != null);
|
||||
});
|
||||
|
||||
API.PerformAsync(request);
|
||||
}
|
||||
|
||||
private void loadArticle(string article)
|
||||
{
|
||||
beginLoading();
|
||||
// This is not yet implemented nor called from anywhere.
|
||||
beginLoading(true);
|
||||
|
||||
Header.SetArticle(article);
|
||||
|
||||
// Temporary, should be handled by ArticleDisplay later
|
||||
LoadDisplay(Empty());
|
||||
Loading.Hide();
|
||||
}
|
||||
|
||||
private void beginLoading()
|
||||
private void beginLoading(bool showLoadingOverlay)
|
||||
{
|
||||
request?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
Loading.Show();
|
||||
|
||||
if (showLoadingOverlay)
|
||||
Loading.Show();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
request?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
50
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs
Normal file
50
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownContainer.cs
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using Markdig.Extensions.Yaml;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Game.Graphics.Containers.Markdown;
|
||||
|
||||
namespace osu.Game.Overlays.Wiki.Markdown
|
||||
{
|
||||
public class WikiMarkdownContainer : OsuMarkdownContainer
|
||||
{
|
||||
public string CurrentPath
|
||||
{
|
||||
set => DocumentUrl = $"{DocumentUrl}wiki/{value}";
|
||||
}
|
||||
|
||||
protected override void AddMarkdownComponent(IMarkdownObject markdownObject, FillFlowContainer container, int level)
|
||||
{
|
||||
switch (markdownObject)
|
||||
{
|
||||
case YamlFrontMatterBlock yamlFrontMatterBlock:
|
||||
container.Add(new WikiNoticeContainer(yamlFrontMatterBlock));
|
||||
break;
|
||||
|
||||
case ParagraphBlock paragraphBlock:
|
||||
// Check if paragraph only contains an image
|
||||
if (paragraphBlock.Inline.Count() == 1 && paragraphBlock.Inline.FirstChild is LinkInline { IsImage: true } linkInline)
|
||||
{
|
||||
container.Add(new WikiMarkdownImageBlock(linkInline));
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
base.AddMarkdownComponent(markdownObject, container, level);
|
||||
}
|
||||
|
||||
public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer();
|
||||
|
||||
private class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
||||
{
|
||||
protected override void AddImage(LinkInline linkInline) => AddDrawable(new WikiMarkdownImage(linkInline));
|
||||
}
|
||||
}
|
||||
}
|
29
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs
Normal file
29
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Markdig.Syntax.Inlines;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Overlays.Wiki.Markdown
|
||||
{
|
||||
public class WikiMarkdownImage : MarkdownImage, IHasTooltip
|
||||
{
|
||||
public string TooltipText { get; }
|
||||
|
||||
public WikiMarkdownImage(LinkInline linkInline)
|
||||
: base(linkInline.Url)
|
||||
{
|
||||
TooltipText = linkInline.Title;
|
||||
}
|
||||
|
||||
protected override ImageContainer CreateImageContainer(string url)
|
||||
{
|
||||
// The idea is replace "https://website.url/wiki/{path-to-image}" to "https://website.url/wiki/images/{path-to-image}"
|
||||
// "/wiki/images/*" is route to fetch wiki image from osu!web server (see: https://github.com/ppy/osu-web/blob/4205eb66a4da86bdee7835045e4bf28c35456e04/routes/web.php#L289)
|
||||
url = url.Replace("/wiki/", "/wiki/images/");
|
||||
|
||||
return base.CreateImageContainer(url);
|
||||
}
|
||||
}
|
||||
}
|
49
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs
Normal file
49
osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImageBlock.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Markdig.Syntax.Inlines;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Wiki.Markdown
|
||||
{
|
||||
public class WikiMarkdownImageBlock : FillFlowContainer
|
||||
{
|
||||
[Resolved]
|
||||
private IMarkdownTextComponent parentTextComponent { get; set; }
|
||||
|
||||
private readonly LinkInline linkInline;
|
||||
|
||||
public WikiMarkdownImageBlock(LinkInline linkInline)
|
||||
{
|
||||
this.linkInline = linkInline;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Direction = FillDirection.Vertical;
|
||||
Spacing = new Vector2(0, 3);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new WikiMarkdownImage(linkInline)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
parentTextComponent.CreateSpriteText().With(t =>
|
||||
{
|
||||
t.Text = linkInline.Title;
|
||||
t.Anchor = Anchor.TopCentre;
|
||||
t.Origin = Anchor.TopCentre;
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
97
osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs
Normal file
97
osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Markdig.Extensions.Yaml;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Containers.Markdown;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Wiki.Markdown
|
||||
{
|
||||
public class WikiNoticeContainer : FillFlowContainer
|
||||
{
|
||||
private readonly bool isOutdated;
|
||||
private readonly bool needsCleanup;
|
||||
|
||||
public WikiNoticeContainer(YamlFrontMatterBlock yamlFrontMatterBlock)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Direction = FillDirection.Vertical;
|
||||
|
||||
foreach (var line in yamlFrontMatterBlock.Lines)
|
||||
{
|
||||
switch (line.ToString())
|
||||
{
|
||||
case "outdated: true":
|
||||
isOutdated = true;
|
||||
break;
|
||||
|
||||
case "needs_cleanup: true":
|
||||
needsCleanup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Reference : https://github.com/ppy/osu-web/blob/master/resources/views/wiki/_notice.blade.php and https://github.com/ppy/osu-web/blob/master/resources/lang/en/wiki.php
|
||||
// TODO : add notice box for fallback translation, legal translation and outdated translation after implement wiki locale in the future.
|
||||
if (isOutdated)
|
||||
{
|
||||
Add(new NoticeBox
|
||||
{
|
||||
Text = "The content on this page is incomplete or outdated. If you are able to help out, please consider updating the article!",
|
||||
});
|
||||
}
|
||||
else if (needsCleanup)
|
||||
{
|
||||
Add(new NoticeBox
|
||||
{
|
||||
Text = "This page does not meet the standards of the osu! wiki and needs to be cleaned up or rewritten. If you are able to help out, please consider updating the article!",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class NoticeBox : Container
|
||||
{
|
||||
[Resolved]
|
||||
private IMarkdownTextFlowComponent parentFlowComponent { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider, OsuColour colour)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
MarkdownTextFlowContainer textFlow;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4,
|
||||
},
|
||||
textFlow = parentFlowComponent.CreateTextFlow().With(t =>
|
||||
{
|
||||
t.Colour = colour.Orange1;
|
||||
t.Padding = new MarginPadding
|
||||
{
|
||||
Vertical = 10,
|
||||
Horizontal = 15,
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
textFlow.AddText(Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -311,6 +311,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// Invoked for this <see cref="DrawableHitObject"/> to take on any values from a newly-applied <see cref="HitObject"/>.
|
||||
/// This is also fired after any changes which occurred via an <see cref="osu.Game.Rulesets.Objects.HitObject.ApplyDefaults"/> call.
|
||||
/// </summary>
|
||||
protected virtual void OnApply()
|
||||
{
|
||||
@ -318,6 +319,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// Invoked for this <see cref="DrawableHitObject"/> to revert any values previously taken on from the currently-applied <see cref="HitObject"/>.
|
||||
/// This is also fired after any changes which occurred via an <see cref="osu.Game.Rulesets.Objects.HitObject.ApplyDefaults"/> call.
|
||||
/// </summary>
|
||||
protected virtual void OnFree()
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved(CanBeNull = true)]
|
||||
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
|
||||
|
||||
public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.Children ?? Enumerable.Empty<DrawableCarouselItem>();
|
||||
public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren;
|
||||
|
||||
[CanBeNull]
|
||||
private Container<DrawableCarouselItem> beatmapContainer;
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -53,6 +54,8 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
});
|
||||
}
|
||||
|
||||
public new void Schedule(Action action) => base.Schedule(action);
|
||||
|
||||
/// <summary>
|
||||
/// Sends frames for an arbitrary user.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user