broadcast intro markdown
parent
bdf238971a
commit
15691e7fec
|
@ -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)
|
||||
|
|
|
@ -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")),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
export interface RelayData {
|
||||
id: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
description: string;
|
||||
markup?: string;
|
||||
credit?: string;
|
||||
sync: RelaySync;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue