mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-08-08 21:08:39 +00:00
Sort team picks by the time they were team picked (#980)
This commit is contained in:
parent
983b1e9984
commit
a2eaedc85b
16 changed files with 136 additions and 21 deletions
|
@ -61,7 +61,7 @@ public struct ApiSlot
|
||||||
MoveRequired = slot.MoveRequired,
|
MoveRequired = slot.MoveRequired,
|
||||||
FirstUploaded = slot.FirstUploaded,
|
FirstUploaded = slot.FirstUploaded,
|
||||||
LastUpdated = slot.LastUpdated,
|
LastUpdated = slot.LastUpdated,
|
||||||
TeamPick = slot.TeamPick,
|
TeamPick = slot.TeamPickTime != 0,
|
||||||
Location = slot.Location,
|
Location = slot.Location,
|
||||||
GameVersion = slot.GameVersion,
|
GameVersion = slot.GameVersion,
|
||||||
Plays = slot.Plays,
|
Plays = slot.Plays,
|
||||||
|
|
|
@ -31,7 +31,7 @@ public struct MinimalApiSlot
|
||||||
Type = slot.Type,
|
Type = slot.Type,
|
||||||
Name = slot.Name,
|
Name = slot.Name,
|
||||||
IconHash = slot.IconHash,
|
IconHash = slot.IconHash,
|
||||||
TeamPick = slot.TeamPick,
|
TeamPick = slot.TeamPickTime != 0,
|
||||||
IsAdventure = slot.IsAdventurePlanet,
|
IsAdventure = slot.IsAdventurePlanet,
|
||||||
Location = slot.Location,
|
Location = slot.Location,
|
||||||
GameVersion = slot.GameVersion,
|
GameVersion = slot.GameVersion,
|
||||||
|
|
|
@ -247,7 +247,8 @@ public class SlotsController : ControllerBase
|
||||||
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
pageData.TotalElements = await StatisticsHelper.SlotCount(this.database, queryBuilder);
|
||||||
|
|
||||||
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
SlotSortBuilder<SlotEntity> sortBuilder = new();
|
||||||
sortBuilder.AddSort(new LastUpdatedSort());
|
sortBuilder.AddSort(new TeamPickSort());
|
||||||
|
sortBuilder.AddSort(new FirstUploadedSort());
|
||||||
|
|
||||||
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
List<SlotBase> slots = await this.database.GetSlots(token, queryBuilder, pageData, sortBuilder);
|
||||||
|
|
||||||
|
|
|
@ -19,5 +19,5 @@ public class TeamPicksCategory : SlotCategory
|
||||||
|
|
||||||
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
public override IQueryable<SlotEntity> GetItems(DatabaseContext database, GameTokenEntity token, SlotQueryBuilder queryBuilder) =>
|
||||||
database.Slots.Where(queryBuilder.Clone().AddFilter(new TeamPickFilter()).Build())
|
database.Slots.Where(queryBuilder.Clone().AddFilter(new TeamPickFilter()).Build())
|
||||||
.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new FirstUploadedSort()));
|
.ApplyOrdering(new SlotSortBuilder<SlotEntity>().AddSort(new TeamPickSort()).AddSort(new FirstUploadedSort()));
|
||||||
}
|
}
|
|
@ -28,7 +28,7 @@ public class ModerationSlotController : ControllerBase
|
||||||
SlotEntity? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
SlotEntity? slot = await this.database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (slot == null) return this.NotFound();
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
slot.TeamPick = true;
|
slot.TeamPickTime = TimeHelper.TimestampMillis;
|
||||||
|
|
||||||
// Send webhook with slot.Name and slot.Creator.Username
|
// Send webhook with slot.Name and slot.Creator.Username
|
||||||
await WebhookHelper.SendWebhook("New Team Pick!", $"The level [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId}) by **{slot.Creator?.Username}** has been team picked");
|
await WebhookHelper.SendWebhook("New Team Pick!", $"The level [**{slot.Name}**]({ServerConfiguration.Instance.ExternalUrl}/slot/{slot.SlotId}) by **{slot.Creator?.Username}** has been team picked");
|
||||||
|
@ -51,7 +51,7 @@ public class ModerationSlotController : ControllerBase
|
||||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (slot == null) return this.NotFound();
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
slot.TeamPick = false;
|
slot.TeamPickTime = 0;
|
||||||
|
|
||||||
// Send a notification to the creator
|
// Send a notification to the creator
|
||||||
await this.database.SendNotification(slot.CreatorId,
|
await this.database.SendNotification(slot.CreatorId,
|
||||||
|
|
|
@ -41,8 +41,9 @@ public class LandingPage : BaseLayout
|
||||||
const int maxShownLevels = 5;
|
const int maxShownLevels = 5;
|
||||||
|
|
||||||
this.LatestTeamPicks = await this.Database.Slots.Where(s => s.Type == SlotType.User && !s.SubLevel && !s.Hidden)
|
this.LatestTeamPicks = await this.Database.Slots.Where(s => s.Type == SlotType.User && !s.SubLevel && !s.Hidden)
|
||||||
.Where(s => s.TeamPick)
|
.Where(s => s.TeamPickTime != 0)
|
||||||
.OrderByDescending(s => s.FirstUploaded)
|
.OrderByDescending(s => s.TeamPickTime)
|
||||||
|
.ThenByDescending(s => s.FirstUploaded)
|
||||||
.Take(maxShownLevels)
|
.Take(maxShownLevels)
|
||||||
.Include(s => s.Creator)
|
.Include(s => s.Creator)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
|
@ -217,9 +217,9 @@ else
|
||||||
<div class="ui green segment">
|
<div class="ui green segment">
|
||||||
<h2>Moderation Options</h2>
|
<h2>Moderation Options</h2>
|
||||||
|
|
||||||
@if (Model.Slot?.TeamPick ?? false)
|
@if ((Model.Slot?.TeamPickTime ?? 0) != 0)
|
||||||
{
|
{
|
||||||
<a href="/moderation/slot/@Model.Slot.SlotId/removeTeamPick">
|
<a href="/moderation/slot/@Model.Slot?.SlotId/removeTeamPick">
|
||||||
<div class="ui pink button">
|
<div class="ui pink button">
|
||||||
<i class="star icon"></i>
|
<i class="star icon"></i>
|
||||||
<span>Remove Team Pick</span>
|
<span>Remove Team Pick</span>
|
||||||
|
|
|
@ -495,4 +495,62 @@ public class SlotControllerTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Team Picks
|
||||||
|
[Fact]
|
||||||
|
public async Task TeamPick_ShouldOnlyIncludeTeamPickedLevels()
|
||||||
|
{
|
||||||
|
DatabaseContext db = await MockHelper.GetTestDatabase(new List<SlotEntity>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SlotId = 1,
|
||||||
|
CreatorId = 1,
|
||||||
|
TeamPickTime = 1,
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SlotId = 2,
|
||||||
|
CreatorId = 1,
|
||||||
|
TeamPickTime = 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
SlotsController controller = new(db);
|
||||||
|
controller.SetupTestController();
|
||||||
|
|
||||||
|
IActionResult result = await controller.TeamPickedSlots();
|
||||||
|
GenericSlotResponse slotResponse = result.CastTo<OkObjectResult, GenericSlotResponse>();
|
||||||
|
Assert.Single(slotResponse.Slots);
|
||||||
|
Assert.Equal(1, slotResponse.Slots.OfType<GameUserSlot>().First().SlotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TeamPick_LevelsAreSortedByTimestamp()
|
||||||
|
{
|
||||||
|
DatabaseContext db = await MockHelper.GetTestDatabase(new List<SlotEntity>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SlotId = 1,
|
||||||
|
CreatorId = 1,
|
||||||
|
TeamPickTime = 1,
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SlotId = 2,
|
||||||
|
CreatorId = 1,
|
||||||
|
TeamPickTime = 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
SlotsController controller = new(db);
|
||||||
|
controller.SetupTestController();
|
||||||
|
|
||||||
|
IActionResult result = await controller.TeamPickedSlots();
|
||||||
|
|
||||||
|
GenericSlotResponse slotResponse = result.CastTo<OkObjectResult, GenericSlotResponse>();
|
||||||
|
Assert.Equal(2, slotResponse.Slots.Count);
|
||||||
|
Assert.Equal(2, slotResponse.Slots.OfType<GameUserSlot>().First().SlotId);
|
||||||
|
Assert.Equal(1, slotResponse.Slots.OfType<GameUserSlot>().Last().SlotId);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class StatisticsControllerTests
|
||||||
{
|
{
|
||||||
SlotId = 3,
|
SlotId = 3,
|
||||||
CreatorId = 1,
|
CreatorId = 1,
|
||||||
TeamPick = true,
|
TeamPickTime = 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await using DatabaseContext db = await MockHelper.GetTestDatabase(slots);
|
await using DatabaseContext db = await MockHelper.GetTestDatabase(slots);
|
||||||
|
@ -90,7 +90,7 @@ public class StatisticsControllerTests
|
||||||
{
|
{
|
||||||
SlotId = 3,
|
SlotId = 3,
|
||||||
CreatorId = 1,
|
CreatorId = 1,
|
||||||
TeamPick = true,
|
TeamPickTime = 1,
|
||||||
GameVersion = GameVersion.LittleBigPlanet2,
|
GameVersion = GameVersion.LittleBigPlanet2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -130,7 +130,7 @@ public class StatisticsControllerTests
|
||||||
{
|
{
|
||||||
SlotId = 3,
|
SlotId = 3,
|
||||||
CreatorId = 1,
|
CreatorId = 1,
|
||||||
TeamPick = true,
|
TeamPickTime = 1,
|
||||||
GameVersion = GameVersion.LittleBigPlanet1,
|
GameVersion = GameVersion.LittleBigPlanet1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -168,7 +168,7 @@ public class StatisticsControllerTests
|
||||||
{
|
{
|
||||||
SlotId = 3,
|
SlotId = 3,
|
||||||
CreatorId = 1,
|
CreatorId = 1,
|
||||||
TeamPick = true,
|
TeamPickTime = 1,
|
||||||
GameVersion = GameVersion.LittleBigPlanet2,
|
GameVersion = GameVersion.LittleBigPlanet2,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -729,7 +729,7 @@ public class FilterTests
|
||||||
|
|
||||||
SlotEntity slot = new()
|
SlotEntity slot = new()
|
||||||
{
|
{
|
||||||
TeamPick = true,
|
TeamPickTime = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.True(teamPickFunc(slot));
|
Assert.True(teamPickFunc(slot));
|
||||||
|
@ -743,7 +743,7 @@ public class FilterTests
|
||||||
|
|
||||||
SlotEntity slot = new()
|
SlotEntity slot = new()
|
||||||
{
|
{
|
||||||
TeamPick = false,
|
TeamPickTime = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.False(teamPickFunc(slot));
|
Assert.False(teamPickFunc(slot));
|
||||||
|
|
|
@ -7,5 +7,5 @@ namespace LBPUnion.ProjectLighthouse.Filter.Filters;
|
||||||
|
|
||||||
public class TeamPickFilter : ISlotFilter
|
public class TeamPickFilter : ISlotFilter
|
||||||
{
|
{
|
||||||
public Expression<Func<SlotEntity, bool>> GetPredicate() => s => s.TeamPick;
|
public Expression<Func<SlotEntity, bool>> GetPredicate() => s => s.TeamPickTime != 0;
|
||||||
}
|
}
|
11
ProjectLighthouse/Filter/Sorts/TeamPickSort.cs
Normal file
11
ProjectLighthouse/Filter/Sorts/TeamPickSort.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||||
|
using LBPUnion.ProjectLighthouse.Types.Filter.Sorts;
|
||||||
|
|
||||||
|
namespace LBPUnion.ProjectLighthouse.Filter.Sorts;
|
||||||
|
|
||||||
|
public class TeamPickSort : ISlotSort
|
||||||
|
{
|
||||||
|
public Expression<Func<SlotEntity, dynamic>> GetExpression() => s => s.TeamPickTime;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using LBPUnion.ProjectLighthouse.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ProjectLighthouse.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DatabaseContext))]
|
||||||
|
[Migration("20240214031744_UpdateTeamPickBoolToTimestamp")]
|
||||||
|
public partial class UpdateTeamPickBoolToTimestamp : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<long>(name: "TeamPickTime",
|
||||||
|
table: "Slots",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.Sql("UPDATE `Slots` SET TeamPickTime = 1 WHERE TeamPick = 1");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TeamPick",
|
||||||
|
table: "Slots");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TeamPickTime",
|
||||||
|
table: "Slots");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "TeamPick",
|
||||||
|
table: "Slots",
|
||||||
|
type: "tinyint(1)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -472,8 +472,8 @@ namespace ProjectLighthouse.Migrations
|
||||||
b.Property<bool>("SubLevel")
|
b.Property<bool>("SubLevel")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<bool>("TeamPick")
|
b.Property<long>("TeamPickTime")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<int>("Type")
|
b.Property<int>("Type")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class SlotEntity
|
||||||
|
|
||||||
public long LastUpdated { get; set; }
|
public long LastUpdated { get; set; }
|
||||||
|
|
||||||
public bool TeamPick { get; set; }
|
public long TeamPickTime { get; set; }
|
||||||
|
|
||||||
public GameVersion GameVersion { get; set; }
|
public GameVersion GameVersion { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ public abstract class SlotBase : ILbpSerializable
|
||||||
InitiallyLocked = slot.InitiallyLocked,
|
InitiallyLocked = slot.InitiallyLocked,
|
||||||
RootLevel = slot.RootLevel,
|
RootLevel = slot.RootLevel,
|
||||||
IsShareable = slot.Shareable,
|
IsShareable = slot.Shareable,
|
||||||
IsTeamPicked = slot.TeamPick,
|
IsTeamPicked = slot.TeamPickTime != 0,
|
||||||
FirstUploaded = slot.FirstUploaded,
|
FirstUploaded = slot.FirstUploaded,
|
||||||
LastUpdated = slot.LastUpdated,
|
LastUpdated = slot.LastUpdated,
|
||||||
IsCrossControlRequired = slot.CrossControllerRequired,
|
IsCrossControlRequired = slot.CrossControllerRequired,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue