More room features.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Aiursoft.ChessServer.Attributes;
|
||||
|
||||
public class ValidNickName : ValidationAttribute
|
||||
{
|
||||
private readonly string _keyWordRegex = "^[-a-zA-Z0-9_]+$";
|
||||
|
||||
public override bool IsValid(object? value)
|
||||
{
|
||||
var regex = new Regex(_keyWordRegex, RegexOptions.Compiled);
|
||||
return value is string input && regex.IsMatch(input);
|
||||
}
|
||||
|
||||
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
|
||||
{
|
||||
return this.IsValid(value) ? ValidationResult.Success : new ValidationResult("The " + validationContext.DisplayName + " can only contain numbers, alphabet and underline.");
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,41 @@ public class HomeController : Controller
|
||||
_database = database;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
var model = new IndexViewModel(_database.Challenges);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Auto(Guid playerId)
|
||||
{
|
||||
// I created a challenge. Go to my challenge.
|
||||
var iHaveAChallenge = _database.Challenges.FirstOrDefault(t => t.Value.Creator.Id == playerId);
|
||||
if (iHaveAChallenge.Value != null)
|
||||
{
|
||||
return RedirectToAction(nameof(Challenge), new { id = iHaveAChallenge.Key, playerId });
|
||||
}
|
||||
|
||||
// Exists a public challenge. Go to that challenge.
|
||||
var otherChallenge = _database.Challenges.FirstOrDefault(t => t.Value.Permission == ChallengePermission.Public);
|
||||
if (otherChallenge.Value != null)
|
||||
{
|
||||
return RedirectToAction(nameof(Challenge), new { id = otherChallenge.Key, playerId });
|
||||
}
|
||||
|
||||
// Create a new challenge.
|
||||
return RedirectToAction(nameof(Create), new { playerId });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Create(Guid playerId)
|
||||
{
|
||||
var iHaveAChallenge = _database.Challenges.FirstOrDefault(t => t.Value.Creator.Id == playerId);
|
||||
if (iHaveAChallenge.Value != null)
|
||||
{
|
||||
return RedirectToAction(nameof(Room), new { id = iHaveAChallenge.Key });
|
||||
return RedirectToAction(nameof(Challenge), new { id = iHaveAChallenge.Key, playerId });
|
||||
}
|
||||
var model = new CreateChallengeViewModel();
|
||||
return View(model);
|
||||
@@ -38,6 +61,11 @@ public class HomeController : Controller
|
||||
[HttpPost]
|
||||
public IActionResult Create(CreateChallengeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var player = _database.GetOrAddPlayer(model.CreatorId);
|
||||
var challenge = new Challenge(player)
|
||||
{
|
||||
@@ -46,14 +74,38 @@ public class HomeController : Controller
|
||||
Permission = model.Permission,
|
||||
TimeLimit = model.TimeLimit,
|
||||
};
|
||||
var uniqueId = _counter.GetUniqueNo();
|
||||
_database.Challenges.TryAdd(uniqueId, challenge);
|
||||
return RedirectToAction(nameof(Room), new { id = uniqueId });
|
||||
var roomId = _counter.GetUniqueNo();
|
||||
_database.Challenges.TryAdd(roomId, challenge);
|
||||
return RedirectToAction(nameof(Challenge), new { id = roomId, playerId = model.CreatorId });
|
||||
}
|
||||
|
||||
public IActionResult Room(int id)
|
||||
[HttpGet]
|
||||
public IActionResult Challenge(int id, Guid playerId)
|
||||
{
|
||||
// Not implemented.
|
||||
return Ok();
|
||||
var player = _database.GetOrAddPlayer(playerId);
|
||||
var challenge = _database.GetOrAddChallenge(id, player);
|
||||
var model = new ChallengeViewModel()
|
||||
{
|
||||
RoomId = id,
|
||||
PlayerId = playerId,
|
||||
IsCreator = challenge.Creator.Id == playerId,
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult DropChallenge(DropChallengeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return RedirectToAction(nameof(Challenge), new { id = model.Id, playerId = model.PlayerId });
|
||||
}
|
||||
var player = _database.GetOrAddPlayer(model.PlayerId);
|
||||
var challenge = _database.GetOrAddChallenge(model.Id, player);
|
||||
if (challenge.Creator.Id == model.PlayerId)
|
||||
{
|
||||
_database.Challenges.TryRemove(model.Id, out _);
|
||||
}
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Aiursoft.ChessServer.Attributes;
|
||||
using Aiursoft.ChessServer.Data;
|
||||
using Aiursoft.CSTools.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Aiursoft.ChessServer.Controllers;
|
||||
@@ -24,7 +24,7 @@ public class PlayersController : ControllerBase
|
||||
|
||||
[HttpPut]
|
||||
[Route("{id:guid}/new-name/{nickname}")]
|
||||
public IActionResult ChangeNickname([Required]Guid id, [Required][MaxLength(20)][ValidDomainName]string nickname)
|
||||
public IActionResult ChangeNickname([Required]Guid id, [Required][MaxLength(20)][ValidNickName]string nickname)
|
||||
{
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
|
||||
@@ -35,4 +35,12 @@ public class InMemoryDatabase : ISingletonDependency
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Challenge GetOrAddChallenge(int id, Player creator)
|
||||
{
|
||||
lock (Challenges)
|
||||
{
|
||||
return Challenges.GetOrAdd(id, _ => new Challenge(creator));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Aiursoft.ChessServer.Models;
|
||||
|
||||
public class ChallengeViewModel
|
||||
{
|
||||
public int RoomId { get; set; }
|
||||
|
||||
public Guid PlayerId { get; set; }
|
||||
|
||||
public bool IsCreator { get; set; }
|
||||
}
|
||||
@@ -1,14 +1,23 @@
|
||||
namespace Aiursoft.ChessServer.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Aiursoft.ChessServer.Models;
|
||||
|
||||
public class CreateChallengeViewModel
|
||||
{
|
||||
public Guid CreatorId { get; set; }
|
||||
[Required]
|
||||
public Guid CreatorId { get; init; }
|
||||
|
||||
public string Message { get; set; } = "A chess room.";
|
||||
[Required]
|
||||
[MinLength(3)]
|
||||
[MaxLength(20)]
|
||||
public string Message { get; init; } = "A chess room.";
|
||||
|
||||
public RoleRule RoleRule { get; set; } = RoleRule.Random;
|
||||
[Required]
|
||||
public RoleRule RoleRule { get; init; } = RoleRule.Random;
|
||||
|
||||
public TimeSpan TimeLimit { get; set; } = TimeSpan.FromMinutes(10);
|
||||
[Required]
|
||||
public TimeSpan TimeLimit { get; init; } = TimeSpan.FromMinutes(10);
|
||||
|
||||
public ChallengePermission Permission { get; set; } = ChallengePermission.Public;
|
||||
[Required]
|
||||
public ChallengePermission Permission { get; init; } = ChallengePermission.Public;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Aiursoft.ChessServer.Models;
|
||||
|
||||
public class DropChallengeViewModel
|
||||
{
|
||||
[FromRoute]
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
|
||||
[FromForm]
|
||||
[Required]
|
||||
public Guid PlayerId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
@model Aiursoft.ChessServer.Models.ChallengeViewModel
|
||||
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<h1 class="display-4">Waiting for joining...</h1>
|
||||
<p class="lead">Please share the link of this room to your friend!</p>
|
||||
@{
|
||||
var link = $"{Context.Request.Scheme}://{Context.Request.Host}/Home/Room/{Model.RoomId}";
|
||||
}
|
||||
<form asp-controller="Home" asp-action="DropChallenge" asp-route-id="@Model.RoomId" method="post" class="d-inline" asp-antiforgery="false">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<input type="hidden" name="playerId" value="@Model.PlayerId"/>
|
||||
|
||||
<input type="text" class="form-control" value="@link" readonly>
|
||||
<a class="btn btn-secondary btn-lg mt-4" data-clipboard-text="@link" role="button" data-toggle="tooltip" data-placement="top" title="Copied!">
|
||||
Copy link
|
||||
</a>
|
||||
<button type="submit" class="btn btn-danger btn-lg mt-4">
|
||||
Leave
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section scripts
|
||||
{
|
||||
<script src="/node_modules/clipboard/dist/clipboard.min.js"></script>
|
||||
<script>
|
||||
// Initialize clipboard.
|
||||
const clipboard = new ClipboardJS('.btn');
|
||||
|
||||
// Activate tooltip tool
|
||||
$('[data-toggle="tooltip"]').tooltip({
|
||||
trigger: 'click',
|
||||
placement: 'bottom'
|
||||
});
|
||||
</script>
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
<div class="card mb-2 col-sm-12 px-1">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Create a new room</h5>
|
||||
<form asp-controller="Home" asp-action="Create" method="post">
|
||||
<form asp-controller="Home" asp-action="Create" method="post" asp-antiforgery="false">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<input type="hidden" asp-for="CreatorId"/>
|
||||
<div class="form-group">
|
||||
<label asp-for="Message"></label>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<h1 class="display-4">Welcome challenger!</h1>
|
||||
<p class="lead">Join a room, or create a room!</p>
|
||||
<p>
|
||||
<button class="btn btn-success btn-lg mt-4" role="button">Auto join</button>
|
||||
<a class="btn btn-success btn-lg mt-4" role="button" id="autoButton" asp-controller="Home" asp-action="Auto" >Auto join</a>
|
||||
<a class="btn btn-secondary btn-lg mt-4" role="button" id="createButton" asp-controller="Home" asp-action="Create">Create a new room</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -47,11 +47,13 @@
|
||||
<script type="module">
|
||||
import { getUserId } from "/scripts/player.js";
|
||||
const createButton = document.getElementById("createButton");
|
||||
const autoButton = document.getElementById("autoButton");
|
||||
const playerId = getUserId();
|
||||
|
||||
// Append player id to create button.
|
||||
if (playerId) {
|
||||
createButton.href += `?playerId=${playerId}`;
|
||||
autoButton.href += `?playerId=${playerId}`;
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
@@ -59,11 +59,12 @@
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="module" src="~/node_modules/jquery/dist/jquery.min.js" defer></script>
|
||||
<script type="module" src="~/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer></script>
|
||||
<script type="module" src="~/node_modules/@@chrisoakman/chessboardjs/dist/chessboard-1.0.0.min.js" defer></script>
|
||||
<script type="module" src="~/scripts/layout.js" defer></script>
|
||||
<script src="~/node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/node_modules/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/node_modules/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"></script>
|
||||
<script src="~/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/node_modules/@@chrisoakman/chessboardjs/dist/chessboard-1.0.0.min.js"></script>
|
||||
<script type="module" src="~/scripts/layout.js"></script>
|
||||
@(await RenderSectionAsync("scripts", false))
|
||||
</body>
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"@aiursoft/autodark.js": "^1.2.0",
|
||||
"@chrisoakman/chessboardjs": "^1.0.0",
|
||||
"chess.js": "^1.0.0-beta.6",
|
||||
"clipboard": "^2.0.11",
|
||||
"jquery-validation": "^1.19.5",
|
||||
"jquery-validation-unobtrusive": "^3.2.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user