parent
d2b38f04d9
commit
43fbd61029
|
@ -138,19 +138,25 @@ final class Challenge(
|
|||
def decline(id: String) =
|
||||
Auth { implicit ctx => _ =>
|
||||
OptionFuResult(api byId id) { c =>
|
||||
if (isForMe(c)) api decline c
|
||||
else notFound
|
||||
isForMe(c) ?? api.decline(c, ChallengeModel.DeclineReason.default)
|
||||
}
|
||||
}
|
||||
def apiDecline(id: String) =
|
||||
Scoped(_.Challenge.Write, _.Bot.Play, _.Board.Play) { _ => me =>
|
||||
ScopedBody(_.Challenge.Write, _.Bot.Play, _.Board.Play) { implicit req => me =>
|
||||
implicit val lang = reqLang
|
||||
api.activeByIdFor(id, me) flatMap {
|
||||
case None =>
|
||||
env.bot.player.rematchDecline(id, me) flatMap {
|
||||
case true => jsonOkResult.fuccess
|
||||
case _ => notFoundJson()
|
||||
}
|
||||
case Some(c) => api.decline(c) inject jsonOkResult
|
||||
case Some(c) =>
|
||||
env.challenge.forms.decline
|
||||
.bindFromRequest()
|
||||
.fold(
|
||||
newJsonFormError,
|
||||
data => api.decline(c, data.realReason) inject jsonOkResult
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +174,7 @@ final class Challenge(
|
|||
case Some(c) => api.cancel(c) inject jsonOkResult
|
||||
case None =>
|
||||
api.activeByIdFor(id, me) flatMap {
|
||||
case Some(c) => api.decline(c) inject jsonOkResult
|
||||
case Some(c) => api.decline(c, ChallengeModel.DeclineReason.default) inject jsonOkResult
|
||||
case None =>
|
||||
env.game.gameRepo game id dmap {
|
||||
_ flatMap { Pov.ofUserId(_, me.id) }
|
||||
|
|
|
@ -25,7 +25,7 @@ object mine {
|
|||
moreCss = cssTag("challenge.page")
|
||||
) {
|
||||
val challengeLink = s"$netBaseUrl${routes.Round.watcher(c.id, "white")}"
|
||||
main(cls := "page-small challenge-page box box-pad")(
|
||||
main(cls := s"page-small challenge-page box box-pad challenge--${c.status.name}")(
|
||||
c.status match {
|
||||
case Status.Created | Status.Offline =>
|
||||
div(id := "ping-challenge")(
|
||||
|
@ -94,6 +94,10 @@ object mine {
|
|||
case Status.Declined =>
|
||||
div(cls := "follow-up")(
|
||||
h1(trans.challenge.challengeDeclined()),
|
||||
blockquote(cls := "challenge-reason pull-quote")(
|
||||
p(c.anyDeclineReason.trans()),
|
||||
footer(userIdLink(c.destUserId))
|
||||
),
|
||||
bits.details(c),
|
||||
a(cls := "button button-fat", href := routes.Lobby.home())(trans.newOpponent())
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import chess.variant.Variant
|
|||
import lila.db.BSON
|
||||
import lila.db.BSON.{ Reader, Writer }
|
||||
import lila.db.dsl._
|
||||
import scala.util.Success
|
||||
|
||||
private object BSONHandlers {
|
||||
|
||||
|
@ -71,6 +72,10 @@ private object BSONHandlers {
|
|||
"s" -> a.secret
|
||||
)
|
||||
}
|
||||
implicit val DeclineReasonBSONHandler = tryHandler[DeclineReason](
|
||||
{ case BSONString(k) => Success(Challenge.DeclineReason(k)) },
|
||||
r => BSONString(r.key)
|
||||
)
|
||||
implicit val ChallengerBSONHandler = new BSON[Challenger] {
|
||||
def reads(r: Reader) =
|
||||
if (r contains "id") RegisteredBSONHandler reads r
|
||||
|
|
|
@ -25,7 +25,8 @@ case class Challenge(
|
|||
createdAt: DateTime,
|
||||
seenAt: Option[DateTime], // None for open challenges, so they don't sweep
|
||||
expiresAt: DateTime,
|
||||
open: Option[Boolean] = None
|
||||
open: Option[Boolean] = None,
|
||||
declineReason: Option[Challenge.DeclineReason] = None
|
||||
) {
|
||||
|
||||
import Challenge._
|
||||
|
@ -94,6 +95,13 @@ case class Challenge(
|
|||
def isOpen = ~open
|
||||
|
||||
lazy val perfType = perfTypeOf(variant, timeControl)
|
||||
|
||||
def anyDeclineReason = declineReason | DeclineReason.default
|
||||
|
||||
def declineWith(reason: DeclineReason) = copy(
|
||||
status = Status.Declined,
|
||||
declineReason = reason.some
|
||||
)
|
||||
}
|
||||
|
||||
object Challenge {
|
||||
|
@ -113,11 +121,17 @@ object Challenge {
|
|||
def apply(id: Int): Option[Status] = all.find(_.id == id)
|
||||
}
|
||||
|
||||
sealed abstract class DeclineReason(key: I18nKey)
|
||||
sealed abstract class DeclineReason(val trans: I18nKey) {
|
||||
val key = toString.toLowerCase
|
||||
}
|
||||
|
||||
object DeclineReason {
|
||||
case object Generic extends DeclineReason(I18nKeys.challenge.declineGeneric)
|
||||
case object Later extends DeclineReason(I18nKeys.challenge.declineLater)
|
||||
|
||||
val default: DeclineReason = Generic
|
||||
val all: List[DeclineReason] = List(Generic, Later)
|
||||
def apply(key: String) = all.find(_.key == key) | Generic
|
||||
}
|
||||
|
||||
case class Rating(int: Int, provisional: Boolean) {
|
||||
|
|
|
@ -73,10 +73,10 @@ final class ChallengeApi(
|
|||
case _ => fuccess(socketReload(id))
|
||||
}
|
||||
|
||||
def decline(c: Challenge) =
|
||||
repo.decline(c) >>- {
|
||||
def decline(c: Challenge, reason: Challenge.DeclineReason) =
|
||||
repo.decline(c, reason) >>- {
|
||||
uncacheAndNotify(c)
|
||||
Bus.publish(Event.Decline(c), "challenge")
|
||||
Bus.publish(Event.Decline(c declineWith reason), "challenge")
|
||||
}
|
||||
|
||||
private val acceptQueue = new lila.hub.DuctSequencer(maxSize = 64, timeout = 5 seconds, "challengeAccept")
|
||||
|
@ -149,7 +149,7 @@ final class ChallengeApi(
|
|||
}
|
||||
|
||||
private def socketReload(id: Challenge.ID): Unit =
|
||||
socket foreach (_ reload id)
|
||||
socket.foreach(_ reload id)
|
||||
|
||||
private def notify(userId: User.ID): Funit =
|
||||
for {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package lila.challenge
|
||||
|
||||
import play.api.data._
|
||||
import play.api.data.Forms._
|
||||
|
||||
final class ChallengeForm {
|
||||
|
||||
val decline = Form(
|
||||
mapping(
|
||||
"reason" -> optional(nonEmptyText)
|
||||
)(DeclineData.apply _)(DeclineData.unapply _)
|
||||
)
|
||||
|
||||
case class DeclineData(reason: Option[String]) {
|
||||
|
||||
def realReason = reason.fold(Challenge.DeclineReason.default)(Challenge.DeclineReason.apply)
|
||||
}
|
||||
}
|
|
@ -114,8 +114,12 @@ final private class ChallengeRepo(coll: Coll, maxPerUser: Max)(implicit
|
|||
|
||||
def offline(challenge: Challenge) = setStatus(challenge, Status.Offline, Some(_ plusHours 3))
|
||||
def cancel(challenge: Challenge) = setStatus(challenge, Status.Canceled, Some(_ plusHours 3))
|
||||
def decline(challenge: Challenge) = setStatus(challenge, Status.Declined, Some(_ plusHours 3))
|
||||
def accept(challenge: Challenge) = setStatus(challenge, Status.Accepted, Some(_ plusHours 3))
|
||||
def decline(challenge: Challenge, reason: Challenge.DeclineReason) =
|
||||
setStatus(challenge, Status.Declined, Some(_ plusHours 3)) >> {
|
||||
(reason != Challenge.DeclineReason.default) ??
|
||||
coll.updateField($id(challenge.id), "declineReason", reason).void
|
||||
}
|
||||
def accept(challenge: Challenge) = setStatus(challenge, Status.Accepted, Some(_ plusHours 3))
|
||||
|
||||
def statusById(id: Challenge.ID) = coll.primitiveOne[Status]($id(id), "status")
|
||||
|
||||
|
|
|
@ -50,6 +50,8 @@ final class Env(
|
|||
|
||||
lazy val jsonView = wire[JsonView]
|
||||
|
||||
val forms = new ChallengeForm
|
||||
|
||||
system.scheduler.scheduleWithFixedDelay(10 seconds, 3 seconds) { () =>
|
||||
api.sweep.unit
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ final class JsonView(
|
|||
)
|
||||
.add("direction" -> direction.map(_.name))
|
||||
.add("initialFen" -> c.initialFen)
|
||||
.add("declineReason" -> c.declineReason.map(_.trans.txt()))
|
||||
|
||||
private def iconChar(c: Challenge) =
|
||||
if (c.variant == chess.variant.FromPosition) '*'
|
||||
|
|
|
@ -16,7 +16,7 @@ final class Env(
|
|||
|
||||
private lazy val maxPlaying = appConfig.get[Max]("setup.max_playing")
|
||||
|
||||
lazy val forms = wire[FormFactory]
|
||||
lazy val forms = wire[SetupForm]
|
||||
|
||||
lazy val processor = wire[Processor]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import play.api.data.Forms._
|
|||
import lila.rating.RatingRange
|
||||
import lila.user.{ User, UserContext }
|
||||
|
||||
final class FormFactory {
|
||||
final class SetupForm {
|
||||
|
||||
import Mappings._
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
$c-challenge: $c-secondary;
|
||||
|
||||
.challenge-page {
|
||||
.challenge-id-form {
|
||||
white-space: nowrap;
|
||||
|
@ -41,18 +39,22 @@ $c-challenge: $c-secondary;
|
|||
.details {
|
||||
@extend %flex-between;
|
||||
|
||||
$c-bg: mix($c-good, $c-bg-box, 10%);
|
||||
--c-font: #{$c-good};
|
||||
--c-bg: #{$c-bg};
|
||||
|
||||
border-radius: 99px;
|
||||
background: mix($c-challenge, $c-bg-box, 10%);
|
||||
border: 1px solid $c-challenge;
|
||||
padding: .5em 1.1em;
|
||||
margin-bottom: 3rem;
|
||||
font-size: 2em;
|
||||
background: var(--c-bg);
|
||||
border: 1px solid var(--c-font);
|
||||
|
||||
> div {
|
||||
@extend %flex-center, %roboto;
|
||||
|
||||
&::before {
|
||||
color: $c-challenge;
|
||||
color: var(--c-font);
|
||||
font-size: 6rem;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
@ -68,11 +70,22 @@ $c-challenge: $c-secondary;
|
|||
|
||||
.mode {
|
||||
font-weight: bold;
|
||||
color: $c-challenge;
|
||||
color: var(--c-font);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
&.challenge--declined .details {
|
||||
$c-bg: mix($c-bad, $c-bg-box, 10%);
|
||||
--c-font: #{$c-bad};
|
||||
--c-bg: #{$c-bg};
|
||||
}
|
||||
|
||||
.challenge-reason {
|
||||
margin: 2em auto 5em auto;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
.follow-up .button {
|
||||
display: block;
|
||||
margin-top: 2em;
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
@import "../../../common/css/plugin";
|
||||
@import "../../../common/css/component/quote";
|
||||
@import "../page";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
:root {
|
||||
--tagify-dd-color-primary: $c-primary;
|
||||
--tagify-dd-color-primary: #{$c-primary};
|
||||
|
||||
// should be same as "$tags-focus-border-color"
|
||||
--tagify-dd-bg-color: $c-bg-box;
|
||||
--tagify-dd-bg-color: #{$c-bg-box};
|
||||
}
|
||||
|
||||
.tagify {
|
||||
|
|
Loading…
Reference in New Issue