Skip to content

Commit

Permalink
Merge branch 'master' into adityaj2003/master
Browse files Browse the repository at this point in the history
* master: (59 commits)
  Revert "fewer dom text nodes and more css tricks"
  only feature swiss tournaments where players actually play
  scala tweaks
  scala tweaks
  New Crowdin updates (lichess-org#12949)
  tweak audio context handling
  voice mic selector
  prevent lichess.storage race condition on init
  moveCtrl is available
  remove ui/voice `let moveCtrl` global
  move voice toggle code out of the event listener
  mic.ts: if the audio context is suspended, wait for user interaction
  use OpaqueInt+Int=OpaqueInt
  Use fullMoveNumber when writing played moves
  Revert RendererActor visibility
  make RoundSocket round robin listen to 16 channels
  scala tweaks
  fewer dom text nodes and more css tricks
  only bind one click for all top players of a team battle
  remove superfluous team battle tag attr
  ...
  • Loading branch information
ornicar committed Jun 5, 2023
2 parents d510038 + d51a533 commit 47026dc
Show file tree
Hide file tree
Showing 91 changed files with 6,030 additions and 5,675 deletions.
3 changes: 3 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ eea12a89f8550d74518f19ec9cda404e3f73b3d0

# Scala Steward: Reformat with scalafmt 3.7.1
02bcdbe4a32467dcc9403e2787126caaa249278b

# Scala Steward: Reformat with scalafmt 3.7.4
84137814df3b827196e1438f3bc654c0dcc1c1f2
2 changes: 1 addition & 1 deletion .github/workflows/assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
submodules: recursive
- uses: pnpm/action-setup@v2
with:
version: '8.1'
version: '8.6'
- run: git submodule absorbgitdirs
- uses: actions/setup-node@v3
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: '8.1'
version: '8.6'
- uses: actions/setup-node@v3
with:
node-version: '16'
node-version: '17'
cache: pnpm
- uses: github/codeql-action/init@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.7.3"
version = "3.7.4"
runner.dialect = scala3

align.preset = more
Expand Down
3 changes: 1 addition & 2 deletions app/controllers/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,8 @@ final class Auth(

private[controllers] object LoginRateLimit:
private val lastAttemptIp =
env.memo.cacheApi.notLoadingSync[UserIdOrEmail, IpAddress](1024, "login.lastIp") {
env.memo.cacheApi.notLoadingSync[UserIdOrEmail, IpAddress](64, "login.lastIp"):
_.expireAfterWrite(10.seconds).build()
}
def apply(id: UserIdOrEmail, req: RequestHeader)(run: RateLimit.Charge => Fu[Result]): Fu[Result] =
val ip = req.ipAddress
val multipleIps = lastAttemptIp.asMap().put(id, ip).fold(false)(_ != ip)
Expand Down
10 changes: 6 additions & 4 deletions app/controllers/Puzzle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,19 @@ final class Puzzle(env: Env, apiC: => Api) extends LilaController(env):
}
}

private def nextPuzzleForMe(angle: PuzzleAngle, color: Option[Option[Color]])(using
ctx: Context
): Fu[Option[Puz]] =
private def nextPuzzleForMe(
angle: PuzzleAngle,
color: Option[Option[Color]],
difficulty: PuzzleDifficulty = PuzzleDifficulty.Normal
)(using ctx: Context): Fu[Option[Puz]] =
ctx.me match
case Some(me) =>
ctx.req.session.get(cookieDifficulty).flatMap(PuzzleDifficulty.find).?? {
env.puzzle.session.setDifficulty(me, _)
} >>
color.?? { env.puzzle.session.setAngleAndColor(me, angle, _) } >>
env.puzzle.selector.nextPuzzleFor(me, angle)
case None => env.puzzle.anon.getOneFor(angle, ~color)
case None => env.puzzle.anon.getOneFor(angle, difficulty, ~color)

