explain why tournament join failed - closes #7966

pull/8121/head
Thibault Duplessis 2021-02-08 16:01:05 +01:00
parent 18960667a8
commit f0df9bba47
5 changed files with 70 additions and 48 deletions

View File

@ -100,7 +100,7 @@ final class Tournament(
html = tourOption
.fold(tournamentNotFound.fuccess) { tour =>
for {
verdicts <- api.verdicts(tour, ctx.me, getUserTeamIds)
verdicts <- api.getVerdicts(tour, ctx.me, getUserTeamIds)
version <- env.tournament.version(tour.id)
json <- jsonView(
tour = tour,
@ -209,13 +209,17 @@ final class Tournament(
api.joinWithResult(id, me, password, teamId, getUserTeamIds, isLeader) flatMap { result =>
negotiate(
html = fuccess {
if (result) Redirect(routes.Tournament.show(id))
else BadRequestWithReason("wrong password maybe?").pp
result.error match {
case None => Redirect(routes.Tournament.show(id))
case Some(error) => BadRequest(error)
}
},
api = _ =>
fuccess {
if (result) jsonOkResult
else BadRequest(Json.obj("joined" -> false))
result.error match {
case None => jsonOkResult
case Some(error) => BadRequest(Json.obj("joined" -> false, "error" -> error))
}
}
)
}

View File

@ -221,4 +221,16 @@ object Tournament {
def makeId = ThreadLocalRandom nextString 8
case class PastAndNext(past: List[Tournament], next: List[Tournament])
sealed abstract class JoinResult(val error: Option[String] = None) {
def ok = error.isEmpty
}
object JoinResult {
case object Ok extends JoinResult()
case object WrongPassword extends JoinResult("Wrong password".some)
case object Paused extends JoinResult("Your pause is not over yet".some)
case object Verdicts extends JoinResult("Tournament restrictions".some)
case object MissingTeam extends JoinResult("Missing team".some)
case object Nope extends JoinResult("Couldn't join for some reason?".some)
}
}

View File

@ -266,7 +266,7 @@ final class TournamentApi(
}
}
def verdicts(
def getVerdicts(
tour: Tournament,
me: Option[User],
getUserTeamIds: User => Fu[List[TeamID]]
@ -289,46 +289,47 @@ final class TournamentApi(
withTeamId: Option[String],
getUserTeamIds: User => Fu[List[TeamID]],
asLeader: Boolean,
promise: Option[Promise[Boolean]]
promise: Option[Promise[Tournament.JoinResult]]
): Funit =
Sequencing(tourId)(tournamentRepo.enterableById) { tour =>
playerRepo.exists(tour.id, me.id) flatMap { playerExists =>
val fuJoined =
if (tour.password == password || playerExists) {
verdicts(tour, me.some, getUserTeamIds) flatMap {
_.accepted ?? {
pause.canJoin(me.id, tour) ?? {
def proceedWithTeam(team: Option[String]) =
playerRepo.join(tour.id, me, tour.perfType, team) >>
updateNbPlayers(tour.id) >>- {
socket.reload(tour.id)
publish()
} inject true
withTeamId match {
case None if tour.isTeamBattle => playerExists ?? proceedWithTeam(none)
case None => proceedWithTeam(none)
case Some(team) =>
tour.teamBattle match {
case Some(battle) if battle.teams contains team =>
getUserTeamIds(me) flatMap { myTeams =>
if (myTeams has team) proceedWithTeam(team.some)
else fuccess(false)
}
case _ => fuccess(false)
}
}
import Tournament.JoinResult
val fuResult: Fu[JoinResult] =
if (!playerExists && tour.password.exists(p => !password.has(p))) fuccess(JoinResult.WrongPassword)
else
getVerdicts(tour, me.some, getUserTeamIds) flatMap { verdicts =>
if (!verdicts.accepted) fuccess(JoinResult.Verdicts)
else if (!pause.canJoin(me.id, tour)) fuccess(JoinResult.Paused)
else {
def proceedWithTeam(team: Option[String]): Fu[JoinResult] =
playerRepo.join(tour.id, me, tour.perfType, team) >>
updateNbPlayers(tour.id) >>- {
socket.reload(tour.id)
publish()
} inject JoinResult.Ok
withTeamId match {
case None if tour.isTeamBattle && playerExists => proceedWithTeam(none)
case None if tour.isTeamBattle => fuccess(JoinResult.MissingTeam)
case None => proceedWithTeam(none)
case Some(team) =>
tour.teamBattle match {
case Some(battle) if battle.teams contains team =>
getUserTeamIds(me) flatMap { myTeams =>
if (myTeams has team) proceedWithTeam(team.some)
else fuccess(JoinResult.MissingTeam)
}
case _ => fuccess(JoinResult.Nope)
}
}
}
}
} else {
socket.reload(tour.id)
fuccess(false)
}
fuJoined map { joined =>
withTeamId.ifTrue(joined && asLeader && tour.isTeamBattle) foreach {
tournamentRepo.setForTeam(tour.id, _)
}
promise.foreach(_ success joined)
fuResult map { result =>
if (result.ok)
withTeamId.ifTrue(asLeader && tour.isTeamBattle) foreach {
tournamentRepo.setForTeam(tour.id, _)
}
else socket.reload(tour.id)
promise.foreach(_ success result)
}
}
}
@ -340,10 +341,10 @@ final class TournamentApi(
teamId: Option[String],
getUserTeamIds: User => Fu[List[TeamID]],
isLeader: Boolean
): Fu[Boolean] = {
val promise = Promise[Boolean]()
): Fu[Tournament.JoinResult] = {
val promise = Promise[Tournament.JoinResult]()
join(tourId, me, password, teamId, getUserTeamIds, isLeader, promise.some)
promise.future.withTimeoutDefault(5.seconds, false)
promise.future.withTimeoutDefault(5.seconds, Tournament.JoinResult.Nope)
}
def pageOf(tour: Tournament, userId: User.ID): Fu[Option[Int]] =

View File

@ -29,13 +29,16 @@ export const json = (url: string, init: RequestInit = {}): Promise<any> =>
/* fetch a string */
export const text = (url: string, init: RequestInit = {}): Promise<string> =>
textRaw(url, init).then(res => {
if (res.ok) return res.text();
throw res.statusText;
});
export const textRaw = (url: string, init: RequestInit = {}): Promise<Response> =>
fetch(url, {
...defaultInit,
headers: { ...xhrHeader },
...init,
}).then(res => {
if (res.ok) return res.text();
throw res.statusText;
});
/* load a remote script */

View File

@ -7,7 +7,7 @@ const onFail = () => lichess.reload();
export const join = throttle(1000, (ctrl: TournamentController, password?: string, team?: string) =>
xhr
.text('/tournament/' + ctrl.data.id + '/join', {
.textRaw('/tournament/' + ctrl.data.id + '/join', {
method: 'POST',
body: JSON.stringify({
p: password || null,
@ -15,7 +15,9 @@ export const join = throttle(1000, (ctrl: TournamentController, password?: strin
}),
headers: { 'Content-Type': 'application/json' },
})
.catch(onFail)
.then(res => {
if (!res.ok) res.text().then(alert);
})
);
export const withdraw = throttle(1000, (ctrl: TournamentController) =>