Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to close playlists within grace period after creation #30793

Merged
merged 10 commits into from
Nov 29, 2024
32 changes: 32 additions & 0 deletions osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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]
Expand All @@ -37,5 +45,29 @@ public void TestStatusUpdateOnEnter()
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());
}
}
}
27 changes: 27 additions & 0 deletions osu.Game/Online/API/Requests/ClosePlaylistRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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}";
}
}
1 change: 1 addition & 0 deletions osu.Game/Online/Rooms/Room.cs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ public void CopyFrom(Room other)
Type = other.Type;
MaxParticipants = other.MaxParticipants;
ParticipantCount = other.ParticipantCount;
StartDate = other.StartDate;
EndDate = other.EndDate;
UserScore = other.UserScore;
QueueMode = other.QueueMode;
Expand Down
43 changes: 38 additions & 5 deletions osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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;
Expand All @@ -22,9 +23,14 @@
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;
Expand All @@ -48,6 +54,12 @@ public required Bindable<Room?> SelectedRoom
[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;
Expand Down Expand Up @@ -144,13 +156,34 @@ public bool MatchingFilter

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)
{
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ protected virtual void OpenNewRoom(Room room)
this.Push(CreateRoomSubScreen(room));
}

public void RefreshRooms() => ListingPollingComponent.PollImmediately();

private void updateLoadingLayer()
{
if (operationInProgress.Value || !ListingPollingComponent.InitialRoomsReceived.Value)
Expand Down
12 changes: 6 additions & 6 deletions osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrack
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; }
Expand All @@ -80,7 +80,7 @@ public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrack
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();
Expand Down Expand Up @@ -282,7 +282,7 @@ private void updateSetupState()
}
}

protected virtual bool IsConnected => api.State.Value == APIState.Online;
protected virtual bool IsConnected => API.State.Value == APIState.Online;

public override bool OnBackButton()
{
Expand Down Expand Up @@ -361,17 +361,17 @@ private bool ensureExitConfirmed()

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();
Expand Down
19 changes: 19 additions & 0 deletions osu.Game/Screens/OnlinePlay/Playlists/ClosePlaylistDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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:";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hesitantly mentioned this in my other comment, but we could just expose this to the user as "deleting" and not require custom terminology for closing. I think to the user they expect to "delete" a playlist, not "close" it.. but I can swing both ways.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am neither here or there, fine with either personally.

BodyText = room.Name;
DangerousAction = closeAction;
}
}
}
101 changes: 94 additions & 7 deletions osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomFooter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,119 @@
// 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
{
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;
}
}
}
Loading
Loading