broadcast intro markdown

relay-intro
Thibault Duplessis 2019-08-29 11:53:09 +02:00
parent bdf238971a
commit 15691e7fec
14 changed files with 104 additions and 29 deletions

View File

@ -86,7 +86,7 @@ object Relay extends LilaController {
private def doShow(relay: RelayModel, oldSc: lila.study.Study.WithChapter)(implicit ctx: Context): Fu[Result] = for {
(sc, studyData) <- Study.getJsonData(oldSc)
data = lila.relay.JsonView.makeData(relay, studyData)
data = env.jsonView.makeData(relay, studyData)
chat <- Study.chatOf(sc.study)
sVersion <- Env.study.version(sc.study.id)
streams <- Study.streamsOf(sc.study)

View File

@ -37,7 +37,10 @@ object form {
private def inner(form: Form[_], url: play.api.mvc.Call)(implicit ctx: Context) =
postForm(cls := "form3", action := url)(
form3.group(form("name"), frag("Event name"))(form3.input(_)(autofocus)),
form3.group(form("description"), raw("Event description"))(form3.textarea(_)(rows := 6)),
form3.group(form("description"), raw("Short event description"))(form3.textarea(_)(rows := 2)),
form3.group(form("markup"), raw("Full event description"), help = frag(
a(href := "https://guides.github.com/features/mastering-markdown/", target := "_blank")("Markdown"), " is available"
).some)(form3.textarea(_)(rows := 10)),
if (isGranted(_.Relay))
form3.checkbox(form("official"), raw("Official lichess broadcast"), help = raw("Feature on /broadcast - for admins only").some)
else form3.hidden(form("official")),

View File

@ -31,7 +31,7 @@ scriptClasspath := Seq("*")
libraryDependencies ++= Seq(
scalaz, chess, compression, scalalib, hasher, typesafeConfig, findbugs,
reactivemongo.driver, reactivemongo.iteratees, akka.actor, akka.slf4j,
maxmind, prismic, netty, guava,
maxmind, prismic, netty, guava, markdown,
kamon.core, kamon.influxdb, scalatags,
java8compat, semver, scrimage, scalaConfigs, scaffeine, lettuce, epoll
)
@ -272,7 +272,7 @@ lazy val study = module("study", Seq(
)
lazy val relay = module("relay", Seq(common, study)).settings(
libraryDependencies ++= Seq(scalaUri) ++ provided(play.api, reactivemongo.driver)
libraryDependencies ++= Seq(scalaUri) ++ provided(play.api, reactivemongo.driver, markdown)
)
lazy val studySearch = module("studySearch", Seq(common, hub, study, search)).settings(

View File

@ -23,11 +23,14 @@ final class Env(
private val withStudy = new RelayWithStudy(studyEnv.api)
val jsonView = new JsonView(new RelayMarkup)
val api = new RelayApi(
repo = repo,
studyApi = studyEnv.api,
socketMap = studyEnv.socketMap,
withStudy = withStudy,
jsonView = jsonView,
clearFormatCache = formatApi.refresh,
system = system
)

View File

@ -2,21 +2,9 @@ package lila.relay
import play.api.libs.json._
object JsonView {
final class JsonView(markup: RelayMarkup) {
implicit val syncLogEventWrites = Json.writes[SyncLog.Event]
implicit val idWrites = Writes[Relay.Id] { id =>
JsString(id.value)
}
private implicit val syncWrites = OWrites[Relay.Sync] { s =>
Json.obj(
"ongoing" -> s.ongoing,
"log" -> s.log.events,
"url" -> s.upstream.url
)
}
import JsonView._
implicit val relayWrites = OWrites[Relay] { r =>
Json.obj(
@ -27,13 +15,31 @@ object JsonView {
"ownerId" -> r.ownerId,
"sync" -> r.sync
).add("credit", r.credit)
.add("markup" -> r.markup.map(markup.apply))
}
case class JsData(relay: JsObject, study: JsObject, analysis: JsObject)
def makeData(relay: Relay, studyData: lila.study.JsonView.JsData) = JsData(
relay = relayWrites writes relay,
study = studyData.study,
analysis = studyData.analysis
)
}
object JsonView {
case class JsData(relay: JsObject, study: JsObject, analysis: JsObject)
implicit val syncLogEventWrites = Json.writes[SyncLog.Event]
implicit val idWrites: Writes[Relay.Id] = Writes[Relay.Id] { id =>
JsString(id.value)
}
private implicit val syncWrites: OWrites[Relay.Sync] = OWrites[Relay.Sync] { s =>
Json.obj(
"ongoing" -> s.ongoing,
"log" -> s.log.events,
"url" -> s.upstream.url
)
}
}

View File

@ -9,6 +9,7 @@ case class Relay(
_id: Relay.Id,
name: String,
description: String,
markup: Option[String] = None,
credit: Option[String] = None,
sync: Relay.Sync,
ownerId: User.ID,

View File

@ -17,6 +17,7 @@ final class RelayApi(
studyApi: StudyApi,
socketMap: lila.study.SocketMap,
withStudy: RelayWithStudy,
jsonView: JsonView,
clearFormatCache: Url => Unit,
system: ActorSystem
) {
@ -126,7 +127,7 @@ final class RelayApi(
repo.coll.remove($id(Relay.Id(studyId))).void
private[relay] def publishRelay(relay: Relay): Funit =
sendToContributors(relay.id, "relayData", JsonView.relayWrites writes relay)
sendToContributors(relay.id, "relayData", jsonView.relayWrites writes relay)
private def sendToContributors(id: Relay.Id, t: String, msg: JsObject): Funit =
studyApi members Study.Id(id.value) map {

View File

@ -14,7 +14,8 @@ object RelayForm {
val form = Form(mapping(
"name" -> text(minLength = 3, maxLength = 80),
"description" -> text(minLength = 3, maxLength = 4000),
"description" -> text(minLength = 3, maxLength = 400),
"markup" -> optional(text(maxLength = 9000)),
"official" -> optional(boolean),
"syncUrl" -> nonEmptyText.verifying("Lichess tournaments can't be used as broadcast source", u => !isTournamentApi(u)),
"credit" -> optional(nonEmptyText),
@ -32,6 +33,7 @@ object RelayForm {
case class Data(
name: String,
description: String,
markup: Option[String],
official: Option[Boolean],
syncUrl: String,
credit: Option[String],
@ -48,6 +50,7 @@ object RelayForm {
def update(relay: Relay, user: User) = relay.copy(
name = name,
description = description,
markup = markup,
official = ~official && Granter(_.Relay)(user),
sync = makeSync,
credit = credit,
@ -67,6 +70,7 @@ object RelayForm {
_id = Relay.makeId,
name = name,
description = description,
markup = markup,
ownerId = user.id,
sync = makeSync,
credit = credit,
@ -84,6 +88,7 @@ object RelayForm {
def make(relay: Relay) = Data(
name = relay.name,
description = relay.description,
markup = relay.markup,
official = relay.official option true,
syncUrl = relay.sync.upstream.url,
credit = relay.credit,

View File

@ -0,0 +1,33 @@
package lila.relay
import scala.concurrent.duration._
import java.util.Arrays
import com.vladsch.flexmark.html.HtmlRenderer
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.util.ast.Node
import com.vladsch.flexmark.util.data.MutableDataSet
import com.vladsch.flexmark.ext.tables.TablesExtension
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
import com.github.blemale.scaffeine.{ Cache, Scaffeine }
private final class RelayMarkup {
type Text = String
type Html = String
private val options = new MutableDataSet()
options.set(Parser.EXTENSIONS, Arrays.asList(TablesExtension.create(), StrikethroughExtension.create()))
options.set(HtmlRenderer.SOFT_BREAK, "<br />\n")
options.set(TablesExtension.CLASS_NAME, "slist")
private val parser = Parser.builder(options).build()
private val renderer = HtmlRenderer.builder(options).build()
private val cache: Cache[Text, Html] = Scaffeine()
.expireAfterWrite(5 minutes)
.build[Text, Html]
private def compute(text: Text): Html =
renderer.render(parser.parse(text))
def apply(text: Text): Html = cache.get(text, compute)
}

View File

@ -45,6 +45,7 @@ object Dependencies {
val scalatags = "com.lihaoyi" %% "scalatags" % "0.6.7"
val lettuce = "io.lettuce" % "lettuce-core" % "5.1.8.RELEASE"
val epoll = "io.netty" % "netty-transport-native-epoll" % "4.1.36.Final" classifier "linux-x86_64"
val markdown = "com.vladsch.flexmark" % "flexmark-all" % "0.50.30"
object reactivemongo {
val version = "0.12.4"

View File

@ -7,7 +7,28 @@
@include fluid-size('font-size', 15px, 27px);
margin-bottom: 3vh;
}
}
.study__name {
h2, h3, h4 {
line-height: 2em;
}
h2 { font-size: 1.8em; }
h3 { font-size: 1.5em; line-height: 2em }
h4 { font-size: 1.3em; }
em {
font-style: italic;
}
ul li {
list-style: disc outside;
margin: .5em 0 0 1.5em;
}
ol li {
list-style: decimal inside;
margin: .5em 0;
}
li {
margin-left: 2em;
p {
display: inline;
}
}
}
}

View File

@ -1,7 +1,8 @@
export interface RelayData {
id: string;
slug: string;
description?: string;
description: string;
markup?: string;
credit?: string;
sync: RelaySync;
}

View File

@ -12,8 +12,8 @@ export default class RelayCtrl {
constructor(public data: RelayData, readonly send: SocketSend, readonly redraw: () => void, readonly members: any, chapter: StudyChapter) {
this.applyChapterRelay(chapter, chapter.relay);
this.intro = {
exists: !!data.description,
active: !!data.description,
exists: !!data.markup,
active: !!data.markup,
disable: () => { this.intro.active = false }
};
}

View File

@ -1,7 +1,7 @@
import { h } from 'snabbdom';
import { VNode } from 'snabbdom/vnode';
import AnalyseCtrl from '../../ctrl';
import { richHTML } from '../../util';
import { innerHTML } from '../../util';
import { view as multiBoardView } from '../multiBoard';
export default function(ctrl: AnalyseCtrl): VNode | undefined {
@ -11,7 +11,7 @@ export default function(ctrl: AnalyseCtrl): VNode | undefined {
h('div.intro__text', [
h('h1', study.data.name),
h('div', {
hook: richHTML(relay.data.description || '')
hook: innerHTML(relay.data.markup, () => relay.data.markup!)
})
]),
multiBoardView(study.multiBoard, study)