1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 14:02:55 +08:00

Merge pull request #9459 from EVAST9919/news

News overlay implementaton
This commit is contained in:
Dan Balasescu 2020-08-03 15:51:27 +09:00 committed by GitHub
commit 138afea431
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 380 additions and 306 deletions

View File

@ -31,15 +31,16 @@ namespace osu.Game.Tests.Visual.Online
{ {
new NewsCard(new APINewsPost new NewsCard(new APINewsPost
{ {
Title = "This post has an image which starts with \"/\" and has many authors!", Title = "This post has an image which starts with \"/\" and has many authors! (clickable)",
Preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", Preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
Author = "someone, someone1, someone2, someone3, someone4", Author = "someone, someone1, someone2, someone3, someone4",
FirstImage = "/help/wiki/shared/news/banners/monthly-beatmapping-contest.png", FirstImage = "/help/wiki/shared/news/banners/monthly-beatmapping-contest.png",
PublishedAt = DateTimeOffset.Now PublishedAt = DateTimeOffset.Now,
Slug = "2020-07-16-summer-theme-park-2020-voting-open"
}), }),
new NewsCard(new APINewsPost new NewsCard(new APINewsPost
{ {
Title = "This post has a full-url image! (HTML entity: &)", Title = "This post has a full-url image! (HTML entity: &) (non-clickable)",
Preview = "boom (HTML entity: &)", Preview = "boom (HTML entity: &)",
Author = "user (HTML entity: &)", Author = "user (HTML entity: &)",
FirstImage = "https://assets.ppy.sh/artists/88/header.jpg", FirstImage = "https://assets.ppy.sh/artists/88/header.jpg",

View File

@ -0,0 +1,53 @@
// 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 NUnit.Framework;
using osu.Game.Overlays.News;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneNewsHeader : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
private TestHeader header;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = header = new TestHeader
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
});
[Test]
public void TestControl()
{
AddAssert("Front page selected", () => header.Current.Value == "frontpage");
AddAssert("1 tab total", () => header.TabCount == 1);
AddStep("Set article 1", () => header.SetArticle("1"));
AddAssert("Article 1 selected", () => header.Current.Value == "1");
AddAssert("2 tabs total", () => header.TabCount == 2);
AddStep("Set article 2", () => header.SetArticle("2"));
AddAssert("Article 2 selected", () => header.Current.Value == "2");
AddAssert("2 tabs total", () => header.TabCount == 2);
AddStep("Set front page", () => header.SetFrontPage());
AddAssert("Front page selected", () => header.Current.Value == "frontpage");
AddAssert("1 tab total", () => header.TabCount == 1);
}
private class TestHeader : NewsHeader
{
public int TabCount => TabControl.Items.Count;
}
}
}

View File

@ -2,65 +2,64 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Graphics; using NUnit.Framework;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.News;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
public class TestSceneNewsOverlay : OsuTestScene public class TestSceneNewsOverlay : OsuTestScene
{ {
private TestNewsOverlay news; private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
protected override void LoadComplete() private NewsOverlay news;
[SetUp]
public void SetUp() => Schedule(() => Child = news = new NewsOverlay());
[Test]
public void TestRequest()
{ {
base.LoadComplete(); setUpNewsResponse(responseExample);
Add(news = new TestNewsOverlay()); AddStep("Show", () => news.Show());
AddStep(@"Show", news.Show); AddStep("Show article", () => news.ShowArticle("article"));
AddStep(@"Hide", news.Hide);
AddStep(@"Show front page", () => news.ShowFrontPage());
AddStep(@"Custom article", () => news.Current.Value = "Test Article 101");
AddStep(@"Article covers", () => news.LoadAndShowContent(new NewsCoverTest()));
} }
private class TestNewsOverlay : NewsOverlay private void setUpNewsResponse(GetNewsResponse r)
{ => AddStep("set up response", () =>
public new void LoadAndShowContent(NewsContent content) => base.LoadAndShowContent(content);
}
private class NewsCoverTest : NewsContent
{
public NewsCoverTest()
{ {
Spacing = new osuTK.Vector2(0, 10); dummyAPI.HandleRequest = request =>
var article = new NewsArticleCover.ArticleInfo
{ {
Author = "Ephemeral", if (!(request is GetNewsRequest getNewsRequest))
CoverUrl = "https://assets.ppy.sh/artists/58/header.jpg", return;
Time = new DateTime(2019, 12, 4),
Title = "New Featured Artist: Kurokotei"
};
Children = new Drawable[] getNewsRequest.TriggerSuccess(r);
{
new NewsArticleCover(article)
{
Height = 200
},
new NewsArticleCover(article)
{
Height = 120
},
new NewsArticleCover(article)
{
RelativeSizeAxes = Axes.None,
Size = new osuTK.Vector2(400, 200),
}
}; };
});
private GetNewsResponse responseExample => new GetNewsResponse
{
NewsPosts = new[]
{
new APINewsPost
{
Title = "This post has an image which starts with \"/\" and has many authors!",
Preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
Author = "someone, someone1, someone2, someone3, someone4",
FirstImage = "/help/wiki/shared/news/banners/monthly-beatmapping-contest.png",
PublishedAt = DateTimeOffset.Now
},
new APINewsPost
{
Title = "This post has a full-url image! (HTML entity: &amp;)",
Preview = "boom (HTML entity: &amp;)",
Author = "user (HTML entity: &amp;)",
FirstImage = "https://assets.ppy.sh/artists/88/header.jpg",
PublishedAt = DateTimeOffset.Now
}
} }
} };
} }
} }

