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;
|
_database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
var model = new IndexViewModel(_database.Challenges);
|
var model = new IndexViewModel(_database.Challenges);
|
||||||
return View(model);
|
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)
|
public IActionResult Create(Guid playerId)
|
||||||
{
|
{
|
||||||
var iHaveAChallenge = _database.Challenges.FirstOrDefault(t => t.Value.Creator.Id == playerId);
|
var iHaveAChallenge = _database.Challenges.FirstOrDefault(t => t.Value.Creator.Id == playerId);
|
||||||
if (iHaveAChallenge.Value != null)
|
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();
|
var model = new CreateChallengeViewModel();
|
||||||
return View(model);
|
return View(model);
|
||||||
@@ -38,6 +61,11 @@ public class HomeController : Controller
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Create(CreateChallengeViewModel model)
|
public IActionResult Create(CreateChallengeViewModel model)
|
||||||
{
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
var player = _database.GetOrAddPlayer(model.CreatorId);
|
var player = _database.GetOrAddPlayer(model.CreatorId);
|
||||||
var challenge = new Challenge(player)
|
var challenge = new Challenge(player)
|
||||||
{
|
{
|
||||||
@@ -46,14 +74,38 @@ public class HomeController : Controller
|
|||||||
Permission = model.Permission,
|
Permission = model.Permission,
|
||||||
TimeLimit = model.TimeLimit,
|
TimeLimit = model.TimeLimit,
|
||||||
};
|
};
|
||||||
var uniqueId = _counter.GetUniqueNo();
|
var roomId = _counter.GetUniqueNo();
|
||||||
_database.Challenges.TryAdd(uniqueId, challenge);
|
_database.Challenges.TryAdd(roomId, challenge);
|
||||||
return RedirectToAction(nameof(Room), new { id = uniqueId });
|
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.
|
var player = _database.GetOrAddPlayer(playerId);
|
||||||
return Ok();
|
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 System.ComponentModel.DataAnnotations;
|
||||||
|
using Aiursoft.ChessServer.Attributes;
|
||||||
using Aiursoft.ChessServer.Data;
|
using Aiursoft.ChessServer.Data;
|
||||||
using Aiursoft.CSTools.Attributes;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Aiursoft.ChessServer.Controllers;
|
namespace Aiursoft.ChessServer.Controllers;
|
||||||
@@ -24,7 +24,7 @@ public class PlayersController : ControllerBase
|
|||||||
|
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[Route("{id:guid}/new-name/{nickname}")]
|
[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)
|
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 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 mb-2 col-sm-12 px-1">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Create a new room</h5>
|
<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"/>
|
<input type="hidden" asp-for="CreatorId"/>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="Message"></label>
|
<label asp-for="Message"></label>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<h1 class="display-4">Welcome challenger!</h1>
|
<h1 class="display-4">Welcome challenger!</h1>
|
||||||
<p class="lead">Join a room, or create a room!</p>
|
<p class="lead">Join a room, or create a room!</p>
|
||||||
<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>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -47,11 +47,13 @@
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
import { getUserId } from "/scripts/player.js";
|
import { getUserId } from "/scripts/player.js";
|
||||||
const createButton = document.getElementById("createButton");
|
const createButton = document.getElementById("createButton");
|
||||||
|
const autoButton = document.getElementById("autoButton");
|
||||||
const playerId = getUserId();
|
const playerId = getUserId();
|
||||||
|
|
||||||
// Append player id to create button.
|
// Append player id to create button.
|
||||||
if (playerId) {
|
if (playerId) {
|
||||||
createButton.href += `?playerId=${playerId}`;
|
createButton.href += `?playerId=${playerId}`;
|
||||||
|
autoButton.href += `?playerId=${playerId}`;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,11 +59,12 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="~/node_modules/jquery/dist/jquery.min.js"></script>
|
||||||
<script type="module" src="~/node_modules/jquery/dist/jquery.min.js" defer></script>
|
<script src="~/node_modules/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||||
<script type="module" src="~/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" defer></script>
|
<script src="~/node_modules/jquery-validation-unobtrusive/dist/jquery.validate.unobtrusive.min.js"></script>
|
||||||
<script type="module" src="~/node_modules/@@chrisoakman/chessboardjs/dist/chessboard-1.0.0.min.js" defer></script>
|
<script src="~/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script type="module" src="~/scripts/layout.js" defer></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))
|
@(await RenderSectionAsync("scripts", false))
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"@aiursoft/autodark.js": "^1.2.0",
|
"@aiursoft/autodark.js": "^1.2.0",
|
||||||
"@chrisoakman/chessboardjs": "^1.0.0",
|
"@chrisoakman/chessboardjs": "^1.0.0",
|
||||||
"chess.js": "^1.0.0-beta.6",
|
"chess.js": "^1.0.0-beta.6",
|
||||||
|
"clipboard": "^2.0.11",
|
||||||
"jquery-validation": "^1.19.5",
|
"jquery-validation": "^1.19.5",
|
||||||
"jquery-validation-unobtrusive": "^3.2.12"
|
"jquery-validation-unobtrusive": "^3.2.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user