mirror of
https://github.com/osukey/osukey.git
synced 2025-05-29 01:17:35 +09:00
timeline works💪
This commit is contained in:
parent
387f434257
commit
dd81505982
@ -11,6 +11,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Misskey.Users.Drawables
|
||||
{
|
||||
@ -35,7 +36,7 @@ namespace osu.Game.Misskey.Users.Drawables
|
||||
set => clickableArea.TooltipText = value ? (user?.Username ?? string.Empty) : default_tooltip_text;
|
||||
}
|
||||
|
||||
private readonly I user;
|
||||
private readonly User user;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuGame game { get; set; }
|
||||
@ -47,7 +48,7 @@ namespace osu.Game.Misskey.Users.Drawables
|
||||
/// If <see cref="OpenOnClick"/> is <c>true</c>, clicking will open the user's profile.
|
||||
/// </summary>
|
||||
/// <param name="user">The user. A null value will get a placeholder avatar.</param>
|
||||
public ClickableAvatar(I user = null)
|
||||
public ClickableAvatar(User user = null)
|
||||
{
|
||||
this.user = user;
|
||||
|
||||
@ -66,6 +67,11 @@ namespace osu.Game.Misskey.Users.Drawables
|
||||
LoadComponentAsync(new DrawableAvatar(user), clickableArea.Add);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
CornerRadius = DrawHeight / 2;
|
||||
}
|
||||
|
||||
private void openProfile()
|
||||
{
|
||||
// if (!string.IsNullOrEmpty(user?.Username))
|
||||
|
@ -9,19 +9,20 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Misskey.Users.Drawables
|
||||
{
|
||||
[LongRunningLoad]
|
||||
public partial class DrawableAvatar : Sprite
|
||||
{
|
||||
private readonly I user;
|
||||
private readonly User user;
|
||||
|
||||
/// <summary>
|
||||
/// A simple, non-interactable avatar sprite for the specified user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user. A null value will get a placeholder avatar.</param>
|
||||
public DrawableAvatar(I user = null)
|
||||
public DrawableAvatar(User user = null)
|
||||
{
|
||||
this.user = user;
|
||||
|
||||
|
@ -8,15 +8,16 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Misskey.Users.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// An avatar which can update to a new user when needed.
|
||||
/// </summary>
|
||||
public partial class UpdateableAvatar : ModelBackedDrawable<I>
|
||||
public partial class UpdateableAvatar : ModelBackedDrawable<User>
|
||||
{
|
||||
public I User
|
||||
public User User
|
||||
{
|
||||
get => Model;
|
||||
set => Model = value;
|
||||
@ -59,7 +60,7 @@ namespace osu.Game.Misskey.Users.Drawables
|
||||
/// <param name="isInteractive">If set to true, hover/click sounds will play and clicking the avatar will open the user's profile.</param>
|
||||
/// <param name="showUsernameTooltip">Whether to show the username rather than "view profile" on the tooltip. (note: this only applies if <paramref name="isInteractive"/> is also true)</param>
|
||||
/// <param name="showGuestOnNull">Whether to show a default guest representation on null user (as opposed to nothing).</param>
|
||||
public UpdateableAvatar(I user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true)
|
||||
public UpdateableAvatar(User user = null, bool isInteractive = true, bool showUsernameTooltip = false, bool showGuestOnNull = true)
|
||||
{
|
||||
this.isInteractive = isInteractive;
|
||||
this.showUsernameTooltip = showUsernameTooltip;
|
||||
@ -68,7 +69,7 @@ namespace osu.Game.Misskey.Users.Drawables
|
||||
User = user;
|
||||
}
|
||||
|
||||
protected override Drawable CreateDrawable(I user)
|
||||
protected override Drawable CreateDrawable(User user)
|
||||
{
|
||||
if (user == null && !showGuestOnNull)
|
||||
return null;
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Misskey.Users.Drawables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Misskey.Users
|
||||
{
|
||||
@ -29,7 +30,7 @@ namespace osu.Game.Misskey.Users
|
||||
private SpriteIcon statusIcon;
|
||||
private OsuSpriteText statusMessage;
|
||||
|
||||
protected ExtendedUserPanel(I user)
|
||||
protected ExtendedUserPanel(User user)
|
||||
: base(user)
|
||||
{
|
||||
}
|
||||
|
@ -14,19 +14,20 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Misskey.Users
|
||||
{
|
||||
public partial class UserCoverBackground : ModelBackedDrawable<I>
|
||||
public partial class UserCoverBackground : ModelBackedDrawable<User>
|
||||
{
|
||||
public I User
|
||||
public User User
|
||||
{
|
||||
get => Model;
|
||||
set => Model = value;
|
||||
}
|
||||
|
||||
protected override Drawable CreateDrawable(I user) => new Cover(user);
|
||||
protected override Drawable CreateDrawable(User user) => new Cover(user);
|
||||
|
||||
protected override double LoadDelay => 300;
|
||||
|
||||
@ -41,9 +42,9 @@ namespace osu.Game.Misskey.Users
|
||||
[LongRunningLoad]
|
||||
private partial class Cover : CompositeDrawable
|
||||
{
|
||||
private readonly I user;
|
||||
private readonly User user;
|
||||
|
||||
public Cover(I user)
|
||||
public Cover(User user)
|
||||
{
|
||||
this.user = user;
|
||||
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osuTK;
|
||||
|
||||
@ -17,7 +18,7 @@ namespace osu.Game.Misskey.Users
|
||||
{
|
||||
private const int margin = 10;
|
||||
|
||||
public UserGridPanel(I user)
|
||||
public UserGridPanel(User user)
|
||||
: base(user)
|
||||
{
|
||||
Height = 120;
|
||||
|
@ -11,6 +11,7 @@ using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osuTK;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
|
||||
@ -18,7 +19,7 @@ namespace osu.Game.Misskey.Users
|
||||
{
|
||||
public partial class UserListPanel : ExtendedUserPanel
|
||||
{
|
||||
public UserListPanel(I user)
|
||||
public UserListPanel(User user)
|
||||
: base(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
@ -17,12 +17,13 @@ using osu.Game.Graphics.Containers;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Misskey.Users
|
||||
{
|
||||
public abstract partial class UserPanel : OsuClickableContainer, IHasContextMenu
|
||||
{
|
||||
public readonly I User;
|
||||
public readonly User User;
|
||||
|
||||
/// <summary>
|
||||
/// Perform an action in addition to showing the user's profile.
|
||||
@ -34,7 +35,7 @@ namespace osu.Game.Misskey.Users
|
||||
|
||||
protected Drawable Background { get; private set; }
|
||||
|
||||
protected UserPanel(I user)
|
||||
protected UserPanel(User user)
|
||||
: base(HoverSampleSet.Button)
|
||||
{
|
||||
if (user == null)
|
||||
|
@ -18,9 +18,12 @@ using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Misskey.Users;
|
||||
using osu.Game.Online.MisskeyAPI.Requests;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using User = osu.Game.Online.MisskeyAPI.Responses.Types.User;
|
||||
using UserActivity = osu.Game.Users.UserActivity;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
@ -44,10 +47,10 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
|
||||
private string password;
|
||||
|
||||
public IBindable<Requests.Responses.I> LocalUser => localUser;
|
||||
public IBindable<User> LocalUser => localUser;
|
||||
public IBindable<UserActivity> Activity => activity;
|
||||
|
||||
private Bindable<Requests.Responses.I> localUser { get; } = new Bindable<Requests.Responses.I>(createGuestUser());
|
||||
private Bindable<User> localUser { get; } = new Bindable<User>(createGuestUser());
|
||||
private Bindable<UserActivity> activity { get; } = new Bindable<UserActivity>();
|
||||
|
||||
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
|
||||
@ -108,7 +111,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
log.Add(@"Queueing a ping request");
|
||||
Queue(new Requests.I(AccessToken));
|
||||
Queue(new Requests.User(AccessToken));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -149,7 +152,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
}
|
||||
}
|
||||
|
||||
var userReq = new Requests.I(AccessToken);
|
||||
var userReq = new Requests.User(AccessToken);
|
||||
|
||||
userReq.Failure += ex =>
|
||||
{
|
||||
@ -428,7 +431,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
flushQueue();
|
||||
}
|
||||
|
||||
private static Requests.Responses.I createGuestUser() => new GuestUser();
|
||||
private static User createGuestUser() => new GuestUser();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
@ -439,7 +442,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
}
|
||||
}
|
||||
|
||||
internal class GuestUser : Requests.Responses.I
|
||||
internal class GuestUser : User
|
||||
{
|
||||
public GuestUser()
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
@ -85,7 +86,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
//// <summary>
|
||||
//// The currently logged in user. Note that this will only be populated during <see cref="Perform"/>.
|
||||
//// </summary>
|
||||
protected I User { get; private set; }
|
||||
protected User User { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked on successful completion of an API request.
|
||||
|
@ -7,6 +7,7 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
@ -17,7 +18,7 @@ namespace osu.Game.Online.MisskeyAPI
|
||||
/// The local user.
|
||||
/// This is not thread-safe and should be scheduled locally if consumed from a drawable component.
|
||||
/// </summary>
|
||||
IBindable<Requests.Responses.I> LocalUser { get; }
|
||||
IBindable<User> LocalUser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current user's activity.
|
||||
|
@ -8,12 +8,12 @@ using osu.Framework.IO.Network;
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests
|
||||
{
|
||||
|
||||
public class I : APIRequest<MisskeyAPI.Requests.Responses.I>
|
||||
public class User : APIRequest<MisskeyAPI.Responses.Types.User>
|
||||
{
|
||||
|
||||
private string i;
|
||||
|
||||
public I(string i)
|
||||
public User(string i)
|
||||
{
|
||||
this.i = i;
|
||||
}
|
||||
|
@ -13,17 +13,25 @@ namespace osu.Game.Online.MisskeyAPI.Requests.Notes
|
||||
{
|
||||
private string text;
|
||||
private string i;
|
||||
private string? cw;
|
||||
|
||||
public Create(string i, string Text)
|
||||
{
|
||||
this.text = Text;
|
||||
this.i = i;
|
||||
this.text = Text;
|
||||
}
|
||||
public Create(string i, string Text, string cw)
|
||||
{
|
||||
this.i = i;
|
||||
this.text = Text;
|
||||
this.cw = cw;
|
||||
}
|
||||
|
||||
private class ReqBody
|
||||
{
|
||||
public string? text;
|
||||
public string? i;
|
||||
public string? cw;
|
||||
};
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
@ -34,6 +42,10 @@ namespace osu.Game.Online.MisskeyAPI.Requests.Notes
|
||||
text = text,
|
||||
i = i
|
||||
};
|
||||
if (cw != null)
|
||||
{
|
||||
body.cw = cw;
|
||||
}
|
||||
var json = JsonConvert.SerializeObject(body);
|
||||
Logger.Log(json, LoggingTarget.Network, LogLevel.Debug);
|
||||
req.AddRaw(json);
|
||||
|
@ -6,41 +6,46 @@ using System.Net.Http;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Notes
|
||||
{
|
||||
public class HybridTimeline : APIRequest<MisskeyAPI.Requests.Responses.Notes.HybridTimeline>
|
||||
public class HybridTimeline : APIRequest<Note[]>
|
||||
{
|
||||
private readonly string i;
|
||||
|
||||
private readonly string sinceId;
|
||||
private readonly string untilId;
|
||||
// private readonly string? sinceId;
|
||||
private readonly string? untilId;
|
||||
|
||||
public HybridTimeline(
|
||||
string i,
|
||||
string sinceId = "",
|
||||
string untilId = ""
|
||||
)
|
||||
{
|
||||
this.i = i;
|
||||
this.sinceId = sinceId;
|
||||
this.untilId = untilId;
|
||||
}
|
||||
|
||||
public HybridTimeline(string i)
|
||||
{
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Post;
|
||||
|
||||
var body = new Dictionary<string, string>
|
||||
var body = new Dictionary<string, object>
|
||||
{
|
||||
{ "i", i },
|
||||
{ "limit", 30 },
|
||||
};
|
||||
|
||||
if (sinceId != "")
|
||||
body.Add("sinceId", sinceId);
|
||||
if (untilId != "")
|
||||
// if (sinceId != null)
|
||||
// body.Add("sinceId", sinceId);
|
||||
if (untilId != null)
|
||||
body.Add("untilId", untilId);
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(body));
|
||||
|
@ -1,267 +0,0 @@
|
||||
// Copyright (c) sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses.Notes
|
||||
{
|
||||
public class Channel
|
||||
{
|
||||
}
|
||||
|
||||
public class Emoji
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
}
|
||||
|
||||
public class File
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("md5")]
|
||||
public string Md5 { get; set; }
|
||||
|
||||
[JsonProperty("size")]
|
||||
public int? Size { get; set; }
|
||||
|
||||
[JsonProperty("isSensitive")]
|
||||
public bool? IsSensitive { get; set; }
|
||||
|
||||
[JsonProperty("blurhash")]
|
||||
public string Blurhash { get; set; }
|
||||
|
||||
[JsonProperty("properties")]
|
||||
public Properties Properties { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("thumbnailUrl")]
|
||||
public string ThumbnailUrl { get; set; }
|
||||
|
||||
[JsonProperty("comment")]
|
||||
public string Comment { get; set; }
|
||||
|
||||
[JsonProperty("folderId")]
|
||||
public string FolderId { get; set; }
|
||||
|
||||
[JsonProperty("folder")]
|
||||
public Folder Folder { get; set; }
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
}
|
||||
|
||||
public class Folder
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("foldersCount")]
|
||||
public int? FoldersCount { get; set; }
|
||||
|
||||
[JsonProperty("filesCount")]
|
||||
public int? FilesCount { get; set; }
|
||||
|
||||
[JsonProperty("parentId")]
|
||||
public string ParentId { get; set; }
|
||||
|
||||
[JsonProperty("parent")]
|
||||
public Parent Parent { get; set; }
|
||||
}
|
||||
|
||||
public class MyReaction
|
||||
{
|
||||
}
|
||||
|
||||
public class Parent
|
||||
{
|
||||
}
|
||||
|
||||
public class Poll
|
||||
{
|
||||
}
|
||||
|
||||
public class Properties
|
||||
{
|
||||
[JsonProperty("width")]
|
||||
public int? Width { get; set; }
|
||||
|
||||
[JsonProperty("height")]
|
||||
public int? Height { get; set; }
|
||||
|
||||
[JsonProperty("orientation")]
|
||||
public int? Orientation { get; set; }
|
||||
|
||||
[JsonProperty("avgColor")]
|
||||
public string AvgColor { get; set; }
|
||||
}
|
||||
|
||||
public class Reactions
|
||||
{
|
||||
}
|
||||
|
||||
public class Renote
|
||||
{
|
||||
}
|
||||
|
||||
public class Reply
|
||||
{
|
||||
}
|
||||
|
||||
public class HybridTimeline
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
[JsonProperty("cw")]
|
||||
public string Cw { get; set; }
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonProperty("replyId")]
|
||||
public string ReplyId { get; set; }
|
||||
|
||||
[JsonProperty("renoteId")]
|
||||
public string RenoteId { get; set; }
|
||||
|
||||
[JsonProperty("reply")]
|
||||
public Reply Reply { get; set; }
|
||||
|
||||
[JsonProperty("renote")]
|
||||
public Renote Renote { get; set; }
|
||||
|
||||
[JsonProperty("isHidden")]
|
||||
public bool? IsHidden { get; set; }
|
||||
|
||||
[JsonProperty("visibility")]
|
||||
public string Visibility { get; set; }
|
||||
|
||||
[JsonProperty("mentions")]
|
||||
public List<string> Mentions { get; set; }
|
||||
|
||||
[JsonProperty("visibleUserIds")]
|
||||
public List<string> VisibleUserIds { get; set; }
|
||||
|
||||
[JsonProperty("fileIds")]
|
||||
public List<string> FileIds { get; set; }
|
||||
|
||||
[JsonProperty("files")]
|
||||
public List<File> Files { get; set; }
|
||||
|
||||
[JsonProperty("tags")]
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
[JsonProperty("poll")]
|
||||
public Poll Poll { get; set; }
|
||||
|
||||
[JsonProperty("channelId")]
|
||||
public string ChannelId { get; set; }
|
||||
|
||||
[JsonProperty("channel")]
|
||||
public Channel Channel { get; set; }
|
||||
|
||||
[JsonProperty("localOnly")]
|
||||
public bool? LocalOnly { get; set; }
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis { get; set; }
|
||||
|
||||
[JsonProperty("reactions")]
|
||||
public Reactions Reactions { get; set; }
|
||||
|
||||
[JsonProperty("renoteCount")]
|
||||
public int? RenoteCount { get; set; }
|
||||
|
||||
[JsonProperty("repliesCount")]
|
||||
public int? RepliesCount { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("myReaction")]
|
||||
public MyReaction MyReaction { get; set; }
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonProperty("host")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
public string AvatarUrl { get; set; }
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
public object AvatarBlurhash { get; set; }
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
public object AvatarColor { get; set; }
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool? IsAdmin { get; set; }
|
||||
|
||||
[JsonProperty("isModerator")]
|
||||
public bool? IsModerator { get; set; }
|
||||
|
||||
[JsonProperty("isBot")]
|
||||
public bool? IsBot { get; set; }
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool? IsCat { get; set; }
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis { get; set; }
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
public string OnlineStatus { get; set; }
|
||||
}
|
||||
}
|
@ -6,6 +6,9 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Misskey.Users;
|
||||
using UserStatusOnline = osu.Game.Users.UserStatusOnline;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Responses.Types
|
||||
{
|
||||
@ -79,7 +82,7 @@ namespace osu.Game.Online.MisskeyAPI.Responses.Types
|
||||
public class Choice
|
||||
{
|
||||
[JsonProperty("isVoted")]
|
||||
public bool Id { get; set; }
|
||||
public bool IsVoted { get; set; }
|
||||
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
@ -96,7 +99,7 @@ namespace osu.Game.Online.MisskeyAPI.Responses.Types
|
||||
public bool Multiple { get; set; }
|
||||
|
||||
[JsonProperty("choices")]
|
||||
public Choice Choices { get; set; }
|
||||
public Choice[] Choices { get; set; }
|
||||
}
|
||||
|
||||
public class Note // https://github.com/misskey-dev/misskey.js/blob/c89374c321aeb1cca2582922d4a9a9be059c691e/src/entities.ts#L128
|
||||
@ -715,6 +718,13 @@ namespace osu.Game.Online.MisskeyAPI.Responses.Types
|
||||
|
||||
public class UserLite // https://github.com/misskey-dev/misskey.js/blob/c89374c321aeb1cca2582922d4a9a9be059c691e/src/entities.ts#L9
|
||||
{
|
||||
/// <summary>
|
||||
/// A user ID which can be used to represent any system user which is not attached to a user profile.
|
||||
/// </summary>
|
||||
public const string SYSTEM_USER_ID = "system";
|
||||
|
||||
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
@ -725,6 +735,9 @@ namespace osu.Game.Online.MisskeyAPI.Responses.Types
|
||||
[CanBeNull]
|
||||
public string Host { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("onlineStatus")] // 'online' | 'active' | 'offline' | 'unknown'
|
||||
public string OnlineStatus { get; set; }
|
||||
|
||||
|
@ -28,6 +28,7 @@ using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using ExpandedContentScrollContainer = osu.Game.Screens.Misskey.Components.Note.Cards.ExpandedContentScrollContainer;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays.Rankings.Tables;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
using ExpandedContentScrollContainer = osu.Game.Screens.Misskey.Components.Note.Cards.ExpandedContentScrollContainer;
|
||||
|
||||
namespace osu.Game.Overlays.Rankings
|
||||
{
|
||||
|
@ -13,26 +13,30 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components
|
||||
{
|
||||
|
||||
[LongRunningLoad]
|
||||
public partial class Avatar : Sprite
|
||||
{
|
||||
// private readonly APIUser user;
|
||||
private Online.MisskeyAPI.Responses.Types.Note note;
|
||||
|
||||
/// <summary>
|
||||
/// A simple, non-interactable avatar sprite for the specified user.
|
||||
/// </summary>
|
||||
///// <param name="user">The user. A null value will get a placeholder avatar.</param>
|
||||
public Avatar()
|
||||
public Avatar(Online.MisskeyAPI.Responses.Types.Note note)
|
||||
{
|
||||
// this.user = user;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
// RelativeSizeAxes = Axes.Both;
|
||||
Size = new Vector2(10f);
|
||||
FillMode = FillMode.Fit;
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -43,7 +47,7 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
// // in remaining cases where this is required (chat tabs, local leaderboard), at which point this should be removed.
|
||||
// Texture = textures.Get(user.AvatarUrl ?? $@"https://a.ppy.sh/{user.Id}");
|
||||
|
||||
Texture ??= textures.Get(@"https://simkey.net/files/thumbnail-328eb27f-f06f-4454-ad52-a79d5f780a6b");
|
||||
Texture ??= textures.Get(note.User.AvatarUrl);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -0,0 +1,98 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class BeatmapCardDownloadProgressBar : CompositeDrawable
|
||||
{
|
||||
public IBindable<DownloadState> State => state;
|
||||
private readonly Bindable<DownloadState> state = new Bindable<DownloadState>();
|
||||
|
||||
public IBindable<double> Progress => progress;
|
||||
private readonly BindableDouble progress = new BindableDouble();
|
||||
|
||||
public override bool IsPresent => true;
|
||||
|
||||
private readonly CircularContainer foreground;
|
||||
|
||||
private readonly Box backgroundFill;
|
||||
private readonly Box foregroundFill;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
public BeatmapCardDownloadProgressBar()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = backgroundFill = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
},
|
||||
foreground = new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Child = foregroundFill = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
backgroundFill.Colour = colourProvider.Background6;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
state.BindValueChanged(_ => stateChanged(), true);
|
||||
progress.BindValueChanged(_ => progressChanged(), true);
|
||||
}
|
||||
|
||||
private void stateChanged()
|
||||
{
|
||||
switch (state.Value)
|
||||
{
|
||||
case DownloadState.Downloading:
|
||||
FinishTransforms(true);
|
||||
foregroundFill.Colour = colourProvider.Highlight1;
|
||||
break;
|
||||
|
||||
case DownloadState.Importing:
|
||||
foregroundFill.FadeColour(colours.Yellow, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void progressChanged()
|
||||
{
|
||||
foreground.ResizeWidthTo((float)progress.Value, progress.Value > 0 ? BeatmapCard.TRANSITION_DURATION : 0, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Game.Screens.Misskey.Components.Note.Cards.Buttons;
|
||||
using osu.Game.Screens.Misskey.Components.Note.Cards.Statistics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the current favourite state of a beatmap set.
|
||||
/// Used to coordinate between <see cref="FavouriteButton"/> and <see cref="FavouritesStatistic"/>.
|
||||
/// </summary>
|
||||
public readonly struct BeatmapSetFavouriteState
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the currently logged-in user has favourited this beatmap.
|
||||
/// </summary>
|
||||
public bool Favourited { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of favourites that the beatmap set has received, including the currently logged-in user.
|
||||
/// </summary>
|
||||
public int FavouriteCount { get; }
|
||||
|
||||
public BeatmapSetFavouriteState(bool favourited, int favouriteCount)
|
||||
{
|
||||
Favourited = favourited;
|
||||
FavouriteCount = favouriteCount;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Buttons
|
||||
{
|
||||
public abstract partial class BeatmapCardIconButton : OsuClickableContainer
|
||||
{
|
||||
private Colour4 idleColour;
|
||||
|
||||
public Colour4 IdleColour
|
||||
{
|
||||
get => idleColour;
|
||||
set
|
||||
{
|
||||
idleColour = value;
|
||||
if (IsLoaded)
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
private Colour4 hoverColour;
|
||||
|
||||
public Colour4 HoverColour
|
||||
{
|
||||
get => hoverColour;
|
||||
set
|
||||
{
|
||||
hoverColour = value;
|
||||
if (IsLoaded)
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
private float iconSize;
|
||||
|
||||
public float IconSize
|
||||
{
|
||||
get => iconSize;
|
||||
set
|
||||
{
|
||||
iconSize = value;
|
||||
Icon.Size = new Vector2(iconSize);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly SpriteIcon Icon;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private readonly Container content;
|
||||
private readonly Box hover;
|
||||
|
||||
protected BeatmapCardIconButton()
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.Centre;
|
||||
|
||||
base.Content.Add(content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
Scale = new Vector2(0.8f),
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
hover = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White.Opacity(0.1f),
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
Icon = new SpriteIcon
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Scale = new Vector2(1.2f),
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
IconSize = 12;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
IdleColour = colourProvider.Light1;
|
||||
HoverColour = colourProvider.Content1;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Enabled.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateState();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
bool isHovered = IsHovered && Enabled.Value;
|
||||
|
||||
hover.FadeTo(isHovered ? 1f : 0f, 500, Easing.OutQuint);
|
||||
content.ScaleTo(isHovered ? 1 : 0.8f, 500, Easing.OutQuint);
|
||||
Icon.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Buttons
|
||||
{
|
||||
public partial class DownloadButton : BeatmapCardIconButton
|
||||
{
|
||||
public Bindable<DownloadState> State { get; } = new Bindable<DownloadState>();
|
||||
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
|
||||
private Bindable<bool> preferNoVideo = null!;
|
||||
|
||||
private readonly LoadingSpinner spinner;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapModelDownloader beatmaps { get; set; } = null!;
|
||||
|
||||
public DownloadButton(APIBeatmapSet beatmapSet)
|
||||
{
|
||||
Icon.Icon = FontAwesome.Solid.Download;
|
||||
|
||||
Content.Add(spinner = new LoadingSpinner { Size = new Vector2(IconSize) });
|
||||
|
||||
this.beatmapSet = beatmapSet;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
preferNoVideo = config.GetBindable<bool>(OsuSetting.PreferNoVideo);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
preferNoVideo.BindValueChanged(_ => updateState());
|
||||
State.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.Unknown:
|
||||
Action = null;
|
||||
TooltipText = string.Empty;
|
||||
break;
|
||||
|
||||
case DownloadState.Downloading:
|
||||
case DownloadState.Importing:
|
||||
Action = null;
|
||||
TooltipText = string.Empty;
|
||||
spinner.Show();
|
||||
Icon.Hide();
|
||||
break;
|
||||
|
||||
case DownloadState.LocallyAvailable:
|
||||
Action = null;
|
||||
TooltipText = string.Empty;
|
||||
this.FadeOut(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case DownloadState.NotDownloaded:
|
||||
if (beatmapSet.Availability.DownloadDisabled)
|
||||
{
|
||||
Enabled.Value = false;
|
||||
TooltipText = BeatmapsetsStrings.AvailabilityDisabled;
|
||||
return;
|
||||
}
|
||||
|
||||
Action = () => beatmaps.Download(beatmapSet, preferNoVideo.Value);
|
||||
this.FadeIn(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
spinner.Hide();
|
||||
Icon.Show();
|
||||
|
||||
if (!beatmapSet.HasVideo)
|
||||
TooltipText = BeatmapsetsStrings.PanelDownloadAll;
|
||||
else
|
||||
TooltipText = preferNoVideo.Value ? BeatmapsetsStrings.PanelDownloadNoVideo : BeatmapsetsStrings.PanelDownloadVideo;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown {nameof(DownloadState)} specified.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Buttons
|
||||
{
|
||||
public partial class FavouriteButton : BeatmapCardIconButton, IHasCurrentValue<BeatmapSetFavouriteState>
|
||||
{
|
||||
private readonly BindableWithCurrent<BeatmapSetFavouriteState> current;
|
||||
|
||||
public Bindable<BeatmapSetFavouriteState> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
|
||||
private PostBeatmapFavouriteRequest favouriteRequest;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
public FavouriteButton(APIBeatmapSet beatmapSet)
|
||||
{
|
||||
current = new BindableWithCurrent<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount));
|
||||
this.beatmapSet = beatmapSet;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Action = toggleFavouriteStatus;
|
||||
current.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
private void toggleFavouriteStatus()
|
||||
{
|
||||
var actionType = current.Value.Favourited ? BeatmapFavouriteAction.UnFavourite : BeatmapFavouriteAction.Favourite;
|
||||
|
||||
favouriteRequest?.Cancel();
|
||||
favouriteRequest = new PostBeatmapFavouriteRequest(beatmapSet.OnlineID, actionType);
|
||||
|
||||
Enabled.Value = false;
|
||||
favouriteRequest.Success += () =>
|
||||
{
|
||||
bool favourited = actionType == BeatmapFavouriteAction.Favourite;
|
||||
|
||||
current.Value = new BeatmapSetFavouriteState(favourited, current.Value.FavouriteCount + (favourited ? 1 : -1));
|
||||
|
||||
Enabled.Value = true;
|
||||
};
|
||||
favouriteRequest.Failure += e =>
|
||||
{
|
||||
Logger.Error(e, $"Failed to {actionType.ToString().ToLowerInvariant()} beatmap: {e.Message}");
|
||||
Enabled.Value = true;
|
||||
};
|
||||
|
||||
api.Queue(favouriteRequest);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
if (current.Value.Favourited)
|
||||
{
|
||||
Icon.Icon = FontAwesome.Solid.Heart;
|
||||
TooltipText = BeatmapsetsStrings.ShowDetailsUnfavourite;
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon.Icon = FontAwesome.Regular.Heart;
|
||||
TooltipText = BeatmapsetsStrings.ShowDetailsFavourite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Buttons
|
||||
{
|
||||
public partial class GoToBeatmapButton : BeatmapCardIconButton
|
||||
{
|
||||
public IBindable<DownloadState> State => state;
|
||||
private readonly Bindable<DownloadState> state = new Bindable<DownloadState>();
|
||||
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
|
||||
public GoToBeatmapButton(APIBeatmapSet beatmapSet)
|
||||
{
|
||||
this.beatmapSet = beatmapSet;
|
||||
|
||||
Icon.Icon = FontAwesome.Solid.AngleDoubleRight;
|
||||
TooltipText = "Go to beatmap";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame? game)
|
||||
{
|
||||
Action = () => game?.PresentBeatmap(beatmapSet);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
state.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
this.FadeTo(state.Value == DownloadState.LocallyAvailable ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Buttons
|
||||
{
|
||||
public partial class PlayButton : OsuHoverContainer
|
||||
{
|
||||
public IBindable<double> Progress => progress;
|
||||
private readonly BindableDouble progress = new BindableDouble();
|
||||
|
||||
public BindableBool Playing { get; } = new BindableBool();
|
||||
|
||||
private readonly IBeatmapSetInfo beatmapSetInfo;
|
||||
|
||||
protected override IEnumerable<Drawable> EffectTargets => icon.Yield();
|
||||
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly LoadingSpinner loadingSpinner;
|
||||
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; } = null!;
|
||||
|
||||
private PreviewTrack? previewTrack;
|
||||
|
||||
public PlayButton(IBeatmapSetInfo beatmapSetInfo)
|
||||
{
|
||||
this.beatmapSetInfo = beatmapSetInfo;
|
||||
|
||||
Anchor = Origin = Anchor.Centre;
|
||||
|
||||
// needed for touch input to work when card is not hovered/expanded
|
||||
AlwaysPresent = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.Play,
|
||||
Size = new Vector2(14)
|
||||
},
|
||||
loadingSpinner = new LoadingSpinner
|
||||
{
|
||||
Size = new Vector2(14)
|
||||
}
|
||||
};
|
||||
|
||||
Action = () => Playing.Toggle();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
HoverColour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Playing.BindValueChanged(updateState, true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Playing.Value && previewTrack != null && previewTrack.TrackLoaded)
|
||||
progress.Value = previewTrack.CurrentTime / previewTrack.Length;
|
||||
else
|
||||
progress.Value = 0;
|
||||
}
|
||||
|
||||
private void updateState(ValueChangedEvent<bool> playing)
|
||||
{
|
||||
icon.Icon = playing.NewValue ? FontAwesome.Solid.Stop : FontAwesome.Solid.Play;
|
||||
|
||||
if (!playing.NewValue)
|
||||
{
|
||||
stopPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
if (previewTrack == null)
|
||||
{
|
||||
toggleLoading(true);
|
||||
|
||||
LoadComponentAsync(previewTrack = previewTrackManager.Get(beatmapSetInfo), onPreviewLoaded);
|
||||
}
|
||||
else
|
||||
tryStartPreview();
|
||||
}
|
||||
|
||||
private void stopPreview()
|
||||
{
|
||||
toggleLoading(false);
|
||||
Playing.Value = false;
|
||||
previewTrack?.Stop();
|
||||
}
|
||||
|
||||
private void onPreviewLoaded(PreviewTrack loadedPreview)
|
||||
{
|
||||
// Make sure that we schedule to after the next audio frame to fix crashes in single-threaded execution.
|
||||
// See: https://github.com/ppy/osu-framework/issues/4692
|
||||
Schedule(() =>
|
||||
{
|
||||
// another async load might have completed before this one.
|
||||
// if so, do not make any changes.
|
||||
if (loadedPreview != previewTrack)
|
||||
{
|
||||
loadedPreview.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
AddInternal(loadedPreview);
|
||||
toggleLoading(false);
|
||||
|
||||
loadedPreview.Stopped += () => Schedule(() => Playing.Value = false);
|
||||
|
||||
if (Playing.Value)
|
||||
tryStartPreview();
|
||||
});
|
||||
}
|
||||
|
||||
private void tryStartPreview()
|
||||
{
|
||||
if (previewTrack?.Start() == false)
|
||||
Playing.Value = false;
|
||||
}
|
||||
|
||||
private void toggleLoading(bool loading)
|
||||
{
|
||||
Enabled.Value = !loading;
|
||||
icon.FadeTo(loading ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
loadingSpinner.State.Value = loading ? Visibility.Visible : Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,10 +10,9 @@ using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public abstract partial class DrawableNoteCard : OsuClickableContainer
|
||||
{
|
||||
@ -24,38 +23,38 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
|
||||
public IBindable<bool> Expanded { get; }
|
||||
|
||||
public readonly APIBeatmapSet BeatmapSet;
|
||||
public readonly Online.MisskeyAPI.Responses.Types.Note Note;
|
||||
|
||||
protected readonly Bindable<BeatmapSetFavouriteState> FavouriteState;
|
||||
// protected readonly Bindable<BeatmapSetFavouriteState> FavouriteState;
|
||||
|
||||
protected abstract Drawable IdleContent { get; }
|
||||
protected abstract Drawable DownloadInProgressContent { get; }
|
||||
// protected abstract Drawable IdleContent { get; }
|
||||
// protected abstract Drawable DownloadInProgressContent { get; }
|
||||
|
||||
protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||
// protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||
|
||||
protected DrawableNoteCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
||||
protected DrawableNoteCard(Online.MisskeyAPI.Responses.Types.Note note, bool allowExpansion = true)
|
||||
: base(HoverSampleSet.Button)
|
||||
{
|
||||
Expanded = new BindableBool { Disabled = !allowExpansion };
|
||||
|
||||
BeatmapSet = beatmapSet;
|
||||
FavouriteState = new Bindable<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount));
|
||||
DownloadTracker = new BeatmapDownloadTracker(beatmapSet);
|
||||
Note = note;
|
||||
// FavouriteState = new Bindable<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(note.HasFavourited, note.FavouriteCount));
|
||||
// DownloadTracker = new BeatmapDownloadTracker(note);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(BeatmapSetOverlay? beatmapSetOverlay)
|
||||
{
|
||||
Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID);
|
||||
// Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(Note.OnlineID);
|
||||
|
||||
AddInternal(DownloadTracker);
|
||||
// AddInternal(DownloadTracker);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
DownloadTracker.State.BindValueChanged(_ => UpdateState());
|
||||
// DownloadTracker.State.BindValueChanged(_ => UpdateState());
|
||||
Expanded.BindValueChanged(_ => UpdateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
@ -74,24 +73,24 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
|
||||
protected virtual void UpdateState()
|
||||
{
|
||||
bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing;
|
||||
// bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing;
|
||||
|
||||
IdleContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint);
|
||||
DownloadInProgressContent.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
// IdleContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint);
|
||||
// DownloadInProgressContent.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a beatmap card of the given <paramref name="size"/> for the supplied <paramref name="beatmapSet"/>.
|
||||
/// Creates a beatmap card of the given <paramref name="size"/> for the supplied <paramref name="note"/>.
|
||||
/// </summary>
|
||||
public static BeatmapCard Create(APIBeatmapSet beatmapSet, BeatmapCardSize size, bool allowExpansion = true)
|
||||
public static NoteCard Create(Online.MisskeyAPI.Responses.Types.Note note, BeatmapCardSize size, bool allowExpansion = true)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case BeatmapCardSize.Normal:
|
||||
return new BeatmapCardNormal(beatmapSet, allowExpansion);
|
||||
return new NoteCardNormal(note, allowExpansion);
|
||||
|
||||
case BeatmapCardSize.Extra:
|
||||
return new BeatmapCardExtra(beatmapSet, allowExpansion);
|
||||
return new NoteCardNormal(note, allowExpansion); //todo: extra
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(size), size, @"Unsupported card size");
|
@ -0,0 +1,81 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class ExpandedContentScrollContainer : OsuScrollContainer
|
||||
{
|
||||
public const float HEIGHT = 200;
|
||||
|
||||
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new ExpandedContentScrollbar(direction);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Height = Math.Min(Content.DrawHeight, HEIGHT);
|
||||
ScrollbarVisible = allowScroll;
|
||||
}
|
||||
|
||||
private bool allowScroll => !Precision.AlmostEquals(DrawSize, Content.DrawSize);
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return false;
|
||||
|
||||
return base.OnDragStart(e);
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return;
|
||||
|
||||
base.OnDrag(e);
|
||||
}
|
||||
|
||||
protected override void OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return;
|
||||
|
||||
base.OnDragEnd(e);
|
||||
}
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e)
|
||||
{
|
||||
if (!allowScroll)
|
||||
return false;
|
||||
|
||||
return base.OnScroll(e);
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e) => true;
|
||||
|
||||
private partial class ExpandedContentScrollbar : OsuScrollbar
|
||||
{
|
||||
public ExpandedContentScrollbar(Direction scrollDir)
|
||||
: base(scrollDir)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
// do not handle hover, as handling hover would make the beatmap card's expanded content not-hovered
|
||||
// and therefore cause it to hide when trying to drag the scroll bar.
|
||||
// see: `BeatmapCardContent.dropdownContent` and its `Unhovered` handler.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class HoverHandlingContainer : Container
|
||||
{
|
||||
public Func<HoverEvent, bool>? Hovered { get; set; }
|
||||
public Action<HoverLostEvent>? Unhovered { get; set; }
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
bool handledByBase = base.OnHover(e);
|
||||
return Hovered?.Invoke(e) ?? handledByBase;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
Unhovered?.Invoke(e);
|
||||
}
|
||||
}
|
||||
}
|
57
osu.Game/Screens/Misskey/Components/Note/Cards/IconPill.cs
Normal file
57
osu.Game/Screens/Misskey/Components/Note/Cards/IconPill.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public abstract partial class IconPill : CircularContainer, IHasTooltip
|
||||
{
|
||||
public Vector2 IconSize
|
||||
{
|
||||
get => iconContainer.Size;
|
||||
set => iconContainer.Size = value;
|
||||
}
|
||||
|
||||
private readonly Container iconContainer;
|
||||
|
||||
protected IconPill(IconUsage icon)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
iconContainer = new Container
|
||||
{
|
||||
Size = new Vector2(22),
|
||||
Padding = new MarginPadding(5),
|
||||
Child = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = icon,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public abstract LocalisableString TooltipText { get; }
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public abstract partial class NoteCard : OsuClickableContainer
|
||||
{
|
||||
@ -24,38 +24,38 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
|
||||
public IBindable<bool> Expanded { get; }
|
||||
|
||||
public readonly APIBeatmapSet BeatmapSet;
|
||||
public readonly Online.MisskeyAPI.Responses.Types.Note Note;
|
||||
|
||||
protected readonly Bindable<BeatmapSetFavouriteState> FavouriteState;
|
||||
// protected readonly Bindable<BeatmapSetFavouriteState> FavouriteState;
|
||||
|
||||
protected abstract Drawable IdleContent { get; }
|
||||
protected abstract Drawable DownloadInProgressContent { get; }
|
||||
|
||||
protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||
// protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||
|
||||
protected NoteCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
||||
protected NoteCard(Online.MisskeyAPI.Responses.Types.Note note, bool allowExpansion = true)
|
||||
: base(HoverSampleSet.Button)
|
||||
{
|
||||
Expanded = new BindableBool { Disabled = !allowExpansion };
|
||||
|
||||
BeatmapSet = beatmapSet;
|
||||
FavouriteState = new Bindable<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount));
|
||||
DownloadTracker = new BeatmapDownloadTracker(beatmapSet);
|
||||
Note = note;
|
||||
// FavouriteState = new Bindable<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(note.HasFavourited, note.FavouriteCount));
|
||||
// DownloadTracker = new BeatmapDownloadTracker(note);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(BeatmapSetOverlay? beatmapSetOverlay)
|
||||
{
|
||||
Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID);
|
||||
// Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(Note.OnlineID);
|
||||
|
||||
AddInternal(DownloadTracker);
|
||||
// AddInternal(DownloadTracker);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
DownloadTracker.State.BindValueChanged(_ => UpdateState());
|
||||
// DownloadTracker.State.BindValueChanged(_ => UpdateState());
|
||||
Expanded.BindValueChanged(_ => UpdateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
@ -74,24 +74,24 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
|
||||
protected virtual void UpdateState()
|
||||
{
|
||||
bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing;
|
||||
|
||||
IdleContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint);
|
||||
DownloadInProgressContent.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
// bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing;
|
||||
//
|
||||
// IdleContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint);
|
||||
// DownloadInProgressContent.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a beatmap card of the given <paramref name="size"/> for the supplied <paramref name="beatmapSet"/>.
|
||||
/// Creates a beatmap card of the given <paramref name="size"/> for the supplied <paramref name="note"/>.
|
||||
/// </summary>
|
||||
public static BeatmapCard Create(APIBeatmapSet beatmapSet, BeatmapCardSize size, bool allowExpansion = true)
|
||||
public static NoteCard Create(Online.MisskeyAPI.Responses.Types.Note note, BeatmapCardSize size, bool allowExpansion = true)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case BeatmapCardSize.Normal:
|
||||
return new BeatmapCardNormal(beatmapSet, allowExpansion);
|
||||
return new NoteCardNormal(note, allowExpansion);
|
||||
|
||||
case BeatmapCardSize.Extra:
|
||||
return new BeatmapCardExtra(beatmapSet, allowExpansion);
|
||||
// case BeatmapCardSize.Extra:
|
||||
// return new BeatmapCardExtra(note, allowExpansion);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(size), size, @"Unsupported card size");
|
@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Beatmaps.Drawables.Cards.Buttons;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Misskey.Users.Drawables;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class NoteCardAvatar : Container
|
||||
{
|
||||
public BindableBool Dimmed { get; } = new BindableBool();
|
||||
|
||||
private Drawable avatar;
|
||||
|
||||
private readonly User user;
|
||||
|
||||
|
||||
public NoteCardAvatar(Online.MisskeyAPI.Responses.Types.Note note)
|
||||
{
|
||||
this.user = note.User;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
ClickableAvatar internalAvatar;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
avatar = new DelayedLoadWrapper(
|
||||
internalAvatar = new ClickableAvatar(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 50,
|
||||
// EdgeEffect = new EdgeEffectParameters
|
||||
// {
|
||||
// Type = EdgeEffectType.Shadow,
|
||||
// Radius = 1,
|
||||
// Colour = Color4.Black.Opacity(0.2f),
|
||||
// },
|
||||
})
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
// Size = new Vector2(HEIGHT - edge_margin * 2, HEIGHT - edge_margin * 2),
|
||||
}
|
||||
};
|
||||
// internalAvatar.OnLoadComplete += d => d.FadeInFromZero(200);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class NoteCardContent : CompositeDrawable
|
||||
{
|
||||
public Drawable MainContent
|
||||
{
|
||||
set => bodyContent.Child = value;
|
||||
}
|
||||
|
||||
public Drawable ExpandedContent
|
||||
{
|
||||
set => dropdownScroll.Child = value;
|
||||
}
|
||||
|
||||
public IBindable<bool> Expanded => expanded;
|
||||
|
||||
private readonly BindableBool expanded = new BindableBool();
|
||||
|
||||
private readonly Box background;
|
||||
private readonly Container content;
|
||||
private readonly Container bodyContent;
|
||||
private readonly Container dropdownContent;
|
||||
private readonly OsuScrollContainer dropdownScroll;
|
||||
private readonly Container borderContainer;
|
||||
|
||||
public NoteCardContent(float height)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChild = content = new HoverHandlingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
CornerRadius = NoteCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
Unhovered = _ => updateFromHoverChange(),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
bodyContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = height,
|
||||
CornerRadius = NoteCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
},
|
||||
dropdownContent = new HoverHandlingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = height },
|
||||
Alpha = 0,
|
||||
Hovered = _ =>
|
||||
{
|
||||
updateFromHoverChange();
|
||||
return true;
|
||||
},
|
||||
Unhovered = _ => updateFromHoverChange(),
|
||||
Child = dropdownScroll = new ExpandedContentScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
ScrollbarVisible = false
|
||||
}
|
||||
},
|
||||
borderContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = NoteCard.CORNER_RADIUS,
|
||||
Masking = true,
|
||||
BorderThickness = 3,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
background.Colour = Colour4.Gray;
|
||||
borderContainer.BorderColour = Colour4.White;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Expanded.BindValueChanged(_ => updateState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private ScheduledDelegate? scheduledExpandedChange;
|
||||
|
||||
public void ExpandAfterDelay() => queueExpandedStateChange(true, 100);
|
||||
|
||||
public void CancelExpand() => scheduledExpandedChange?.Cancel();
|
||||
|
||||
private void updateFromHoverChange() =>
|
||||
queueExpandedStateChange(content.IsHovered || dropdownContent.IsHovered, 100);
|
||||
|
||||
private void queueExpandedStateChange(bool newState, int delay = 0)
|
||||
{
|
||||
if (Expanded.Disabled)
|
||||
return;
|
||||
|
||||
scheduledExpandedChange?.Cancel();
|
||||
scheduledExpandedChange = Scheduler.AddDelayed(() => expanded.Value = newState, delay);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
// Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards.
|
||||
// This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left.
|
||||
this.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint);
|
||||
|
||||
background.FadeTo(Expanded.Value ? 1 : 0, NoteCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
dropdownContent.FadeTo(Expanded.Value ? 1 : 0, NoteCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
borderContainer.FadeTo(Expanded.Value ? 1 : 0, NoteCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
|
||||
content.TweenEdgeEffectTo(new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Offset = new Vector2(0, 2),
|
||||
Radius = 10,
|
||||
Colour = Colour4.Black.Opacity(Expanded.Value ? 0.3f : 0f),
|
||||
Hollow = true,
|
||||
}, NoteCard.TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,19 +7,20 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osuTK;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.Misskey.Components.Note.Cards.Statistics;
|
||||
using osu.Game.Skinning.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class NoteCardNormal : DrawableNoteCard
|
||||
public partial class NoteCardNormal : NoteCard
|
||||
{
|
||||
protected override Drawable IdleContent => idleBottomContent;
|
||||
protected override Drawable DownloadInProgressContent => downloadProgressBar;
|
||||
@ -27,23 +28,27 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
private const float height = 100;
|
||||
|
||||
[Cached]
|
||||
private readonly BeatmapCardContent content;
|
||||
private readonly NoteCardContent content;
|
||||
|
||||
private BeatmapCardThumbnail thumbnail = null!;
|
||||
private CollapsibleButtonContainer buttonContainer = null!;
|
||||
private NoteCardAvatar thumbnail = null!;
|
||||
// private CollapsibleButtonContainer buttonContainer = null!;
|
||||
|
||||
private FillFlowContainer<BeatmapCardStatistic> statisticsContainer = null!;
|
||||
// private FillFlowContainer<BeatmapCardStatistic> statisticsContainer = null!;
|
||||
private TextFlowContainer noteText = null!;
|
||||
|
||||
private FillFlowContainer idleBottomContent = null!;
|
||||
private BeatmapCardDownloadProgressBar downloadProgressBar = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public NoteCardNormal(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
||||
: base(beatmapSet, allowExpansion)
|
||||
// [Resolved]
|
||||
// private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
public NoteCardNormal(Online.MisskeyAPI.Responses.Types.Note note, bool allowExpansion = true)
|
||||
: base(note, allowExpansion)
|
||||
{
|
||||
content = new BeatmapCardContent(height);
|
||||
content = new NoteCardContent(height);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -56,6 +61,9 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
FillFlowContainer titleBadgeArea = null!;
|
||||
GridContainer artistContainer = null!;
|
||||
|
||||
// LinkFlowContainer titleText = null!;
|
||||
LinkFlowContainer artistText = null!;
|
||||
|
||||
Child = content.With(c =>
|
||||
{
|
||||
c.MainContent = new Container
|
||||
@ -63,11 +71,11 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
thumbnail = new BeatmapCardThumbnail(BeatmapSet)
|
||||
thumbnail = new NoteCardAvatar(Note)
|
||||
{
|
||||
Name = @"Left (icon) area",
|
||||
Size = new Vector2(height),
|
||||
Padding = new MarginPadding { Right = CORNER_RADIUS },
|
||||
Size = new Vector2(height - 10),
|
||||
// Padding = new MarginPadding { Right = CORNER_RADIUS },
|
||||
Child = leftIconArea = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding(5),
|
||||
@ -76,17 +84,19 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
Spacing = new Vector2(1)
|
||||
}
|
||||
},
|
||||
buttonContainer = new CollapsibleButtonContainer(BeatmapSet)
|
||||
{
|
||||
X = height - CORNER_RADIUS,
|
||||
Width = WIDTH - height + CORNER_RADIUS,
|
||||
FavouriteState = { BindTarget = FavouriteState },
|
||||
ButtonsCollapsedWidth = CORNER_RADIUS,
|
||||
ButtonsExpandedWidth = 30,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
// buttonContainer = new CollapsibleButtonContainer(Note)
|
||||
// {
|
||||
// X = height - CORNER_RADIUS,
|
||||
// Width = WIDTH - height + CORNER_RADIUS,
|
||||
// // FavouriteState = { BindTarget = FavouriteState },
|
||||
// ButtonsCollapsedWidth = CORNER_RADIUS,
|
||||
// ButtonsExpandedWidth = 30,
|
||||
// Children = new Drawable[]
|
||||
// {
|
||||
new FillFlowContainer
|
||||
{
|
||||
// X = height - CORNER_RADIUS,
|
||||
X = height,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
@ -108,12 +118,14 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
new OsuSpriteText()
|
||||
{
|
||||
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
|
||||
Text = new RomanisableString(Note.User.Name, Note.User.Username),
|
||||
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
// Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS },
|
||||
// AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
titleBadgeArea = new FillFlowContainer
|
||||
{
|
||||
@ -142,28 +154,28 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
artistText = new LinkFlowContainer()
|
||||
{
|
||||
Text = createArtistText(),
|
||||
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
||||
// Text = createArtistText(),
|
||||
// Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true
|
||||
// Truncate = true
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
Empty()
|
||||
},
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
noteText = new TextFlowContainer
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Alpha = 1,
|
||||
AlwaysPresent = true,
|
||||
}.With(flow =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Margin = new MarginPadding { Top = 2 };
|
||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||
d.AddUserLink(BeatmapSet.Author);
|
||||
}),
|
||||
flow.AddText(Note.Text, t => t.Font = OsuFont.Default.With(size: 15));
|
||||
})
|
||||
}
|
||||
},
|
||||
new Container
|
||||
@ -184,104 +196,97 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
ChildrenEnumerable = createStatistics()
|
||||
},
|
||||
new BeatmapCardExtraInfoRow(BeatmapSet)
|
||||
// statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
|
||||
// {
|
||||
// RelativeSizeAxes = Axes.X,
|
||||
// AutoSizeAxes = Axes.Y,
|
||||
// Direction = FillDirection.Horizontal,
|
||||
// Spacing = new Vector2(10, 0),
|
||||
// Alpha = 0,
|
||||
// AlwaysPresent = true,
|
||||
// ChildrenEnumerable = createStatistics()
|
||||
// },
|
||||
|
||||
}
|
||||
},
|
||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 6,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { BindTarget = DownloadTracker.State },
|
||||
Progress = { BindTarget = DownloadTracker.Progress }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
}
|
||||
};
|
||||
c.ExpandedContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
|
||||
Child = new BeatmapCardDifficultyList(BeatmapSet)
|
||||
};
|
||||
// c.ExpandedContent = new Container
|
||||
// {
|
||||
// RelativeSizeAxes = Axes.X,
|
||||
// AutoSizeAxes = Axes.Y,
|
||||
// Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
|
||||
// Child = new BeatmapCardDifficultyList(BeatmapSet)
|
||||
// };
|
||||
c.Expanded.BindTarget = Expanded;
|
||||
// titleText.AddLink(Note.User.Name, $"{api.APIEndpointUrl}@{Note.User.Username}@{Note.User.Host}");
|
||||
artistText.AddLink($"@{Note.User.Username}@{Note.User.Host}", $"{api.APIEndpointUrl}/@{Note.User.Username}@{Note.User.Host}");
|
||||
});
|
||||
|
||||
if (BeatmapSet.HasVideo)
|
||||
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||
// if (BeatmapSet.HasVideo)
|
||||
// leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||
//
|
||||
// if (BeatmapSet.HasStoryboard)
|
||||
// leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||
//
|
||||
// if (BeatmapSet.FeaturedInSpotlight)
|
||||
// {
|
||||
// titleBadgeArea.Add(new SpotlightBeatmapBadge
|
||||
// {
|
||||
// Anchor = Anchor.BottomRight,
|
||||
// Origin = Anchor.BottomRight,
|
||||
// Margin = new MarginPadding { Left = 5 }
|
||||
// });
|
||||
// }
|
||||
|
||||
if (BeatmapSet.HasStoryboard)
|
||||
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||
// if (BeatmapSet.HasExplicitContent)
|
||||
// {
|
||||
// titleBadgeArea.Add(new ExplicitContentBeatmapBadge
|
||||
// {
|
||||
// Anchor = Anchor.BottomRight,
|
||||
// Origin = Anchor.BottomRight,
|
||||
// Margin = new MarginPadding { Left = 5 }
|
||||
// });
|
||||
// }
|
||||
|
||||
if (BeatmapSet.FeaturedInSpotlight)
|
||||
{
|
||||
titleBadgeArea.Add(new SpotlightBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 5 }
|
||||
});
|
||||
}
|
||||
|
||||
if (BeatmapSet.HasExplicitContent)
|
||||
{
|
||||
titleBadgeArea.Add(new ExplicitContentBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 5 }
|
||||
});
|
||||
}
|
||||
|
||||
if (BeatmapSet.TrackId != null)
|
||||
{
|
||||
artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 5 }
|
||||
};
|
||||
}
|
||||
// if (BeatmapSet.TrackId != null)
|
||||
// {
|
||||
// artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge
|
||||
// {
|
||||
// Anchor = Anchor.BottomRight,
|
||||
// Origin = Anchor.BottomRight,
|
||||
// Margin = new MarginPadding { Left = 5 }
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
private LocalisableString createArtistText()
|
||||
{
|
||||
var romanisableArtist = new RomanisableString(BeatmapSet.ArtistUnicode, BeatmapSet.Artist);
|
||||
return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist);
|
||||
var romanisableArtist = new RomanisableString(Note.User.Username + "@" + Note.User.Host, Note.User.Username + "@" + Note.User.Host);
|
||||
return romanisableArtist;
|
||||
}
|
||||
|
||||
private IEnumerable<BeatmapCardStatistic> createStatistics()
|
||||
{
|
||||
var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet);
|
||||
if (hypesStatistic != null)
|
||||
yield return hypesStatistic;
|
||||
|
||||
var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet);
|
||||
if (nominationsStatistic != null)
|
||||
yield return nominationsStatistic;
|
||||
|
||||
yield return new FavouritesStatistic(BeatmapSet) { Current = FavouriteState };
|
||||
yield return new PlayCountStatistic(BeatmapSet);
|
||||
|
||||
var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet);
|
||||
if (dateStatistic != null)
|
||||
yield return dateStatistic;
|
||||
}
|
||||
// private IEnumerable<BeatmapCardStatistic> createStatistics()
|
||||
// {
|
||||
// var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet);
|
||||
// if (hypesStatistic != null)
|
||||
// yield return hypesStatistic;
|
||||
//
|
||||
// var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet);
|
||||
// if (nominationsStatistic != null)
|
||||
// yield return nominationsStatistic;
|
||||
//
|
||||
// yield return new FavouritesStatistic(BeatmapSet) { Current = FavouriteState };
|
||||
// yield return new PlayCountStatistic(BeatmapSet);
|
||||
//
|
||||
// var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet);
|
||||
// if (dateStatistic != null)
|
||||
// yield return dateStatistic;
|
||||
// }
|
||||
|
||||
protected override void UpdateState()
|
||||
{
|
||||
@ -289,10 +294,10 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
|
||||
bool showDetails = IsHovered || Expanded.Value;
|
||||
|
||||
buttonContainer.ShowDetails.Value = showDetails;
|
||||
// buttonContainer.ShowDetails.Value = showDetails;
|
||||
thumbnail.Dimmed.Value = showDetails;
|
||||
|
||||
statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
// statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// 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.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
public partial class BeatmapCardDateStatistic : BeatmapCardStatistic
|
||||
{
|
||||
private readonly DateTimeOffset dateTime;
|
||||
|
||||
private BeatmapCardDateStatistic(DateTimeOffset dateTime)
|
||||
{
|
||||
this.dateTime = dateTime;
|
||||
|
||||
Icon = FontAwesome.Regular.CheckCircle;
|
||||
Text = dateTime.ToLocalisableString(@"d MMM yyyy");
|
||||
}
|
||||
|
||||
public override object TooltipContent => dateTime;
|
||||
public override ITooltip GetCustomTooltip() => new DateTooltip();
|
||||
|
||||
public static BeatmapCardDateStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetInfo)
|
||||
{
|
||||
var displayDate = displayDateFor(beatmapSetInfo);
|
||||
|
||||
if (displayDate == null)
|
||||
return null;
|
||||
|
||||
return new BeatmapCardDateStatistic(displayDate.Value);
|
||||
}
|
||||
|
||||
private static DateTimeOffset? displayDateFor(IBeatmapSetOnlineInfo beatmapSetInfo)
|
||||
{
|
||||
// reference: https://github.com/ppy/osu-web/blob/ef432c11719fd1207bec5f9194b04f0033bdf02c/resources/assets/lib/beatmapset-panel.tsx#L36-L44
|
||||
switch (beatmapSetInfo.Status)
|
||||
{
|
||||
case BeatmapOnlineStatus.Ranked:
|
||||
case BeatmapOnlineStatus.Approved:
|
||||
case BeatmapOnlineStatus.Loved:
|
||||
case BeatmapOnlineStatus.Qualified:
|
||||
return beatmapSetInfo.Ranked;
|
||||
|
||||
default:
|
||||
return beatmapSetInfo.LastUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// A single statistic shown on a beatmap card.
|
||||
/// </summary>
|
||||
public abstract partial class BeatmapCardStatistic : CompositeDrawable, IHasTooltip, IHasCustomTooltip
|
||||
{
|
||||
protected IconUsage Icon
|
||||
{
|
||||
get => spriteIcon.Icon;
|
||||
set => spriteIcon.Icon = value;
|
||||
}
|
||||
|
||||
protected LocalisableString Text
|
||||
{
|
||||
get => spriteText.Text;
|
||||
set => spriteText.Text = value;
|
||||
}
|
||||
|
||||
public LocalisableString TooltipText { get; protected set; }
|
||||
|
||||
private readonly SpriteIcon spriteIcon;
|
||||
private readonly OsuSpriteText spriteText;
|
||||
|
||||
protected BeatmapCardStatistic()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
spriteIcon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(10),
|
||||
Margin = new MarginPadding { Top = 1 }
|
||||
},
|
||||
spriteText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.Default.With(size: 14)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
spriteIcon.Colour = colourProvider.Content2;
|
||||
}
|
||||
|
||||
#region Tooltip implementation
|
||||
|
||||
public virtual ITooltip GetCustomTooltip() => null;
|
||||
public virtual object TooltipContent => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Humanizer;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the number of favourites that a beatmap set has received.
|
||||
/// </summary>
|
||||
public partial class FavouritesStatistic : BeatmapCardStatistic, IHasCurrentValue<BeatmapSetFavouriteState>
|
||||
{
|
||||
private readonly BindableWithCurrent<BeatmapSetFavouriteState> current;
|
||||
|
||||
public Bindable<BeatmapSetFavouriteState> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
public FavouritesStatistic(IBeatmapSetOnlineInfo onlineInfo)
|
||||
{
|
||||
current = new BindableWithCurrent<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(onlineInfo.HasFavourited, onlineInfo.FavouriteCount));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
current.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
Icon = current.Value.Favourited ? FontAwesome.Solid.Heart : FontAwesome.Regular.Heart;
|
||||
Text = current.Value.FavouriteCount.ToMetric(decimals: 1);
|
||||
TooltipText = BeatmapsStrings.PanelFavourites(current.Value.FavouriteCount.ToLocalisableString(@"N0"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// 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.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the number of current hypes that a map has received, as well as the number of hypes required for nomination.
|
||||
/// </summary>
|
||||
public partial class HypesStatistic : BeatmapCardStatistic
|
||||
{
|
||||
private HypesStatistic(BeatmapSetHypeStatus hypeStatus)
|
||||
{
|
||||
Icon = FontAwesome.Solid.Bullhorn;
|
||||
Text = hypeStatus.Current.ToLocalisableString();
|
||||
TooltipText = BeatmapsStrings.HypeRequiredText(hypeStatus.Current.ToLocalisableString(), hypeStatus.Required.ToLocalisableString());
|
||||
}
|
||||
|
||||
public static HypesStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetOnlineInfo)
|
||||
=> beatmapSetOnlineInfo.HypeStatus == null ? null : new HypesStatistic(beatmapSetOnlineInfo.HypeStatus);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// 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.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the number of current nominations that a map has received, as well as the number of nominations required for qualification.
|
||||
/// </summary>
|
||||
public partial class NominationsStatistic : BeatmapCardStatistic
|
||||
{
|
||||
private NominationsStatistic(BeatmapSetNominationStatus nominationStatus)
|
||||
{
|
||||
Icon = FontAwesome.Solid.ThumbsUp;
|
||||
Text = nominationStatus.Current.ToLocalisableString();
|
||||
TooltipText = BeatmapsStrings.NominationsRequiredText(nominationStatus.Current.ToLocalisableString(), nominationStatus.Required.ToLocalisableString());
|
||||
}
|
||||
|
||||
public static NominationsStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetOnlineInfo)
|
||||
// web does not show nominations unless hypes are also present.
|
||||
// see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443
|
||||
=> beatmapSetOnlineInfo.HypeStatus == null || beatmapSetOnlineInfo.NominationStatus == null ? null : new NominationsStatistic(beatmapSetOnlineInfo.NominationStatus);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Humanizer;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the number of times the given beatmap set has been played.
|
||||
/// </summary>
|
||||
public partial class PlayCountStatistic : BeatmapCardStatistic
|
||||
{
|
||||
public PlayCountStatistic(IBeatmapSetOnlineInfo onlineInfo)
|
||||
{
|
||||
Icon = FontAwesome.Regular.PlayCircle;
|
||||
Text = onlineInfo.PlayCount.ToMetric(decimals: 1);
|
||||
TooltipText = BeatmapsStrings.PanelPlaycount(onlineInfo.PlayCount.ToLocalisableString(@"N0"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class StoryboardIconPill : IconPill
|
||||
{
|
||||
public StoryboardIconPill()
|
||||
: base(FontAwesome.Solid.Image)
|
||||
{
|
||||
}
|
||||
|
||||
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoStoryboard;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Misskey.Components.Note.Cards
|
||||
{
|
||||
public partial class VideoIconPill : IconPill
|
||||
{
|
||||
public VideoIconPill()
|
||||
: base(FontAwesome.Solid.Film)
|
||||
{
|
||||
}
|
||||
|
||||
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoVideo;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
@ -16,6 +17,7 @@ using osu.Game.Online.MisskeyAPI;
|
||||
using osu.Game.Misskey.Overlays.Settings;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Notes;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Overlays.OSD;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.Misskey;
|
||||
@ -26,6 +28,8 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
public partial class PostForm : FillFlowContainer
|
||||
{
|
||||
private OnScreenDisplay? onScreenDisplay { get; set; }
|
||||
[Resolved(CanBeNull = true)]
|
||||
private INotificationOverlay? notifications { get; set; }
|
||||
private partial class ResToast : Toast
|
||||
{
|
||||
public ResToast(string value, string desc)
|
||||
@ -57,11 +61,20 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
{
|
||||
textBox.Text = string.Empty;
|
||||
cwBox.Text = string.Empty;
|
||||
onScreenDisplay?.Display(new ResToast("投稿しました", createReq.CompletionState.ToString()));
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = "投稿しました",
|
||||
Icon = FontAwesome.Solid.Check,
|
||||
});
|
||||
};
|
||||
|
||||
api.Queue(createReq);
|
||||
onScreenDisplay?.Display(new ResToast("送信しています", ""));
|
||||
notifications?.Post(new SimpleNotification
|
||||
{
|
||||
Text = "送信しています",
|
||||
Icon = FontAwesome.Solid.PaperPlane,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
@ -128,15 +141,14 @@ namespace osu.Game.Screens.Misskey.Components
|
||||
}
|
||||
}
|
||||
},
|
||||
// new SettingsButton
|
||||
// {
|
||||
// Text = "Register",
|
||||
// Action = () =>
|
||||
// {
|
||||
// RequestHide?.Invoke();
|
||||
// accountCreation.Show();
|
||||
// }
|
||||
// }
|
||||
new SettingsButton
|
||||
{
|
||||
Text = "test",
|
||||
Action = () =>
|
||||
{
|
||||
onScreenDisplay?.Display(new ResToast("送信しています", ""));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// forgottenPaswordLink.AddLink(LayoutStrings.PopupLoginLoginForgot, $"https://simkey.net/about");
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Screens.Misskey
|
||||
public partial class MisskeyComponents : OsuScreen
|
||||
{
|
||||
private Container contentContainer;
|
||||
private Drawable avatar;
|
||||
// private Drawable avatar;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
@ -44,23 +44,23 @@ namespace osu.Game.Screens.Misskey
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
// RelativeSizeAxes = Axes.Both,
|
||||
// AutoSizeAxes = Axes.Y,
|
||||
Size = new Vector2(200),
|
||||
Masking = true,
|
||||
CornerRadius = 100,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
avatar = new DelayedLoadWrapper(
|
||||
new Avatar()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
})
|
||||
},
|
||||
}
|
||||
// new Container
|
||||
// {
|
||||
// // RelativeSizeAxes = Axes.Both,
|
||||
// // AutoSizeAxes = Axes.Y,
|
||||
// Size = new Vector2(200),
|
||||
// Masking = true,
|
||||
// CornerRadius = 100,
|
||||
// AutoSizeEasing = Easing.OutQuint,
|
||||
// Children = new Drawable[]
|
||||
// {
|
||||
// avatar = new DelayedLoadWrapper(
|
||||
// new Avatar()
|
||||
// {
|
||||
// RelativeSizeAxes = Axes.Both,
|
||||
// })
|
||||
// },
|
||||
// }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -98,14 +98,14 @@ namespace osu.Game.Screens.Misskey
|
||||
Text = "MisskeyLogin",
|
||||
Action = () => this.Push(new MisskeyLogin())
|
||||
},
|
||||
// new OsuButton()
|
||||
// {
|
||||
// Anchor = Anchor.Centre,
|
||||
// Origin = Anchor.Centre,
|
||||
// Size = new Vector2(500f, 50f),
|
||||
// Text = "Timeline",
|
||||
// Action = () => this.Push(new Timeline())
|
||||
// },
|
||||
new OsuButton()
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500f, 50f),
|
||||
Text = "Timeline",
|
||||
Action = () => this.Push(new Timeline())
|
||||
},
|
||||
new OsuButton()
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
303
osu.Game/Screens/Misskey/Timeline.cs
Normal file
303
osu.Game/Screens/Misskey/Timeline.cs
Normal file
@ -0,0 +1,303 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Notes;
|
||||
using osu.Game.Online.MisskeyAPI.Responses.Types;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.Misskey.Components;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using ExpandedContentScrollContainer = osu.Game.Screens.Misskey.Components.Note.Cards.ExpandedContentScrollContainer;
|
||||
using IAPIProvider = osu.Game.Online.MisskeyAPI.IAPIProvider;
|
||||
using NoteCard = osu.Game.Screens.Misskey.Components.Note.Cards.NoteCard;
|
||||
|
||||
namespace osu.Game.Screens.Misskey
|
||||
{
|
||||
public partial class Timeline : OsuScreen
|
||||
{
|
||||
// [Cached]
|
||||
// protected readonly OsuScrollContainer ScrollFlow;
|
||||
|
||||
protected readonly LoadingLayer Loading;
|
||||
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private OsuScrollContainer panelTarget;
|
||||
private FillFlowContainer<NoteCard> foundContent;
|
||||
|
||||
private int page = 0;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding(10f),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.1f)
|
||||
},
|
||||
panelTarget = new OsuScrollContainer()
|
||||
{
|
||||
Height = 750f,
|
||||
Width = 600f,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
// RelativeSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
Padding = new MarginPadding { Horizontal = 20 },
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private string lastNoteID;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
var req = new HybridTimeline(api.AccessToken);
|
||||
req.Success += res =>
|
||||
{
|
||||
onSearchFinished(res);
|
||||
lastNoteID = res.Last().Id;
|
||||
};
|
||||
onSearchStarted();
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
private CancellationTokenSource cancellationToken;
|
||||
|
||||
private Task panelLoadTask;
|
||||
|
||||
private void onSearchStarted()
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
// if (panelTarget.Any())
|
||||
// Loading.Show();
|
||||
}
|
||||
|
||||
private void onSearchFinished(Note[] searchResult)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
var newCards = createCardsFor(searchResult);
|
||||
|
||||
if (page == 0)
|
||||
{
|
||||
//No matches case
|
||||
if (!newCards.Any())
|
||||
{
|
||||
replaceResultsAreaContent(new NotFoundDrawable());
|
||||
return;
|
||||
}
|
||||
|
||||
var content = createCardContainerFor(newCards);
|
||||
|
||||
panelLoadTask = LoadComponentAsync(foundContent = content, replaceResultsAreaContent, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
// new results may contain beatmaps from a previous page,
|
||||
// this is dodgy but matches web behaviour for now.
|
||||
// see: https://github.com/ppy/osu-web/issues/9270
|
||||
// todo: replace custom equality compraer with ExceptBy in net6.0
|
||||
// newCards = newCards.ExceptBy(foundContent.Select(c => c.BeatmapSet.OnlineID), c => c.BeatmapSet.OnlineID);
|
||||
// newCards = newCards.Except(foundContent, BeatmapCardEqualityComparer.Default);
|
||||
|
||||
panelLoadTask = LoadComponentsAsync(newCards, loaded =>
|
||||
{
|
||||
lastFetchDisplayedTime = Time.Current;
|
||||
foundContent.AddRange(loaded);
|
||||
loaded.ForEach(p => p.FadeIn(200, Easing.OutQuint));
|
||||
}, (cancellationToken = new CancellationTokenSource()).Token);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<NoteCard> createCardsFor(Note[] notes) =>
|
||||
notes.Select(set =>
|
||||
NoteCard.Create(set, BeatmapCardSize.Normal).With(c =>
|
||||
{
|
||||
c.Anchor = Anchor.TopCentre;
|
||||
c.Origin = Anchor.TopCentre;
|
||||
})).ToArray();
|
||||
|
||||
private static ReverseChildIDFillFlowContainer<NoteCard> createCardContainerFor(IEnumerable<NoteCard> newCards)
|
||||
{
|
||||
// spawn new children with the contained so we only clear old content at the last moment.
|
||||
// reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most).
|
||||
var content = new ReverseChildIDFillFlowContainer<NoteCard>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(10),
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Top = 15,
|
||||
// the + 20 adjustment is roughly eyeballed in order to fit all of the expanded content height after it's scaled
|
||||
// as well as provide visual balance to the top margin.
|
||||
Bottom = ExpandedContentScrollContainer.HEIGHT + 20
|
||||
},
|
||||
ChildrenEnumerable = newCards
|
||||
};
|
||||
return content;
|
||||
}
|
||||
|
||||
private void replaceResultsAreaContent(Drawable content)
|
||||
{
|
||||
// Loading.Hide();
|
||||
lastFetchDisplayedTime = Time.Current;
|
||||
|
||||
panelTarget.Child = content;
|
||||
|
||||
content.FadeInFromZero();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancellationToken?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
public partial class NotFoundDrawable : CompositeDrawable
|
||||
{
|
||||
public NotFoundDrawable()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 250;
|
||||
Alpha = 0;
|
||||
Margin = new MarginPadding { Top = 15 };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LargeTextureStore textures)
|
||||
{
|
||||
AddInternal(new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Texture = textures.Get(@"Online/not-found")
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = BeatmapsStrings.ListingSearchNotFoundQuote,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: localisation requires Text/LinkFlowContainer support for localising strings with links inside
|
||||
// (https://github.com/ppy/osu-framework/issues/4530)
|
||||
|
||||
private const double time_between_fetches = 500;
|
||||
|
||||
private double lastFetchDisplayedTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
const int pagination_scroll_distance = 200;
|
||||
|
||||
bool shouldShowMore = panelLoadTask?.IsCompleted != false
|
||||
&& Time.Current - lastFetchDisplayedTime > time_between_fetches
|
||||
&& (panelTarget.ScrollableExtent > 0 && panelTarget.IsScrolledToEnd(pagination_scroll_distance));
|
||||
|
||||
if (shouldShowMore)
|
||||
FetchNextPage();
|
||||
}
|
||||
|
||||
private bool locked;
|
||||
private void FetchNextPage()
|
||||
{
|
||||
if (locked)
|
||||
return;
|
||||
var req = new HybridTimeline(api.AccessToken, lastNoteID);
|
||||
req.Success += res =>
|
||||
{
|
||||
lastNoteID = res.Last().Id;
|
||||
page++;
|
||||
onSearchFinished(res);
|
||||
locked = false;
|
||||
};
|
||||
api.Queue(req);
|
||||
locked = true;
|
||||
}
|
||||
|
||||
private class BeatmapCardEqualityComparer : IEqualityComparer<BeatmapCard>
|
||||
{
|
||||
public static BeatmapCardEqualityComparer Default { get; } = new BeatmapCardEqualityComparer();
|
||||
|
||||
public bool Equals(BeatmapCard x, BeatmapCard y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (ReferenceEquals(x, null)) return false;
|
||||
if (ReferenceEquals(y, null)) return false;
|
||||
|
||||
return x.BeatmapSet.Equals(y.BeatmapSet);
|
||||
}
|
||||
|
||||
public int GetHashCode(BeatmapCard obj) => obj.BeatmapSet.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -147,7 +147,7 @@ namespace osu.Game.Screens.Misskey
|
||||
Origin = Anchor.TopCentre,
|
||||
Margin = new MarginPadding(100),
|
||||
Width = 400f,
|
||||
Text = "Misskey.io は、地球で生まれた分散マイクロブログSNSです。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。\n暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。",
|
||||
Text = "Misskey.。Fediverse(様々なSNSで構成される宇宙)の中に存在するため、他のSNSと相互に繋がっています。\n暫し都会の喧騒から離れて、新しいインターネットにダイブしてみませんか。",
|
||||
Font = new FontUsage(size: 20),
|
||||
Colour = Colour4.White,
|
||||
},
|
||||
|
@ -45,4 +45,7 @@
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Online\MisskeyAPI\Responses\Notes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
x
Reference in New Issue
Block a user