more challenge refactoring, bootstrap mithril project
parent
ba93101c83
commit
911a70dda1
|
@ -21,12 +21,6 @@ private[app] final class Renderer extends Actor {
|
|||
case lila.tournament.actorApi.RemindTournament(tournament, _) =>
|
||||
sender ! spaceless(V.tournament.reminder(tournament))
|
||||
|
||||
case lila.hub.actorApi.setup.RemindChallenge(gameId, from, _) =>
|
||||
val replyTo = sender
|
||||
(GameRepo game gameId) zip (UserRepo named from) onSuccess {
|
||||
case (Some(game), Some(user)) => replyTo ! spaceless(V.setup.challengeNotification(game, user))
|
||||
}
|
||||
|
||||
case lila.hub.actorApi.RemindDeployPre =>
|
||||
sender ! spaceless(V.notification.deploy("pre"))
|
||||
case lila.hub.actorApi.RemindDeployPost =>
|
||||
|
|
|
@ -4,19 +4,22 @@ import play.api.data.Form
|
|||
import play.api.i18n.Messages.Implicits._
|
||||
import play.api.libs.json.Json
|
||||
import play.api.mvc.{ Result, Results, Call, RequestHeader, Accepting }
|
||||
import play.api.Play.current
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import lila.api.{ Context, BodyContext }
|
||||
import lila.app._
|
||||
import lila.common.{ HTTPRequest, LilaCookie }
|
||||
import lila.user.UserRepo
|
||||
import views._
|
||||
|
||||
object Challenge extends LilaController {
|
||||
|
||||
private def env = Env.setup
|
||||
private def env = Env.challenge
|
||||
|
||||
private val PostRateLimit = new lila.memo.RateLimit(5, 1 minute)
|
||||
|
||||
def all = Auth { implicit ctx =>
|
||||
me =>
|
||||
env.api.findByDestId(me.id) zip
|
||||
env.api.findByChallengerId(me.id) map {
|
||||
case (out, in) => Ok(env.jsonView.all(in, out)) as JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ object Round extends LilaController with TheftPrevention {
|
|||
}
|
||||
}
|
||||
},
|
||||
Redirect(routes.Setup.await(pov.fullId)).fuccess
|
||||
notFound
|
||||
)
|
||||
},
|
||||
api = apiVersion => {
|
||||
|
@ -176,12 +176,13 @@ object Round extends LilaController with TheftPrevention {
|
|||
Env.tournament.api.miniStanding(tid, ctx.userId, withStanding)
|
||||
}
|
||||
|
||||
// remove me
|
||||
private def join(pov: Pov)(implicit ctx: Context): Fu[Result] =
|
||||
GameRepo initialFen pov.game zip
|
||||
Env.api.roundApi.watcher(pov, lila.api.Mobile.Api.currentVersion, tv = none) zip
|
||||
((pov.player.userId orElse pov.opponent.userId) ?? UserRepo.byId) map {
|
||||
case ((fen, data), opponent) => Ok(html.setup.join(
|
||||
pov, data, opponent, Env.setup.friendConfigMemo get pov.game.id, fen))
|
||||
pov, data, opponent, none, fen))
|
||||
}
|
||||
|
||||
def playerText(fullId: String) = Open { implicit ctx =>
|
||||
|
|
|
@ -80,48 +80,25 @@ object Setup extends LilaController with TheftPrevention {
|
|||
), {
|
||||
case config =>
|
||||
import lila.challenge.Challenge._
|
||||
Env.challenge.api.create(
|
||||
val challenge = lila.challenge.Challenge.make(
|
||||
variant = config.variant,
|
||||
timeControl = config.makeClock.map {
|
||||
case chess.Clock(limit, inc) => TimeControl.Clock(limit, inc)
|
||||
}.orElse config.makeDaysPerTurn.map {
|
||||
timeControl = config.makeClock map { c =>
|
||||
TimeControl.Clock(c.limit, c.increment)
|
||||
} orElse config.makeDaysPerTurn.map {
|
||||
TimeControl.Correspondence.apply
|
||||
}.getOrElse TimeControl.Unlimited,
|
||||
} getOrElse TimeControl.Unlimited,
|
||||
mode = config.mode,
|
||||
color = config.color.name,
|
||||
challenger = ctx.me match {
|
||||
case Some(
|
||||
rated: Boolean,
|
||||
color: String,
|
||||
challenger: Either[String, lila.user.User],
|
||||
destUserId: Option[String]): Challenge = new Challenge(
|
||||
|
||||
|
||||
|
||||
friend config flatMap { pov =>
|
||||
negotiate(
|
||||
html = fuccess(redirectPov(pov, routes.Setup.await(pov.fullId, userId))),
|
||||
api = apiVersion => Env.api.roundApi.player(pov, apiVersion) map { data =>
|
||||
Created(data) as JSON
|
||||
}
|
||||
)
|
||||
}
|
||||
challenger = ctx.me,
|
||||
destUserId = userId)
|
||||
(Env.challenge.api insert challenge) >> negotiate(
|
||||
html = fuccess(Redirect(routes.Challenge.all)),
|
||||
api = apiVersion => ??? // TODO fix me!
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def decline(gameId: String) = Auth { implicit ctx =>
|
||||
me =>
|
||||
OptionResult(GameRepo game gameId) { game =>
|
||||
if (game.started) BadRequest("Cannot decline started challenge")
|
||||
else {
|
||||
GameRepo remove game.id
|
||||
Env.hub.actor.challenger ! lila.hub.actorApi.setup.DeclineChallenge(gameId)
|
||||
Ok("ok")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def hookForm = Open { implicit ctx =>
|
||||
if (HTTPRequest isXhr ctx.req) NoPlaybanOrCurrent {
|
||||
env.forms.hookFilled(timeModeString = get("time")) map { html.setup.hook(_) }
|
||||
|
@ -189,48 +166,27 @@ object Setup extends LilaController with TheftPrevention {
|
|||
|
||||
def join(id: String) = Open { implicit ctx =>
|
||||
OptionFuResult(GameRepo game id) { game =>
|
||||
env.friendJoiner(game, ctx.me).fold(
|
||||
err => negotiate(
|
||||
html = fuccess {
|
||||
Redirect(routes.Round.watcher(id, "white"))
|
||||
},
|
||||
api = _ => fuccess {
|
||||
BadRequest(Json.obj("error" -> err.toString)) as JSON
|
||||
}
|
||||
),
|
||||
_ flatMap {
|
||||
case (p, events) => {
|
||||
Env.hub.socket.round ! lila.hub.actorApi.map.Tell(p.gameId, lila.round.actorApi.EventList(events))
|
||||
negotiate(
|
||||
html = fuccess {
|
||||
implicit val req = ctx.req
|
||||
redirectPov(p, routes.Round.player(p.fullId))
|
||||
},
|
||||
api = apiVersion => Env.api.roundApi.player(p, apiVersion) map { data =>
|
||||
Created(data) as JSON
|
||||
})
|
||||
}
|
||||
})
|
||||
???
|
||||
}
|
||||
}
|
||||
|
||||
def await(fullId: String, userId: Option[String]) = Open { implicit ctx =>
|
||||
OptionFuResult(GameRepo pov fullId) { pov =>
|
||||
pov.game.started.fold(
|
||||
Redirect(routes.Round.player(pov.fullId)).fuccess,
|
||||
Env.api.roundApi.player(pov, lila.api.Mobile.Api.currentVersion) zip
|
||||
(userId ?? UserRepo.named) flatMap {
|
||||
case (data, user) => PreventTheft(pov) {
|
||||
Ok(html.setup.await(
|
||||
pov,
|
||||
data,
|
||||
env.friendConfigMemo get pov.game.id,
|
||||
user)).fuccess
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
// def await(fullId: String, userId: Option[String]) = Open { implicit ctx =>
|
||||
// OptionFuResult(GameRepo pov fullId) { pov =>
|
||||
// pov.game.started.fold(
|
||||
// Redirect(routes.Round.player(pov.fullId)).fuccess,
|
||||
// Env.api.roundApi.player(pov, lila.api.Mobile.Api.currentVersion) zip
|
||||
// (userId ?? UserRepo.named) flatMap {
|
||||
// case (data, user) => PreventTheft(pov) {
|
||||
// Ok(html.setup.await(
|
||||
// pov,
|
||||
// data,
|
||||
// env.friendConfigMemo get pov.game.id,
|
||||
// user)).fuccess
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
def cancel(fullId: String) = Open { implicit ctx =>
|
||||
OptionResult(GameRepo pov fullId) { pov =>
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
@(pov: Pov, data: play.api.libs.json.JsObject, config: Option[lila.setup.FriendConfig], userOption: Option[User])(implicit ctx: Context)
|
||||
|
||||
@import pov._
|
||||
|
||||
@moreJs = {
|
||||
@embedJs {
|
||||
lichess = lichess || {};
|
||||
lichess.prelude = {
|
||||
data: @Html(play.api.libs.json.Json.stringify(data)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@round.layout(
|
||||
title = userOption.isDefined.fold(trans.challengeToPlay,trans.playWithAFriend).str(),
|
||||
moreJs = moreJs,
|
||||
side = Html("")) {
|
||||
<div class="lichess_game">
|
||||
<div class="lichess_board_wrap cg-512">
|
||||
<div class="lichess_board"></div>
|
||||
</div>
|
||||
<div class="lichess_overboard">
|
||||
@userOption.map { user =>
|
||||
<div id="challenge_await" data-user="@user.id">
|
||||
<h2>@trans.challengeToPlay()</h2>
|
||||
@userLink(user, cssClass="target".some)
|
||||
<span class="loader"><span data-icon="U"></span></span>
|
||||
<p class="explanations">
|
||||
@trans.waitingForOpponent()<br />
|
||||
<a href="@routes.Setup.cancel(fullId)">@trans.cancel()</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="challenge_declined" class="none">
|
||||
<h2>Challenge declined</h2>
|
||||
<a href="@routes.Lobby.home()">@trans.playWithAnotherOpponent()</a>
|
||||
</div>
|
||||
}.getOrElse {
|
||||
@trans.toInviteSomeoneToPlayGiveThisUrl():
|
||||
<input id="lichess_id_input" class="copyable" spellcheck="false" readonly="true" value="@protocol@commonDomain@routes.Round.watcher(gameId, "white")" />
|
||||
<button class="copy button" data-rel="lichess_id_input" data-icon="""></button>
|
||||
<p class="explanations">
|
||||
@trans.theFirstPersonToComeOnThisUrlWillPlayWithYou()<br />
|
||||
<a href="@routes.Setup.cancel(fullId)">@trans.cancel()</a>
|
||||
</p>
|
||||
}
|
||||
@config.map { c =>
|
||||
<p class="explanations">
|
||||
@trans.variant(): @views.html.game.variantLink(c.variant, variantName(c.variant))<br />
|
||||
@game.daysPerTurn.map { days =>
|
||||
<span data-icon=";"> @{(days == 1).fold(trans.oneDay(), trans.nbDays(days))}</span>
|
||||
}.getOrElse {
|
||||
<span data-icon="p"> @shortClockName(c.makeClock)</span>
|
||||
}
|
||||
<br />
|
||||
@trans.mode(): @modeName(c.mode)
|
||||
</p>
|
||||
}
|
||||
@base.quote(lila.quote.Quote.one(pov.gameId))
|
||||
</div>
|
||||
</div>
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
@(g: Game, user: User)
|
||||
|
||||
<div id="challenge_reminder_@g.id" data-href="@routes.Round.watcher(g.id, "white")" class="notification">
|
||||
<div class="game_infos" data-icon="@g.perfType match {
|
||||
case _ if g.fromPosition => {*}
|
||||
case Some(p) => {@p.iconChar}
|
||||
case _ => {8}
|
||||
}">
|
||||
@userSpan(user, withOnline = false, withPerfRating = g.perfType)
|
||||
<span class="setup">
|
||||
@g.clock.map(_.show).getOrElse {
|
||||
@g.daysPerTurn.map { days =>
|
||||
@if(days == 1) {@trans.oneDay.en()} else {@trans.nbDays.en(days)}
|
||||
}.getOrElse {∞}
|
||||
} •
|
||||
@if(g.variant.exotic) {
|
||||
@(if (g.variant == chess.variant.KingOfTheHill) g.variant.shortName else g.variant.name)
|
||||
} else {
|
||||
@g.perfType.map(_.name)
|
||||
} • @g.rated.fold(trans.rated.en(), trans.casual.en())
|
||||
</span>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<form action="@routes.Setup.join(g.id)" method="post">
|
||||
<button name="submit" type="submit" class="submit button" data-icon="E" title="@trans.joinTheGame.en()"></button>
|
||||
</form>
|
||||
<a class="button decline" href="@routes.Setup.decline(g.id)" data-icon="L" title="@trans.decline.en()"></a>
|
||||
</div>
|
||||
</div>
|
|
@ -220,7 +220,6 @@ GET /setup/ai controllers.Setup.aiForm
|
|||
POST /setup/ai controllers.Setup.ai
|
||||
GET /setup/friend controllers.Setup.friendForm(user: Option[String] ?= None)
|
||||
POST /setup/friend controllers.Setup.friend(user: Option[String] ?= None)
|
||||
POST /setup/decline controllers.Setup.decline(gameId: String)
|
||||
GET /setup/hook controllers.Setup.hookForm
|
||||
POST /setup/hook/:uid/like/:gameId controllers.Setup.like(uid: String, gameId: String)
|
||||
POST /setup/hook/:uid controllers.Setup.hook(uid: String)
|
||||
|
@ -229,9 +228,10 @@ POST /setup/filter controllers.Setup.filter
|
|||
GET /setup/validate-fen controllers.Setup.validateFen
|
||||
|
||||
# Challenge
|
||||
GET /challenge/$fullId<\w{12}>/await controllers.Setup.await(fullId: String, user: Option[String] ?= None)
|
||||
GET /challenge/$fullId<\w{12}>/cancel controllers.Setup.cancel(fullId: String)
|
||||
POST /challenge/$id<\w{8}>/join controllers.Setup.join(id: String)
|
||||
GET /challenge controllers.Challenge.all
|
||||
# GET /challenge/$fullId<\w{12}>/await controllers.Setup.await(fullId: String, user: Option[String] ?= None)
|
||||
# GET /challenge/$fullId<\w{12}>/cancel controllers.Setup.cancel(fullId: String)
|
||||
# POST /challenge/$id<\w{8}>/join controllers.Setup.join(id: String)
|
||||
|
||||
# Video
|
||||
GET /video controllers.Video.index
|
||||
|
|
|
@ -12,12 +12,7 @@ final class ChallengeApi(
|
|||
|
||||
import BSONHandlers._
|
||||
|
||||
def findByChallengerId(userId: String): Fu[List[Challenge]] =
|
||||
coll.find(BSONDocument("challenger.id" -> userId))
|
||||
.sort(BSONDocument("createdAt" -> -1))
|
||||
.cursor[Challenge]().collect[List]()
|
||||
|
||||
def insert(c: Challenge) =
|
||||
def insert(c: Challenge): Funit =
|
||||
coll.insert(c) >> c.challenger.right.toOption.?? { challenger =>
|
||||
findByChallengerId(challenger.id).flatMap {
|
||||
case challenges if challenges.size <= maxPerUser => funit
|
||||
|
@ -25,6 +20,11 @@ final class ChallengeApi(
|
|||
}
|
||||
}
|
||||
|
||||
def findByChallengerId(userId: String): Fu[List[Challenge]] =
|
||||
coll.find(BSONDocument("challenger.id" -> userId))
|
||||
.sort(BSONDocument("createdAt" -> -1))
|
||||
.cursor[Challenge]().collect[List]()
|
||||
|
||||
def findByDestId(userId: String): Fu[List[Challenge]] =
|
||||
coll.find(BSONDocument("destUserId" -> userId))
|
||||
.sort(BSONDocument("createdAt" -> -1))
|
||||
|
|
|
@ -6,6 +6,10 @@ final class JsonView(getLightUser: String => Option[lila.common.LightUser]) {
|
|||
|
||||
import Challenge._
|
||||
|
||||
def all(in: List[Challenge], out: List[Challenge]) = Json.obj(
|
||||
"in" -> in.map(apply),
|
||||
"out" -> out.map(apply))
|
||||
|
||||
def apply(c: Challenge) = Json.obj(
|
||||
"id" -> c.id,
|
||||
"challenger" -> c.challenger.right.toOption.map { u =>
|
||||
|
|
|
@ -63,11 +63,6 @@ case class MarkCheater(userId: String)
|
|||
case class MarkBooster(userId: String)
|
||||
}
|
||||
|
||||
package setup {
|
||||
case class RemindChallenge(gameId: String, from: String, to: String)
|
||||
case class DeclineChallenge(gameId: String)
|
||||
}
|
||||
|
||||
package captcha {
|
||||
case object AnyCaptcha
|
||||
case class GetCaptcha(id: String)
|
||||
|
|
|
@ -161,8 +161,6 @@ private[round] final class Socket(
|
|||
|
||||
case AnalysisAvailable => notifyAll("analysisAvailable")
|
||||
|
||||
case lila.hub.actorApi.setup.DeclineChallenge(_) => notifyAll("declined")
|
||||
|
||||
case Quit(uid) =>
|
||||
members get uid foreach { member =>
|
||||
quit(uid)
|
||||
|
|
|
@ -80,9 +80,6 @@ private[round] final class SocketHandler(
|
|||
case ("moretime", _) => round(Moretime(playerId))
|
||||
case ("outoftime", _) => round(Outoftime)
|
||||
case ("bye", _) => socket ! Bye(ref.color)
|
||||
case ("challenge", o) => ((o str "d") |@| member.userId) apply {
|
||||
case (to, from) => hub.actor.challenger ! lila.hub.actorApi.setup.RemindChallenge(gameId, from, to)
|
||||
}
|
||||
case ("talk", o) => o str "d" foreach { text =>
|
||||
messenger.owner(gameId, member, text, socket)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import lila.i18n.I18nDomain
|
|||
import lila.lobby.actorApi.{ AddHook, AddSeek }
|
||||
import lila.lobby.Hook
|
||||
import lila.user.{ User, UserContext }
|
||||
import lila.challenge.Challenge
|
||||
import makeTimeout.short
|
||||
import tube.{ userConfigTube, anonConfigTube }
|
||||
|
||||
|
|
2
ui/build
2
ui/build
|
@ -5,7 +5,7 @@ target=${1-dev}
|
|||
|
||||
mkdir -p public/compiled
|
||||
|
||||
for app in insight editor puzzle round analyse lobby tournament tournamentSchedule opening simul perfStat; do
|
||||
for app in challenge insight editor puzzle round analyse lobby tournament tournamentSchedule opening simul perfStat; do
|
||||
cd ui/$app
|
||||
npm install --no-optional && gulp $target
|
||||
cd -
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
var source = require('vinyl-source-stream');
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var watchify = require('watchify');
|
||||
var browserify = require('browserify');
|
||||
var uglify = require('gulp-uglify');
|
||||
var streamify = require('gulp-streamify');
|
||||
|
||||
var sources = ['./src/main.js'];
|
||||
var destination = '../../public/compiled/';
|
||||
var onError = function(error) {
|
||||
gutil.log(gutil.colors.red(error.message));
|
||||
};
|
||||
var standalone = 'LichessChallenge';
|
||||
|
||||
gulp.task('prod', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.challenge.min.js'))
|
||||
.pipe(streamify(uglify()))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('dev', function() {
|
||||
return browserify('./src/main.js', {
|
||||
standalone: standalone
|
||||
}).bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.challenge.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
var opts = watchify.args;
|
||||
opts.debug = true;
|
||||
opts.standalone = standalone;
|
||||
|
||||
var bundleStream = watchify(browserify(sources, opts))
|
||||
.on('update', rebundle)
|
||||
.on('log', gutil.log);
|
||||
|
||||
function rebundle() {
|
||||
return bundleStream.bundle()
|
||||
.on('error', onError)
|
||||
.pipe(source('lichess.challenge.js'))
|
||||
.pipe(gulp.dest(destination));
|
||||
}
|
||||
|
||||
return rebundle();
|
||||
});
|
||||
|
||||
gulp.task('default', ['watch']);
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "challenge",
|
||||
"version": "1.0.0",
|
||||
"description": "lichess.org chess performance statistics",
|
||||
"main": "src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ornicar/lila"
|
||||
},
|
||||
"keywords": [
|
||||
"chess",
|
||||
"lichess",
|
||||
"challenge"
|
||||
],
|
||||
"author": "ornicar",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ornicar/lila/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ornicar/lila",
|
||||
"devDependencies": {
|
||||
"browserify": "~9.0.8",
|
||||
"gulp": "~3.9.0",
|
||||
"gulp-streamify": "~0.0.5",
|
||||
"gulp-uglify": "~1.2.0",
|
||||
"gulp-util": "~3.0.4",
|
||||
"vinyl-source-stream": "~1.1.0",
|
||||
"watchify": "~3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"mithril": "github:ornicar/mithril.js#v1.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
var m = require('mithril');
|
||||
var socket = require('./socket');
|
||||
var xhr = require('./xhr');
|
||||
|
||||
module.exports = function(env) {
|
||||
|
||||
this.data = env.data;
|
||||
|
||||
this.userId = env.userId;
|
||||
|
||||
this.socket = new socket(env.socketSend, this);
|
||||
|
||||
this.vm = {
|
||||
};
|
||||
|
||||
this.trans = lichess.trans(env.i18n);
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
var m = require('mithril');
|
||||
|
||||
module.exports = function(element, opts) {
|
||||
|
||||
m.module(element, {
|
||||
controller: function() {
|
||||
return {
|
||||
data: opts.data
|
||||
};
|
||||
},
|
||||
view: require('./view')
|
||||
});
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
var m = require('mithril');
|
||||
var xhr = require('./xhr');
|
||||
|
||||
module.exports = function(send, ctrl) {
|
||||
|
||||
this.send = send;
|
||||
|
||||
var handlers = {
|
||||
};
|
||||
|
||||
this.receive = function(type, data) {
|
||||
if (handlers[type]) {
|
||||
handlers[type](data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}.bind(this);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
var m = require('mithril');
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
var d = ctrl.data;
|
||||
return m('div.challenges', 'hehe');
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
var m = require('mithril');
|
||||
|
||||
var xhrConfig = function(xhr) {
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader('Accept', 'application/vnd.lichess.v1+json');
|
||||
}
|
||||
|
||||
function uncache(url) {
|
||||
return url + '?_=' + new Date().getTime();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
};
|
Loading…
Reference in New Issue