private def redirectNoPuzzle =
Redirect(routes.Puzzle.themes).flashFailure("No more puzzles available! Try another theme.").toFuccess
Expand Down
141 changes: 73 additions & 68 deletions app/controllers/Team.scala
Original file line number Diff line number Diff line change
Expand Up @@ -219,30 +219,27 @@ final class Team(
}
}

private val OneAtATime = new lila.memo.FutureConcurrencyLimit[UserId](
private val OneAtATime = lila.memo.FutureConcurrencyLimit[UserId](
key = "team.concurrency.user",
ttl = 10.minutes,
maxConcurrency = 1
)
def create = AuthBody { ctx ?=> me =>
OneAtATime(me.id, rateLimitedFu):
api hasJoinedTooManyTeams me flatMap { tooMany =>
if (tooMany) tooManyTeams(me)
else
LimitPerWeek(me):
forms.create
.bindFromRequest()
.fold(
err =>
forms.anyCaptcha map { captcha =>
BadRequest(html.team.form.create(err, captcha))
},
data =>
api.create(data, me) map { team =>
Redirect(routes.Team.show(team.id)).flashSuccess
}
)
}
LimitPerWeek(me):
JoinLimit(me)(tooManyTeamsHtml(me)):
forms.create
.bindFromRequest()
.fold(
err =>
forms.anyCaptcha map { captcha =>
BadRequest(html.team.form.create(err, captcha))
},
data =>
api.create(data, me) map { team =>
Redirect(routes.Team.show(team.id)).flashSuccess
}
)
}

def mine = Auth { ctx ?=> me =>
Expand All @@ -251,7 +248,7 @@ final class Team(
}
}

private def tooManyTeams(me: UserModel)(using Context) =
private def tooManyTeamsHtml(me: UserModel)(using Context): Fu[Result] =
api mine me map html.team.list.mine map { BadRequest(_) }

def leader = Auth { ctx ?=> me =>
Expand All @@ -260,6 +257,14 @@ final class Team(
}
}

private def JoinLimit(me: UserModel)(tooMany: => Fu[Result])(f: => Fu[Result]) =
api
.hasJoinedTooManyTeams(me)
.flatMap:
if _ then tooMany else f

private val tooManyTeamsJson = BadRequest(jsonError("You have joined too many teams"))

def join(id: TeamId) =
AuthOrScopedBody(_.Team.Write)(
auth = ctx ?=>
Expand All @@ -268,32 +273,30 @@ final class Team(
.teamEnabled(id)
.flatMapz: team =>
OneAtATime(me.id, rateLimitedFu):
api hasJoinedTooManyTeams me flatMap { tooMany =>
if (tooMany)
negotiate(
html = tooManyTeams(me),
api = _ => BadRequest(jsonError("You have joined too many teams")).toFuccess
)
else
negotiate(
html = webJoin(team, me, request = none, password = none),
api = _ =>
forms
.apiRequest(team)
.bindFromRequest()
.fold(
newJsonFormError,
setup =>
api.join(team, me, setup.message, setup.password) flatMap {
case Requesting.Joined => jsonOkResult.toFuccess
case Requesting.NeedRequest =>
BadRequest(jsonError("This team requires confirmation.")).toFuccess
case Requesting.NeedPassword =>
BadRequest(jsonError("This team requires a password.")).toFuccess
}
)
)
}
JoinLimit(me)(
negotiate(
html = tooManyTeamsHtml(me),
api = _ => tooManyTeamsJson.toFuccess
)
):
negotiate(
html = webJoin(team, me, request = none, password = none),
api = _ =>
forms
.apiRequest(team)
.bindFromRequest()
.fold(
newJsonFormError,
setup =>
api.join(team, me, setup.message, setup.password) flatMap {
case Requesting.Joined => jsonOkResult.toFuccess
case Requesting.NeedRequest =>
BadRequest(jsonError("This team requires confirmation.")).toFuccess
case Requesting.NeedPassword =>
BadRequest(jsonError("This team requires a password.")).toFuccess
}
)
)
,
scoped = req ?=>
me =>
Expand All @@ -306,17 +309,17 @@ final class Team(
newJsonFormError,
setup =>
OneAtATime(me.id, rateLimitedFu):
api.join(team, me, setup.message, setup.password) flatMap {
case Requesting.Joined => jsonOkResult.toFuccess
case Requesting.NeedPassword =>
Forbidden(jsonError("This team requires a password.")).toFuccess
case Requesting.NeedRequest =>
Forbidden(
jsonError(
"This team requires confirmation, and is not owned by the oAuth app owner."
)
).toFuccess
}
JoinLimit(me)(tooManyTeamsJson.toFuccess):
api.join(team, me, setup.message, setup.password) flatMap {
case Requesting.Joined => jsonOkResult.toFuccess
case Requesting.NeedPassword =>
Forbidden(jsonError("This team requires a password.")).toFuccess
case Requesting.NeedRequest =>
Forbidden:
jsonError:
"This team requires confirmation, and is not owned by the oAuth app owner."
.toFuccess
}
)
}
)
Expand Down Expand Up @@ -345,18 +348,20 @@ final class Team(

def requestCreate(id: TeamId) = AuthBody { ctx ?=> me =>
OptionFuResult(api.requestable(id, me)): team =>
forms
.request(team)
.bindFromRequest()
.fold(
err => BadRequest(html.team.request.requestForm(team, err)).toFuccess,
setup =>
if (team.open) webJoin(team, me, request = none, password = setup.password)
else
setup.message ?? { msg =>
api.createRequest(team, me, msg) inject Redirect(routes.Team.show(team.id)).flashSuccess
}
)
OneAtATime(me.id, rateLimitedFu):
JoinLimit(me)(tooManyTeamsHtml(me)):
forms
.request(team)
.bindFromRequest()
.fold(
err => BadRequest(html.team.request.requestForm(team, err)).toFuccess,
setup =>
if (team.open) webJoin(team, me, request = none, password = setup.password)
else
setup.message ?? { msg =>
api.createRequest(team, me, msg) inject Redirect(routes.Team.show(team.id)).flashSuccess
}
)
}

private def webJoin(team: TeamModel, me: UserModel, request: Option[String], password: Option[String]) =
Expand Down
4 changes: 1 addition & 3 deletions app/views/game/mini.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ object mini:

def noCtx(pov: Pov, tv: Boolean = false): Tag =
val link = if (tv) routes.Tv.index else routes.Round.watcher(pov.gameId, pov.color.name)
renderMini(pov, link.url.some)(using
defaultLang
)
renderMini(pov, link.url.some)(using defaultLang)

private def renderMini(
pov: Pov,
Expand Down
79 changes: 40 additions & 39 deletions app/views/site/helpModal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ object helpModal:
)
)
)

