1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 03:03:21 +08:00

Merge pull request #30793 from bdach/close-playlists

Add ability to close playlists within grace period after creation
This commit is contained in:
Dean Herbert 2024-11-29 22:37:32 +09:00 committed by GitHub
commit f56b2b9aef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 238 additions and 19 deletions

View File

@ -2,8 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
@ -14,6 +19,9 @@ namespace osu.Game.Tests.Visual.Playlists
{
public partial class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
{
[Resolved]
private IAPIProvider api { get; set; } = null!;
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
[Test]
@ -37,5 +45,29 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
AddAssert("status is still ended", () => roomScreen.Room.Status, Is.TypeOf<RoomStatusEnded>);
}
[Test]
public void TestCloseButtonGoesAwayAfterGracePeriod()
{
Room room = null!;
PlaylistsRoomSubScreen roomScreen = null!;
AddStep("create room", () =>
{
RoomManager.AddRoom(room = new Room
{
Name = @"Test Room",
Host = api.LocalUser.Value,
Category = RoomCategory.Normal,
StartDate = DateTimeOffset.Now.AddMinutes(-5).AddSeconds(3),
EndDate = DateTimeOffset.Now.AddMinutes(30)
});
});
AddStep("push screen", () => LoadScreen(roomScreen = new PlaylistsRoomSubScreen(room)));
AddUntilStep("wait for screen load", () => roomScreen.IsCurrentScreen());
AddAssert("close button present", () => roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
AddUntilStep("wait for close button to disappear", () => !roomScreen.ChildrenOfType<DangerousRoundedButton>().Any());
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Net.Http;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
public class ClosePlaylistRequest : APIRequest
{
private readonly long roomId;
public ClosePlaylistRequest(long roomId)
{
this.roomId = roomId;
}
protected override WebRequest CreateWebRequest()
{
var request = base.CreateWebRequest();
request.Method = HttpMethod.Delete;
return request;
}
protected override string Target => $@"rooms/{roomId}";
}
}

View File

@ -375,6 +375,7 @@ namespace osu.Game.Online.Rooms
Type = other.Type;
MaxParticipants = other.MaxParticipants;
ParticipantCount = other.ParticipantCount;
StartDate = other.StartDate;
EndDate = other.EndDate;
UserScore = other.UserScore;
QueueMode = other.QueueMode;

View File

@ -1,6 +1,7 @@
// 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 System.Collections.Generic;
using System.ComponentModel;
using osu.Framework.Allocation;
@ -22,9 +23,14 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Playlists;
using osuTK;
using osuTK.Graphics;
using Container = osu.Framework.Graphics.Containers.Container;
@ -48,6 +54,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved(canBeNull: true)]
private LoungeSubScreen? lounge { get; set; }
[Resolved]
private IDialogOverlay? dialogOverlay { get; set; }
[Resolved]
private IAPIProvider api { get; set; } = null!;
private readonly BindableWithCurrent<Room?> selectedRoom = new BindableWithCurrent<Room?>();
private Sample? sampleSelect;
private Sample? sampleJoin;
@ -144,13 +156,34 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
public Popover GetPopover() => new PasswordEntryPopover(Room);
public MenuItem[] ContextMenuItems => new MenuItem[]
public MenuItem[] ContextMenuItems
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
get
{
lounge?.OpenCopy(Room);
})
};
var items = new List<MenuItem>
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
{
lounge?.OpenCopy(Room);
})
};
if (Room.Type == MatchType.Playlists && Room.Host?.Id == api.LocalUser.Value.Id && Room.StartDate?.AddMinutes(5) >= DateTimeOffset.Now && Room.Status is not RoomStatusEnded)
{
items.Add(new OsuMenuItem("Close playlist", MenuItemType.Destructive, () =>
{
dialogOverlay?.Push(new ClosePlaylistDialog(Room, () =>
{
var request = new ClosePlaylistRequest(Room.RoomID!.Value);
request.Success += () => lounge?.RefreshRooms();
api.Queue(request);
}));
}));
}
return items.ToArray();
}
}
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{

View File

@ -379,6 +379,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
this.Push(CreateRoomSubScreen(room));
}
public void RefreshRooms() => ListingPollingComponent.PollImmediately();
private void updateLoadingLayer()
{
if (operationInProgress.Value || !ListingPollingComponent.InitialRoomsReceived.Value)

View File

@ -71,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
protected RulesetStore Rulesets { get; private set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
protected IAPIProvider API { get; private set; } = null!;
[Resolved(canBeNull: true)]
protected OnlinePlayScreen? ParentScreen { get; private set; }
@ -80,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
private PreviewTrackManager previewTrackManager { get; set; } = null!;
[Resolved(canBeNull: true)]
private IDialogOverlay? dialogOverlay { get; set; }
protected IDialogOverlay? DialogOverlay { get; private set; }
[Cached]
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
@ -282,7 +282,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
}
}
protected virtual bool IsConnected => api.State.Value == APIState.Online;
protected virtual bool IsConnected => API.State.Value == APIState.Online;
public override bool OnBackButton()
{
@ -361,17 +361,17 @@ namespace osu.Game.Screens.OnlinePlay.Match
bool hasUnsavedChanges = Room.RoomID == null && Room.Playlist.Count > 0;
if (dialogOverlay == null || !hasUnsavedChanges)
if (DialogOverlay == null || !hasUnsavedChanges)
return true;
// if the dialog is already displayed, block exiting until the user explicitly makes a decision.
if (dialogOverlay.CurrentDialog is ConfirmDiscardChangesDialog discardChangesDialog)
if (DialogOverlay.CurrentDialog is ConfirmDiscardChangesDialog discardChangesDialog)
{
discardChangesDialog.Flash();
return false;
}
dialogOverlay.Push(new ConfirmDiscardChangesDialog(() =>
DialogOverlay.Push(new ConfirmDiscardChangesDialog(() =>
{
ExitConfirmed = true;
settingsOverlay.Hide();

View File

@ -0,0 +1,19 @@
// 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.Game.Online.Rooms;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
public partial class ClosePlaylistDialog : DeletionDialog
{
public ClosePlaylistDialog(Room room, Action closeAction)
{
HeaderText = "Are you sure you want to close the following playlist:";
BodyText = room.Name;
DangerousAction = closeAction;
}
}
}

View File

@ -2,9 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Playlists
@ -12,22 +17,104 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public partial class PlaylistsRoomFooter : CompositeDrawable
{
public Action? OnStart;
public Action? OnClose;
private readonly Room room;
private DangerousRoundedButton closeButton = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
public PlaylistsRoomFooter(Room room)
{
this.room = room;
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new[]
InternalChild = new FillFlowContainer
{
new PlaylistsReadyButton(room)
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10),
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Size = new Vector2(600, 1),
Action = () => OnStart?.Invoke()
new PlaylistsReadyButton(room)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Size = new Vector2(600, 1),
Action = () => OnStart?.Invoke()
},
closeButton = new DangerousRoundedButton
{
Text = "Close",
Action = () => OnClose?.Invoke(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(120, 1),
Alpha = 0,
RelativeSizeAxes = Axes.Y,
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
room.PropertyChanged += onRoomChanged;
updateState();
}
private void hideCloseButton()
{
closeButton.ResizeWidthTo(0, 100, Easing.OutQuint)
.Then().FadeOut().Expire();
}
private void onRoomChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(Room.Status):
case nameof(Room.Host):
case nameof(Room.StartDate):
updateState();
break;
}
}
private void updateState()
{
TimeSpan? deletionGracePeriodRemaining = room.StartDate?.AddMinutes(5) - DateTimeOffset.Now;
if (room.Host?.Id == api.LocalUser.Value.Id)
{
if (deletionGracePeriodRemaining > TimeSpan.Zero && room.Status is not RoomStatusEnded)
{
closeButton.FadeIn();
using (BeginDelayedSequence(deletionGracePeriodRemaining.Value.TotalMilliseconds))
hideCloseButton();
}
else if (closeButton.Alpha > 0)
hideCloseButton();
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
room.PropertyChanged -= onRoomChanged;
}
}
}

View File

@ -1,6 +1,7 @@
// 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 System.ComponentModel;
using System.Diagnostics;
using System.Linq;
@ -13,7 +14,9 @@ using osu.Framework.Screens;
using osu.Game.Graphics.Cursor;
using osu.Game.Input;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Match.Components;
@ -259,7 +262,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
protected override Drawable CreateFooter() => new PlaylistsRoomFooter(Room)
{
OnStart = StartPlay
OnStart = StartPlay,
OnClose = closePlaylist,
};
protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new PlaylistsRoomSettingsOverlay(room)
@ -277,6 +281,20 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Logger.Log($"Polling adjusted (selection: {selectionPollingComponent.TimeBetweenPolls.Value})");
}
private void closePlaylist()
{
DialogOverlay?.Push(new ClosePlaylistDialog(Room, () =>
{
var request = new ClosePlaylistRequest(Room.RoomID!.Value);
request.Success += () =>
{
Room.Status = new RoomStatusEnded();
Room.EndDate = DateTimeOffset.UtcNow;
};
API.Queue(request);
}));
}
protected override Screen CreateGameplayScreen(PlaylistItem selectedItem)
{
return new PlayerLoader(() => new PlaylistsPlayer(Room, selectedItem)