View File

@ -44,6 +44,7 @@ namespace osu.Game.Tests.Visual
typeof(NotificationOverlay), typeof(NotificationOverlay),
typeof(BeatmapListingOverlay), typeof(BeatmapListingOverlay),
typeof(DashboardOverlay), typeof(DashboardOverlay),
typeof(NewsOverlay),
typeof(ChannelManager), typeof(ChannelManager),
typeof(ChatOverlay), typeof(ChatOverlay),
typeof(SettingsOverlay), typeof(SettingsOverlay),

View File

@ -0,0 +1,27 @@
// 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 osu.Framework.IO.Network;
using osu.Game.Extensions;
namespace osu.Game.Online.API.Requests
{
public class GetNewsRequest : APIRequest<GetNewsResponse>
{
private readonly Cursor cursor;
public GetNewsRequest(Cursor cursor = null)
{
this.cursor = cursor;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.AddCursor(cursor);
return req;
}
protected override string Target => "news";
}
}

View File

@ -0,0 +1,15 @@
// 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.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetNewsResponse : ResponseWithCursor
{
[JsonProperty("news_posts")]
public IEnumerable<APINewsPost> NewsPosts;
}
}

View File

@ -73,6 +73,8 @@ namespace osu.Game
private DashboardOverlay dashboard; private DashboardOverlay dashboard;
private NewsOverlay news;
private UserProfileOverlay userProfile; private UserProfileOverlay userProfile;
private BeatmapSetOverlay beatmapSetOverlay; private BeatmapSetOverlay beatmapSetOverlay;
@ -632,6 +634,7 @@ namespace osu.Game
// overlay elements // overlay elements
loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true);
loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true);
loadComponentSingleFile(news = new NewsOverlay(), overlayContent.Add, true);
var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true);
loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true);
loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true);
@ -689,7 +692,7 @@ namespace osu.Game
} }
// ensure only one of these overlays are open at once. // ensure only one of these overlays are open at once.
var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay };
foreach (var overlay in singleDisplayOverlays) foreach (var overlay in singleDisplayOverlays)
{ {

View File

@ -19,7 +19,6 @@ namespace osu.Game.Overlays
{ {
private CancellationTokenSource cancellationToken; private CancellationTokenSource cancellationToken;
private Box background;
private Container content; private Container content;
private DashboardOverlayHeader header; private DashboardOverlayHeader header;
private LoadingLayer loading; private LoadingLayer loading;
@ -35,9 +34,10 @@ namespace osu.Game.Overlays
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
background = new Box new Box
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background5
}, },
scrollFlow = new OverlayScrollContainer scrollFlow = new OverlayScrollContainer
{ {
@ -66,8 +66,6 @@ namespace osu.Game.Overlays
}, },
loading = new LoadingLayer(content), loading = new LoadingLayer(content),
}; };
background.Colour = ColourProvider.Background5;
} }
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -0,0 +1,114 @@
// 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 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 osuTK;
namespace osu.Game.Overlays.News.Displays
{
public class FrontPageDisplay : CompositeDrawable
{
[Resolved]
private IAPIProvider api { get; set; }
private FillFlowContainer content;
private ShowMoreButton showMore;
private GetNewsRequest request;
private Cursor lastCursor;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding
{
Vertical = 20,
Left = 30,
Right = 50
};
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
content = new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10)
},
showMore = new ShowMoreButton
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding
{
Top = 15
},
Action = performFetch,
Alpha = 0
}
}
};
performFetch();
}
private void performFetch()
{
request?.Cancel();
request = new GetNewsRequest(lastCursor);
request.Success += response => Schedule(() => onSuccess(response));
api.PerformAsync(request);
}
private CancellationTokenSource cancellationToken;
private void onSuccess(GetNewsResponse response)
{
cancellationToken?.Cancel();
lastCursor = response.Cursor;
var flow = new FillFlowContainer<NewsCard>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = response.NewsPosts.Select(p => new NewsCard(p)).ToList()
};
LoadComponentAsync(flow, loaded =>
{
content.Add(loaded);
showMore.IsLoading = false;
showMore.Show();
}, (cancellationToken = new CancellationTokenSource()).Token);
}
protected override void Dispose(bool isDisposing)
{
request?.Cancel();
cancellationToken?.Cancel();
base.Dispose(isDisposing);
}
}
}

