Merge branch 'master' into crosstable3

* master:
  fix and restore activity truncation
  tweak ublog paginator max pages
  prevent arena berserk if inc > limit*2
  separate cancel from terminate buttons
  Use oninput instead of onkeyup & onpaste
  prettier
  Flatten rating line to today
crosstable3
Thibault Duplessis 2021-09-18 14:57:43 +02:00
commit 62ffcb7710
10 changed files with 45 additions and 20 deletions

View File

@ -235,7 +235,7 @@ final class Ublog(env: Env) extends LilaController(env) {
def community(code: String, page: Int) = Open { implicit ctx =>
NotForKids {
val l = Lang.get(code).filter(LangList.popularNoRegion.contains)
Reasonable(page, 20) {
Reasonable(page, 8) {
env.ublog.paginator.liveByCommunity(l, page) map { posts =>
Ok(html.ublog.index.community(l, posts))
}
@ -265,7 +265,7 @@ final class Ublog(env: Env) extends LilaController(env) {
def topic(str: String, page: Int) = Open { implicit ctx =>
NotForKids {
Reasonable(page, 15) {
Reasonable(page, 5) {
lila.ublog.UblogTopic.fromUrl(str) ?? { top =>
env.ublog.paginator.liveByTopic(top, page) map { posts =>
Ok(html.ublog.index.topic(top, posts))

View File

@ -84,6 +84,9 @@ object form {
form3.submit(trans.save(), icon = "".some)
)
),
hr,
br,
br,
postForm(cls := "terminate", action := routes.Tournament.terminate(tour.id))(
submitButton(dataIcon := "", cls := "text button button-red confirm")(
trans.cancelTournament()

View File

@ -1,5 +1,7 @@
package lila.activity
import reactivemongo.api.bson._
import lila.db.AsyncCollFailingSilently
import lila.db.dsl._
import lila.game.Game
@ -152,9 +154,9 @@ final class ActivityWriteApi(
!setters.isEmpty ?? {
coll.update
.one($id(activity.id), $set(setters), upsert = true)
// .flatMap {
// _.upserted.nonEmpty ?? truncateAfterInserting(coll, activity.id)
// }
.flatMap {
_.upserted.nonEmpty ?? truncateAfterInserting(coll, activity.id)
}
.void
}
}
@ -170,8 +172,16 @@ final class ActivityWriteApi(
.one[Bdoc]
.flatMap {
_.flatMap(_.getAsOpt[Activity.Id]("_id")) ?? { oldId =>
coll.delete.one($doc("_id" $lte oldId)).void
coll.delete
.one(
$doc(
"_id" -> $doc(
"$lte" -> oldId,
"$regex" -> BSONRegex(s"^${id.userId}$idSep", "")
)
)
)
.void
}
}
}

View File

@ -19,18 +19,18 @@ private object BSONHandlers {
import activities._
import model._
def regexId(userId: User.ID): Bdoc = "_id" $startsWith s"$userId:"
val idSep = ':'
def regexId(userId: User.ID): Bdoc = "_id" $startsWith s"$userId$idSep"
implicit lazy val activityIdHandler = {
val sep = ':'
tryHandler[Id](
{ case BSONString(v) =>
v split sep match {
v split idSep match {
case Array(userId, dayStr) => Success(Id(userId, Day(Integer.parseInt(dayStr))))
case _ => handlerBadValue(s"Invalid activity id $v")
}
},
id => BSONString(s"${id.userId}$sep${id.day.value}")
id => BSONString(s"${id.userId}$idSep${id.day.value}")
)
}

View File

@ -74,7 +74,7 @@ final class TournamentApi(
password = setup.password,
variant = setup.realVariant,
position = setup.realPosition,
berserkable = setup.berserkable | true,
berserkable = setup.isBerserkable,
streakable = setup.streakable | true,
teamBattle = setup.teamBattleByTeam map TeamBattle.init,
description = setup.description,

View File

@ -203,6 +203,13 @@ private[tournament] case class TournamentSetup(
def isPrivate = password.isDefined || conditions.teamMember.isDefined
// prevent berserk tournament abuse with TC like 1+60,
// where perf is Classical but berserked games are Hyperbullet.
private def timeControlPreventsBerserk =
clockConfig.incrementSeconds > clockConfig.limitInMinutes * 2
def isBerserkable = ~berserkable && !timeControlPreventsBerserk
// update all fields and use default values for missing fields
// meant for HTML form updates
def updateAll(old: Tournament): Tournament = {
@ -220,7 +227,7 @@ private[tournament] case class TournamentSetup(
if (old.isCreated || old.position.isDefined) realPosition
else old.position
},
noBerserk = !(~berserkable),
noBerserk = !isBerserkable,
noStreak = !(~streakable),
teamBattle = old.teamBattle,
description = description,
@ -245,7 +252,7 @@ private[tournament] case class TournamentSetup(
if (position.isDefined && (old.isCreated || old.position.isDefined)) realPosition
else old.position
},
noBerserk = berserkable.fold(old.noBerserk)(!_),
noBerserk = berserkable.fold(old.noBerserk)(!_) || timeControlPreventsBerserk,
noStreak = streakable.fold(old.noStreak)(!_),
teamBattle = old.teamBattle,
description = description.fold(old.description)(_.some.filter(_.nonEmpty)),

View File

@ -2,6 +2,14 @@ lichess.ratingHistoryChart = function (data, { singlePerfName, perfIndex }) {
var oneDay = 86400000;
function smoothDates(data) {
if (!data.length) return [];
// If last rating wasn't today, add to the array
var today = new Date().setUTCHours(0, 0, 0, 0);
var lastRating = data[data.length - 1];
if (lastRating[0] != today) {
data.push([today, lastRating[1]]);
}
var begin = data[0][0];
var end = data[data.length - 1][0];
var reversed = data.slice().reverse();

View File

@ -112,10 +112,7 @@ export function view(root: AnalyseCtrl): VNode {
insert(vnode) {
setupTextarea(vnode);
const el = vnode.elm as HTMLInputElement;
function onChange() {
setTimeout(() => ctrl.submit(el.value), 50);
}
el.onkeyup = el.onpaste = onChange;
el.oninput = () => setTimeout(() => ctrl.submit(el.value), 50);
},
postpatch: (old, vnode) => setupTextarea(vnode, old),
},

View File

@ -102,7 +102,7 @@ function edit(ctrl: DescriptionCtrl, id: string, chapter: boolean): VNode {
h('textarea#form-control.desc-text.' + id, {
hook: onInsert<HTMLInputElement>(el => {
el.value = ctrl.text === '-' ? '' : ctrl.text || '';
el.onkeyup = el.onpaste = () => {
el.oninput = () => {
ctrl.save(el.value.trim());
};
el.focus();

View File

@ -171,7 +171,7 @@ function textareaHook(ctrl: AnalyseCtrl, field: 'deviation' | 'hint'): Hooks {
insert(vnode: VNode) {
const el = vnode.elm as HTMLInputElement;
el.value = value;
el.onkeyup = el.onpaste = () => {
el.oninput = () => {
const node = ctrl.node;
node.gamebook = node.gamebook || {};
node.gamebook[field] = el.value.trim();