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:
commit
138afea431
@ -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",
|
||||||
|
53
osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs
Normal file
53
osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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: &)",
|
||||||
|
Preview = "boom (HTML entity: &)",
|
||||||
|
Author = "user (HTML entity: &)",
|
||||||
|
FirstImage = "https://assets.ppy.sh/artists/88/header.jpg",
|
||||||
|
PublishedAt = DateTimeOffset.Now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
27
osu.Game/Online/API/Requests/GetNewsRequest.cs
Normal file
27
osu.Game/Online/API/Requests/GetNewsRequest.cs
Normal 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";
|
||||||
|
}
|
||||||
|
}
|
15
osu.Game/Online/API/Requests/GetNewsResponse.cs
Normal file
15
osu.Game/Online/API/Requests/GetNewsResponse.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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()
|
||||||
|
114
osu.Game/Overlays/News/Displays/FrontPageDisplay.cs
Normal file
114
osu.Game/Overlays/News/Displays/FrontPageDisplay.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
22
osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs
Normal file
22
osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user