View File

@ -1,174 +0,0 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
namespace osu.Game.Overlays.News
{
public class NewsArticleCover : Container
{
private const int hover_duration = 300;
private readonly Box gradient;
public NewsArticleCover(ArticleInfo info)
{
RelativeSizeAxes = Axes.X;
Masking = true;
CornerRadius = 4;
NewsBackground bg;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f))
},
new DelayedLoadWrapper(bg = new NewsBackground(info.CoverUrl)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Alpha = 0
})
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
},
gradient = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.7f)),
Alpha = 0
},
new DateContainer(info.Time)
{
Margin = new MarginPadding
{
Right = 20,
Top = 20,
}
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding
{
Left = 25,
Bottom = 50,
},
Font = OsuFont.GetFont(Typeface.Torus, 24, FontWeight.Bold),
Text = info.Title,
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding
{
Left = 25,
Bottom = 30,
},
Font = OsuFont.GetFont(Typeface.Torus, 16, FontWeight.Bold),
Text = "by " + info.Author
}
};
bg.OnLoadComplete += d => d.FadeIn(250, Easing.In);
}
protected override bool OnHover(HoverEvent e)
{
gradient.FadeIn(hover_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
gradient.FadeOut(hover_duration, Easing.OutQuint);
}
[LongRunningLoad]
private class NewsBackground : Sprite
{
private readonly string url;
public NewsBackground(string coverUrl)
{
url = coverUrl ?? "Headers/news";
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore store)
{
Texture = store.Get(url);
}
}
private class DateContainer : Container, IHasTooltip
{
private readonly DateTime date;
public DateContainer(DateTime date)
{
this.date = date;
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
Masking = true;
CornerRadius = 4;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Bold, false, false),
Text = date.ToString("d MMM yyy").ToUpper(),
Margin = new MarginPadding
{
Vertical = 4,
Horizontal = 8,
}
}
};
}
public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper();
}
// fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now
public class ArticleInfo
{
public string Title { get; set; }
public string CoverUrl { get; set; }
public DateTime Time { get; set; }
public string Author { get; set; }
}
}
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -11,17 +12,17 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Platform;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Overlays.News namespace osu.Game.Overlays.News
{ {
public class NewsCard : CompositeDrawable public class NewsCard : OsuHoverContainer
{ {
[Resolved] protected override IEnumerable<Drawable> EffectTargets => new[] { background };
private OverlayColourProvider colourProvider { get; set; }
private readonly APINewsPost post; private readonly APINewsPost post;
@ -31,24 +32,28 @@ namespace osu.Game.Overlays.News
public NewsCard(APINewsPost post) public NewsCard(APINewsPost post)
{ {
this.post = post; this.post = post;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;
Masking = true; Masking = true;
CornerRadius = 6; CornerRadius = 6;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider, GameHost host)
{
if (post.Slug != null)
{
TooltipText = "view in browser";
Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug);
}
NewsBackground bg; NewsBackground bg;
AddRange(new Drawable[]
InternalChildren = new Drawable[]
{ {
background = new Box background = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both
Colour = colourProvider.Background4
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -104,9 +109,11 @@ namespace osu.Game.Overlays.News
} }
} }
} }
}, }
new HoverClickSounds() });
};
IdleColour = colourProvider.Background4;
HoverColour = colourProvider.Background3;
bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); bg.OnLoadComplete += d => d.FadeIn(250, Easing.In);
@ -116,18 +123,6 @@ namespace osu.Game.Overlays.News
main.AddText(post.Author, t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)); main.AddText(post.Author, t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold));
} }
protected override bool OnHover(HoverEvent e)
{
background.FadeColour(colourProvider.Background3, 200, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
background.FadeColour(colourProvider.Background4, 200, Easing.OutQuint);
base.OnHoverLost(e);
}
[LongRunningLoad] [LongRunningLoad]
private class NewsBackground : Sprite private class NewsBackground : Sprite
{ {
@ -193,6 +188,8 @@ namespace osu.Game.Overlays.News
} }
}; };
} }
protected override bool OnClick(ClickEvent e) => true; // Protects the NewsCard from clicks while hovering DateContainer
} }
} }
} }

View File

@ -1,19 +0,0 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.News
{
public abstract class NewsContent : FillFlowContainer
{
protected NewsContent()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Padding = new MarginPadding { Bottom = 100, Top = 20, Horizontal = 50 };
}
}
}

View File

