client-side DGT form persistence

infinitescroll-v2
Thibault Duplessis 2020-09-07 18:40:04 +02:00
parent 2f95de1f34
commit 15e023d0f4
3 changed files with 134 additions and 45 deletions

View File

@ -31,8 +31,11 @@ final class DgtCtrl(env: Env) extends LilaController(env) {
}
def play =
Auth { implicit ctx => _ =>
Ok(views.html.dgt.play).fuccess
Auth { implicit ctx => me =>
findToken(me) map { t =>
if (t.isEmpty) Redirect(routes.DgtCtrl.config())
else Ok(views.html.dgt.play)
}
}
private val dgtScopes: Set[OAuthScope] = {

View File

@ -1,6 +1,7 @@
package views.html
import controllers.routes
import scala.util.chaining._
import lila.api.Context
import lila.app.templating.Environment._
@ -51,28 +52,32 @@ object dgt {
layout("config", embedJsUnsafeLoadThen("lichessDgt.configPage()"))(
div(cls := "account")(
h1("DGT - configure"),
st.section(
h2("Lichess connectivity"),
if (token.isDefined)
p(cls := "text", dataIcon := "E")("You have an OAuth token suitable for DGT play.")
else
form(action := routes.DgtCtrl.generateToken(), method := "post")(
p("No suitable OAuth token available yet."),
form3.submit("Click to generate one")
)
form(action := routes.DgtCtrl.generateToken(), method := "post")(
st.section(
h2("Lichess connectivity"),
if (token.isDefined)
p(cls := "text", dataIcon := "E")("You have an OAuth token suitable for DGT play.")
else
frag(
p("No suitable OAuth token available yet."),
form3.submit("Click to generate one")
)
)
),
form(cls := "form3")(
form(cls := "form3", id := "dgt-config")(
st.section(
h2("DGT board connectivity"),
div(cls := "form-group")(
st.label(`for` := "dgt-livechess-url", cls := "form-label")(
s"LiveChess $liveChessVersion WebSocket URL"
),
st.input(id := "dgt-livechess-url", cls := "form-control", required := true),
st.small(cls := "form-help")(
"""Use "ws://localhost:1982/api/v1.0" unless LiveChess is running on a different machine or different port."""
"dgt-livechess-url" pipe { name =>
div(cls := "form-group")(
st.label(`for` := name, cls := "form-label")(
s"LiveChess $liveChessVersion WebSocket URL"
),
st.input(id := name, st.name := name, cls := "form-control", required := true),
st.small(cls := "form-help")(
"""Use "ws://localhost:1982/api/v1.0" unless LiveChess is running on a different machine or different port."""
)
)
)
}
),
st.section(
h2("Text to speech"),
@ -83,20 +88,22 @@ object dgt {
st.label(cls := "form-label")("Enable Speech Synthesis"),
radios(
"dgt-speech-synthesis",
List((0, trans.no.txt()), (1, trans.yes.txt()))
List((false, trans.no.txt()), (true, trans.yes.txt()))
)
),
div(cls := "form-group")(
st.label(`for` := "dgt-voice-select", cls := "form-label")(
s"Speech synthesis voice"
),
st.select(id := "dgt-voice-select", cls := "form-control")
),
"dgt-speech-voice" pipe { name =>
div(cls := "form-group")(
st.label(`for` := name, cls := "form-label")(
s"Speech synthesis voice"
),
st.select(id := name, st.name := name, cls := "form-control")
)
},
div(cls := "form-group")(
st.label(cls := "form-label")("Announce All Moves"),
radios(
"dgt-speech-announce-al-moves",
List((0, trans.no.txt()), (1, trans.yes.txt()))
"dgt-speech-announce-all-moves",
List((false, trans.no.txt()), (true, trans.yes.txt()))
),
st.small(cls := "form-help")(
"""Select YES to annouce both your moves and your opponent's moves. Select NO to annouce only your opponent's moves."""
@ -106,24 +113,27 @@ object dgt {
st.label(cls := "form-label")("Announce Move Format"),
radios(
"dgt-speech-announce-move-format",
List((0, "SAN (Nf6)"), (1, "UCI (g8f6)"))
List(("san", "SAN (Nf6)"), ("uci", "UCI (g8f6)"))
),
st.small(cls := "form-help")(
"""San is the standard on Lichess like "Nf6". UCI is common on engines like "g8f6""""
)
),
div(cls := "form-group")(
st.label(`for` := "dgt-speech-keywords", cls := "form-label")("Keywords"),
st.textarea(
id := "dgt-speech-keywords",
cls := "form-control",
maxlength := 600,
rows := 10
),
st.small(cls := "form-help")(
"""Keywords are in JSON format. They are used to translate moves and results into your language. Default is English, but feel free to change it."""
"dgt-speech-keywords" pipe { name =>
div(cls := "form-group")(
st.label(`for` := name, cls := "form-label")("Keywords"),
st.textarea(
id := name,
st.name := name,
cls := "form-control",
maxlength := 600,
rows := 10
),
st.small(cls := "form-help")(
"""Keywords are in JSON format. They are used to translate moves and results into your language. Default is English, but feel free to change it."""
)
)
)
}
),
st.section(
h2("Debug"),
@ -131,13 +141,14 @@ object dgt {
st.label(cls := "form-label")("Verbose logging"),
radios(
"dgt-verbose",
List((0, trans.no.txt()), (1, trans.yes.txt()))
List((false, trans.no.txt()), (true, trans.yes.txt()))
),
st.small(cls := "form-help")(
"""To see console message press Command + Option + C (Mac) or Control + Shift + C (Windows, Linux, Chrome OS)"""
)
)
)
),
form3.submit(trans.save())
)
)
)
@ -145,7 +156,7 @@ object dgt {
private def radios(name: String, options: Iterable[(Any, String)]) =
st.group(cls := "radio")(
options.map { v =>
val id = s"dgt-${name}_${v._1}"
val id = s"${name}_${v._1}"
div(
input(
st.id := id,

View File

@ -1,5 +1,80 @@
export function configPage() {
console.log('config');
const form = document.getElementById('dgt-config') as HTMLFormElement,
voiceSelector = document.getElementById("dgt-speech-voice") as HTMLSelectElement;
(function populateVoiceList() {
if (typeof speechSynthesis === 'undefined') return;
speechSynthesis.getVoices().forEach((voice, i) => {
const option = document.createElement('option');
option.value = voice.name;
option.textContent = voice.name + ' (' + voice.lang + ')';
if (voice.default) option.textContent += ' -- DEFAULT';
voiceSelector.appendChild(option);
if (voice.name == localStorage.getItem("dgt-speech-voice")) voiceSelector.selectedIndex = i;
});
speechSynthesis.onvoiceschanged = populateVoiceList;
})();
const defaultSpeechKeywords = {
"K": "King",
"Q": "Queen",
"R": "Rook",
"B": "Bishop",
"N": "Knight",
"P": "Pawn",
"x": "Takes",
"+": "Check",
"#": "Checkmate",
"(=)": "Game ends in draw",
"O-O-O": "Castles queenside",
"O-O": "Castles kingside",
"white": "White",
"black": "Black",
"wins by": "wins by",
"timeout": "timeout",
"resignation": "resignation",
"illegal": "illegal",
"move": "move"
};
function ensureDefaults() {
[
['dgt-livechess-url', 'ws://localhost:1982/api/v1.0'],
['dgt-speech-keywords', JSON.stringify(defaultSpeechKeywords, undefined, 2)],
['dgt-speech-synthesis', 'true'],
['dgt-speech-announce-all-moves', 'true'],
['dgt-speech-announce-move-format', 'san'],
['dgt-verbose', 'false']
].forEach(([k, v]) => {
if (!localStorage.getItem(k)) localStorage.setItem(k, v)
});
}
function populateForm() {
['dgt-livechess-url', 'dgt-speech-keywords'].forEach(k => {
form[k].value = localStorage.getItem(k)
});
['dgt-speech-synthesis', 'dgt-speech-announce-all-moves', 'dgt-verbose'].forEach(k =>
[true, false].forEach(v => {
const input = document.getElementById(`${k}_${v}`) as HTMLInputElement;
input.checked = localStorage.getItem(k) == '' + v;
})
);
['san', 'uci'].forEach(v => {
const k = 'dgt-speech-announce-move-format';
const input = document.getElementById(`${k}_${v}`) as HTMLInputElement;
input.checked = localStorage.getItem(k) == '' + v;
});
}
ensureDefaults();
populateForm();
form.addEventListener('submit', (e: Event) => {
e.preventDefault();
Array.from(new FormData(form).entries()).forEach(([k, v]) => localStorage.setItem(k, v.toString()));
});
}
export function playPage() {