def voiceMove(using Lang) =
import trans.keyboardMove.*
frag(
Expand All @@ -158,42 +159,41 @@ object helpModal:
li(
"Use the ",
i(dataIcon := licon.Voice),
" button to toggle voice recognition mode."
),
li(
"Your voice audio never leaves your device. Moves are sent as plain text just like those made by mouse or touch."
" button to toggle voice recognition. Moves are sent to lichess.org "
+ "as plain text. Audio does not leave your device."
),
li(
"You may speak UCI, SAN, piece names, board squares, or phrases like ",
voice("P,x,R"),
" and ",
voice("x"),
". Click ",
strong("Show me everything"),
" for a full list."
"We show arrows for multiple moves when we're not sure. Speak the color or number of a move "
+ "arrow to select it. Choose between colors or numbers in the voice ",
i(dataIcon := licon.Gear),
" menu"
),
li(
"We show colored or numbered arrows for up to 8 available moves when we're not sure. " +
"If an arrow shows a growing pie, that move will be played when the pie becomes a full circle."
),
li(
"During this countdown, you may only say ",
"If an arrow shows a sweeping arc, that move will be played when the arc becomes a full circle. "
+ "During this countdown, you may say \"",
voice("yes"),
" to play the move immediately, ",
"\" to play it immediately, \"",
voice("no"),
" to cancel, ",
voice("stop"),
" to stop the clock, or the color/number of an arrow. No other command will be recognized."
"\" to cancel, or choose a different arrow. The timer can be adjusted or turned off in the ",
i(dataIcon := licon.Gear),
" menu."
),
li(
"Higher clarity values will decrease arrows & countdowns but increase the chance of misplays."
"An increased clarity setting reduces the number of arrows shown when using a good microphone "
+ "in quiet surroundings. Decrease clarity to offer more choices if your moves are often "
+ "misheard. Try the phonetic alphabet to improve recognition. ",
phonetics()
),
li(
"Clarity, countdown, and arrow display settings are in the voice bar hamburger menu."
),
li(
"The phonetic alphabet is ",
phonetic("a,b,c,d,e,f,g,h")
"Enable \"",
strong("Hey Lichess"),
"\" in the ",
i(dataIcon := licon.Gear),
" menu to start and stop listening with your voice. Say \"",
strong("Hey Lichess"),
"\" to start listening and make a move. Say \"",
voice("stop"),
"\" to go back to sleep. We go to sleep automatically after 20 seconds without a spoken command."
)
)
)
Expand All @@ -204,29 +204,26 @@ object helpModal:
table(
tbody(
header(performAMove()),
row(frag(voice("e,4"), br, phonetic("e,4")), "Move to e4 or select a piece there"),
row(voice("N"), "Move my knight or capture a knight"),
row(frag(voice("B,h,6"), br, phonetic("B,h,6")), "Move bishop to h6"),
row(voice("e,4"), "Move to e4 or select e4 piece"),
row(voice("N"), "Select or capture a knight"),
row(voice("B,h,6"), "Move bishop to h6"),
row(voice("Q,x,R"), "Take rook with queen"),
row(
frag(voice("c,8,=,N"), br, phonetic("c,8,N")),
"Move c8 promote to knight"
),
row(voice("c,8,=,N"), "Pawn to c8 promote to knight"),
row(voice("castle"), "castle (either side)"),
row(voice("O-O-O"), "Queenside castle"),
row(frag(voice("a,7,g,1"), br, phonetic("a,7,g,1")), "Full UCI works too"),
row(voice("draw"), offerOrAcceptDraw())
row(phonetic("a,7,g,1"), "Phonetic alphabet is best"),
row(voice("draw"), offerOrAcceptDraw()),
row(voice("resign"), trans.resignTheGame()),
row(voice("takeback"), "Request a takeback")
)
),
table(
tbody(
header(otherCommands()),
row(voice("resign"), trans.resignTheGame()),
row(voice("takeback"), "Request a takeback"),
row(voice("no"), "Cancel timer or deny a request"),
row(voice("yes"), "Play preferred move or confirm something"),
row(voice("stop"), "Stop the timer but keep the arrows"),
row(voice("mic-off"), "Turn off your microphone"),
row(voice("stop"), "Sleep (if wake word enabled)"),
row(voice("mic-off"), "Turn off voice recognition"),
row(voice("next"), trans.puzzle.nextPuzzle()),
row(voice("upvote"), trans.puzzle.upVote()),
row(voice("solve"), "Show puzzle solution"),
Expand All @@ -239,3 +236,7 @@ object helpModal:
)
)
)

private def phonetics() =
for letter <- List("a", "b", "c", "d", "e", "f", "g", "h")
yield frag(s"$letter is \"", phonetic(letter), "\". ")

0 comments on commit 47026dc

Please sign in to comment.