@ -1,9 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using System;
namespace osu.Game.Overlays.News namespace osu.Game.Overlays.News
{ {
@ -11,24 +11,28 @@ namespace osu.Game.Overlays.News
{ {
private const string front_page_string = "frontpage"; private const string front_page_string = "frontpage";
public readonly Bindable<string> Post = new Bindable<string>(null);
public Action ShowFrontPage; public Action ShowFrontPage;
private readonly Bindable<string> article = new Bindable<string>(null);
public NewsHeader() public NewsHeader()
{ {
TabControl.AddItem(front_page_string); TabControl.AddItem(front_page_string);
Current.ValueChanged += e => Current.BindValueChanged(e =>
{ {
if (e.NewValue == front_page_string) if (e.NewValue == front_page_string)
ShowFrontPage?.Invoke(); ShowFrontPage?.Invoke();
}; });
Post.ValueChanged += showPost; article.BindValueChanged(onArticleChanged, true);
} }
private void showPost(ValueChangedEvent<string> e) public void SetFrontPage() => article.Value = null;
public void SetArticle(string slug) => article.Value = slug;
private void onArticleChanged(ValueChangedEvent<string> e)
{ {
if (e.OldValue != null) if (e.OldValue != null)
TabControl.RemoveItem(e.OldValue); TabControl.RemoveItem(e.OldValue);

View File

@ -7,18 +7,20 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.News; using osu.Game.Overlays.News;
using osu.Game.Overlays.News.Displays;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class NewsOverlay : FullscreenOverlay public class NewsOverlay : FullscreenOverlay
{ {
private readonly Bindable<string> article = new Bindable<string>(null);
private Container content;
private LoadingLayer loading;
private NewsHeader header; private NewsHeader header;
private OverlayScrollContainer scrollFlow;
private Container<NewsContent> content;
public readonly Bindable<string> Current = new Bindable<string>(null);
public NewsOverlay() public NewsOverlay()
: base(OverlayColourScheme.Purple) : base(OverlayColourScheme.Purple)
@ -26,18 +28,19 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load()
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.PurpleDarkAlternative Colour = ColourProvider.Background5,
}, },
new OverlayScrollContainer scrollFlow = new OverlayScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new FillFlowContainer Child = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -49,7 +52,7 @@ namespace osu.Game.Overlays
{ {
ShowFrontPage = ShowFrontPage ShowFrontPage = ShowFrontPage
}, },
content = new Container<NewsContent> content = new Container
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
@ -57,31 +60,60 @@ namespace osu.Game.Overlays
}, },
}, },
}, },
loading = new LoadingLayer(content),
}; };
header.Post.BindTo(Current);
Current.TriggerChange();
} }
private CancellationTokenSource loadContentCancellation; protected override void LoadComplete()
protected void LoadAndShowContent(NewsContent newContent)
{ {
content.FadeTo(0.2f, 300, Easing.OutQuint); base.LoadComplete();
article.BindValueChanged(onArticleChanged, true);
loadContentCancellation?.Cancel();
LoadComponentAsync(newContent, c =>
{
content.Child = c;
content.FadeIn(300, Easing.OutQuint);
}, (loadContentCancellation = new CancellationTokenSource()).Token);
} }
public void ShowFrontPage() public void ShowFrontPage()
{ {
Current.Value = null; article.Value = null;
Show(); Show();
} }
public void ShowArticle(string slug)
{
article.Value = slug;
Show();
}
private CancellationTokenSource cancellationToken;
private void onArticleChanged(ValueChangedEvent<string> e)
{
cancellationToken?.Cancel();
loading.Show();
if (e.NewValue == null)
{
header.SetFrontPage();
LoadDisplay(new FrontPageDisplay());
return;
}
header.SetArticle(e.NewValue);
LoadDisplay(Empty());
}
protected void LoadDisplay(Drawable display)
{
scrollFlow.ScrollToStart();
LoadComponentAsync(display, loaded =>
{
content.Child = loaded;
loading.Hide();
}, (cancellationToken = new CancellationTokenSource()).Token);
}
protected override void Dispose(bool isDisposing)
{
cancellationToken?.Cancel();
base.Dispose(isDisposing);
}
} }
} }

View File

@ -69,6 +69,7 @@ namespace osu.Game.Overlays.Toolbar
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
new ToolbarNewsButton(),
new ToolbarChangelogButton(), new ToolbarChangelogButton(),
new ToolbarRankingsButton(), new ToolbarRankingsButton(),
new ToolbarBeatmapListingButton(), new ToolbarBeatmapListingButton(),

View File

@ -0,0 +1,22 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarNewsButton : ToolbarOverlayToggleButton
{
public ToolbarNewsButton()
{
Icon = FontAwesome.Solid.Newspaper;
}
[BackgroundDependencyLoader(true)]
private void load(NewsOverlay news)
{
StateContainer = news;
}
}
}