From 7a366bb59734c4d960f123e547cdd7d2a249cbd8 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 1 Oct 2017 17:56:57 -0500 Subject: [PATCH] edit relay --- app/controllers/Relay.scala | 22 ++++++++++++++++++++-- app/views/relay/create.scala.html | 19 +------------------ app/views/relay/edit.scala.html | 10 ++++++++++ app/views/relay/formLayout.scala.html | 20 ++++++++++++++++++++ conf/routes | 2 ++ modules/relay/src/main/JsonView.scala | 1 + modules/relay/src/main/RelayApi.scala | 7 +++++++ modules/relay/src/main/RelayForm.scala | 2 ++ public/stylesheets/relay.css | 5 +++++ ui/analyse/src/interfaces.ts | 2 +- ui/analyse/src/study/interfaces.ts | 2 +- ui/analyse/src/study/relay/interfaces.ts | 16 ++++++++++++++++ ui/analyse/src/study/relay/relayCtrl.ts | 20 +++----------------- ui/analyse/src/study/relay/relayView.ts | 13 +++++++++++-- ui/analyse/src/study/studyCtrl.ts | 19 ++++++++++--------- ui/analyse/src/study/studyForm.ts | 2 +- 16 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 app/views/relay/edit.scala.html create mode 100644 app/views/relay/formLayout.scala.html create mode 100644 ui/analyse/src/study/relay/interfaces.ts diff --git a/app/controllers/Relay.scala b/app/controllers/Relay.scala index 580a16bbf4..1f70a6d92c 100644 --- a/app/controllers/Relay.scala +++ b/app/controllers/Relay.scala @@ -29,14 +29,30 @@ object Relay extends LilaController { env.forms.create.bindFromRequest.fold( err => BadRequest(html.relay.create(err)).fuccess, setup => env.api.create(setup, me) map { relay => - Redirect(routes.Relay.show(relay.slug, relay.id.value)) + Redirect(showRoute(relay)) } ) } + def edit(slug: String, id: String) = Auth { implicit ctx => implicit me => + OptionFuResult(env.api.byIdAndOwner(RelayModel.Id(id), me)) { relay => + Ok(html.relay.edit(relay, env.forms.edit(relay))).fuccess + } + } + + def update(slug: String, id: String) = AuthBody { implicit ctx => implicit me => + OptionFuResult(env.api.byIdAndOwner(RelayModel.Id(id), me)) { relay => + implicit val req = ctx.body + env.forms.edit(relay).bindFromRequest.fold( + err => BadRequest(html.relay.edit(relay, err)).fuccess, + data => env.api.update(data update relay) inject Redirect(showRoute(relay)) + ) + } + } + def show(slug: String, id: String) = Open { implicit ctx => OptionFuResult(env.api byId RelayModel.Id(id)) { relay => - if (relay.slug != slug) Redirect(routes.Relay.show(relay.slug, relay.id.value)).fuccess + if (relay.slug != slug) Redirect(showRoute(relay)).fuccess else Env.study.api byIdWithChapter relay.studyId flatMap { _ ?? { oldSc => for { @@ -63,4 +79,6 @@ object Relay extends LilaController { } } } + + private def showRoute(r: RelayModel) = routes.Relay.show(r.slug, r.id.value) } diff --git a/app/views/relay/create.scala.html b/app/views/relay/create.scala.html index aab5b641e4..b27282395c 100644 --- a/app/views/relay/create.scala.html +++ b/app/views/relay/create.scala.html @@ -1,23 +1,6 @@ @(form: Form[_])(implicit ctx: Context) -@moreCss = { -@cssTag("flatpickr.css") -@cssTag("material.form.css") -@cssTag("relay.css") -} - -@moreJs = { -@jsAt("vendor/flatpickr/dist/flatpickr.min.js") -@embedJs { -$(".flatpickr").flatpickr(); -} -} - -@base.layout( -title = "New live broadcast", -moreCss = moreCss, -moreJs = moreJs -) { +@formLayout("New live broadcast") {

New live broadcast

diff --git a/app/views/relay/edit.scala.html b/app/views/relay/edit.scala.html new file mode 100644 index 0000000000..2c88cda7a8 --- /dev/null +++ b/app/views/relay/edit.scala.html @@ -0,0 +1,10 @@ +@(r: lila.relay.Relay, form: Form[_])(implicit ctx: Context) + +@formLayout(r.name) { +
+

Edit "@r.name"

+ + @inForm(form) + +
+} diff --git a/app/views/relay/formLayout.scala.html b/app/views/relay/formLayout.scala.html new file mode 100644 index 0000000000..613c83a4af --- /dev/null +++ b/app/views/relay/formLayout.scala.html @@ -0,0 +1,20 @@ +@(title: String)(body: Html)(implicit ctx: Context) + +@moreCss = { +@cssTag("flatpickr.css") +@cssTag("material.form.css") +@cssTag("relay.css") +} + +@moreJs = { +@jsAt("vendor/flatpickr/dist/flatpickr.min.js") +@embedJs { +$(".flatpickr").flatpickr(); +} +} + +@base.layout( +title = title, +moreCss = moreCss, +moreJs = moreJs +)(body) diff --git a/conf/routes b/conf/routes index 77d74af727..7dab221d0d 100644 --- a/conf/routes +++ b/conf/routes @@ -154,6 +154,8 @@ GET /relay controllers.Relay.index GET /relay/new controllers.Relay.form POST /relay/new controllers.Relay.create GET /relay/:slug/$id<\w{8}> controllers.Relay.show(slug: String, id: String) +GET /relay/:slug/$id<\w{8}>/edit controllers.Relay.edit(slug: String, id: String) +POST /relay/:slug/$id<\w{8}>/edit controllers.Relay.update(slug: String, id: String) # GET /study/:slug/$id<\w{8}>/$chapterId<\w{8}> controllers.Relay.chapter(slug: String, id: String, chapterId: String) GET /relay/$id<\w{8}>/socket/v:apiVersion controllers.Relay.websocket(id: String, apiVersion: Int) diff --git a/modules/relay/src/main/JsonView.scala b/modules/relay/src/main/JsonView.scala index 5708af6eca..222df26e9d 100644 --- a/modules/relay/src/main/JsonView.scala +++ b/modules/relay/src/main/JsonView.scala @@ -17,6 +17,7 @@ object JsonView { implicit val relayWrites = OWrites[Relay] { r => Json.obj( "id" -> r.id.value, + "slug" -> r.slug, "name" -> r.name, "description" -> r.description, "ownerId" -> r.ownerId, diff --git a/modules/relay/src/main/RelayApi.scala b/modules/relay/src/main/RelayApi.scala index 7b3463af96..69500aadd7 100644 --- a/modules/relay/src/main/RelayApi.scala +++ b/modules/relay/src/main/RelayApi.scala @@ -18,6 +18,10 @@ final class RelayApi( def byId(id: Relay.Id) = coll.byId[Relay](id.value) + def byIdAndOwner(id: Relay.Id, owner: User) = byId(id) map { + _.filter(_.ownerId == owner.id) + } + def byIdWithStudy(id: Relay.Id): Fu[Option[Relay.WithStudy]] = byId(id) flatMap { _ ?? { relay => @@ -62,6 +66,9 @@ final class RelayApi( ), user) inject relay } + def update(relay: Relay): Funit = + coll.update($id(relay.id), relay).void + def setSync(id: Relay.Id, user: User, v: Boolean, socket: ActorRef): Funit = byId(id) flatMap { _.map(_ setSync v) ?? { relay => coll.update($id(relay.id.value), relay).void >>- { diff --git a/modules/relay/src/main/RelayForm.scala b/modules/relay/src/main/RelayForm.scala index 11de194945..27ee21d131 100644 --- a/modules/relay/src/main/RelayForm.scala +++ b/modules/relay/src/main/RelayForm.scala @@ -26,6 +26,8 @@ object RelayForm { def create = form + def edit(r: Relay) = form fill Data.make(r) + case class Data( name: String, description: String, diff --git a/public/stylesheets/relay.css b/public/stylesheets/relay.css index 37362af7ff..ea4f0effbf 100644 --- a/public/stylesheets/relay.css +++ b/public/stylesheets/relay.css @@ -9,6 +9,11 @@ color: #fff; font-size: 1.2em; padding: 5px 10px; + display: flex; + justify-content: space-between; +} +.relay_edit .relay_wrap h2 a { + color: #fff; } .relay_edit .relay { background: #e0e0e0; diff --git a/ui/analyse/src/interfaces.ts b/ui/analyse/src/interfaces.ts index 6f4e0b8be8..c55a6935cd 100644 --- a/ui/analyse/src/interfaces.ts +++ b/ui/analyse/src/interfaces.ts @@ -1,7 +1,7 @@ import { Player, Status, Source } from 'game'; import * as cg from 'chessground/types'; import { StudyPracticeData, Goal as PracticeGoal } from './study/practice/interfaces'; -import { RelayData } from './study/relay/relayCtrl'; +import { RelayData } from './study/relay/interfaces'; export type MaybeVNode = VNode | string | null | undefined; export type MaybeVNodes = MaybeVNode[] diff --git a/ui/analyse/src/study/interfaces.ts b/ui/analyse/src/study/interfaces.ts index 33d9a4d726..6db0700818 100644 --- a/ui/analyse/src/study/interfaces.ts +++ b/ui/analyse/src/study/interfaces.ts @@ -5,7 +5,7 @@ import { StudyPracticeCtrl } from './practice/interfaces'; import { ChapterDescriptionCtrl } from './chapterDescription'; import GamebookPlayCtrl from './gamebook/gamebookPlayCtrl'; import { GamebookOverride } from './gamebook/interfaces'; -import { RelayCtrl } from './relay/relayCtrl'; +import RelayCtrl from './relay/relayCtrl'; export interface StudyCtrl { data: StudyData; diff --git a/ui/analyse/src/study/relay/interfaces.ts b/ui/analyse/src/study/relay/interfaces.ts new file mode 100644 index 0000000000..d843d04b64 --- /dev/null +++ b/ui/analyse/src/study/relay/interfaces.ts @@ -0,0 +1,16 @@ +export interface RelayData { + sync: RelaySync; + id: string; + slug: string; +} + +export interface RelaySync { + seconds?: number; // how long until lichess stops syncing + url: string; + log: LogEvent[]; +} + +export interface LogEvent { + error?: string; + at: number; +} diff --git a/ui/analyse/src/study/relay/relayCtrl.ts b/ui/analyse/src/study/relay/relayCtrl.ts index dd1ec147a0..249c463edc 100644 --- a/ui/analyse/src/study/relay/relayCtrl.ts +++ b/ui/analyse/src/study/relay/relayCtrl.ts @@ -1,25 +1,11 @@ +import { RelayData, LogEvent } from './interfaces'; -export interface RelayData { - sync: RelaySync; -} - -interface LogEvent { - error?: string; - at: number; -} - -interface RelaySync { - seconds?: number; // how long until lichess stops syncing - url: string; - log: LogEvent[]; -} - -export class RelayCtrl { +export default class RelayCtrl { data: RelayData; log: LogEvent[] = []; - constructor(d: RelayData, readonly send: SocketSend, readonly redraw: () => void) { + constructor(d: RelayData, readonly send: SocketSend, readonly redraw: () => void, readonly isOwner: () => boolean) { this.data = d; } diff --git a/ui/analyse/src/study/relay/relayView.ts b/ui/analyse/src/study/relay/relayView.ts index d44caa7cb1..5dddb5dc25 100644 --- a/ui/analyse/src/study/relay/relayView.ts +++ b/ui/analyse/src/study/relay/relayView.ts @@ -1,12 +1,21 @@ import { h } from 'snabbdom'; import { VNode } from 'snabbdom/vnode'; -import { RelayCtrl, RelayData } from './relayCtrl'; +import RelayCtrl from './relayCtrl'; +import { RelayData } from './interfaces'; import { iconTag, bind } from '../../util'; export default function(ctrl: RelayCtrl): VNode { const d = ctrl.data; return h('div.relay_wrap', [ - h('h2', 'Relay manager'), + h('h2', [ + 'Relay manager', + ctrl.isOwner() ? h('a', { + attrs: { + href: `/relay/${d.slug}/${d.id}/edit`, + 'data-icon': '%' + } + }) : null + ]), h('div.relay', [ (d.sync.seconds ? stateOn : stateOff)(ctrl), renderLog(d) diff --git a/ui/analyse/src/study/studyCtrl.ts b/ui/analyse/src/study/studyCtrl.ts index 618f2d46c0..609202e29a 100644 --- a/ui/analyse/src/study/studyCtrl.ts +++ b/ui/analyse/src/study/studyCtrl.ts @@ -16,7 +16,8 @@ import { path as treePath } from 'tree'; import { StudyCtrl, StudyVm, Tab, TagTypes, StudyData, StudyChapterMeta, ReloadData } from './interfaces'; import GamebookPlayCtrl from './gamebook/gamebookPlayCtrl'; import { ChapterDescriptionCtrl } from './chapterDescription'; -import { RelayCtrl, RelayData } from './relay/relayCtrl'; +import RelayCtrl from './relay/relayCtrl'; +import { RelayData } from './relay/interfaces'; const li = window.lichess; @@ -49,16 +50,8 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes, }; })(); - const relay = relayData ? new RelayCtrl(relayData, send, redraw) : undefined; - const notif = notifCtrl(redraw); - const form: StudyFormCtrl = studyFormCtrl((d, isNew) => { - send("editStudy", d); - if (isNew && data.chapter.setup.variant.key === 'standard' && ctrl.mainline.length === 1 && !data.chapter.setup.fromFen && !relay) - chapters.newForm.openInitial(); - }, () => data, redraw, relay); - function startTour() { tours.study(ctrl); }; @@ -91,6 +84,14 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes, return ctrl.opts.userId === data.chapter.ownerId; }; + const relay = relayData ? new RelayCtrl(relayData, send, redraw, members.isOwner) : undefined; + + const form: StudyFormCtrl = studyFormCtrl((d, isNew) => { + send("editStudy", d); + if (isNew && data.chapter.setup.variant.key === 'standard' && ctrl.mainline.length === 1 && !data.chapter.setup.fromFen && !relay) + chapters.newForm.openInitial(); + }, () => data, redraw, relay); + function isWriting(): boolean { return vm.mode.write && !isGamebookPlay(); } diff --git a/ui/analyse/src/study/studyForm.ts b/ui/analyse/src/study/studyForm.ts index 98f0b387c5..1931755a75 100644 --- a/ui/analyse/src/study/studyForm.ts +++ b/ui/analyse/src/study/studyForm.ts @@ -5,7 +5,7 @@ import { prop, Prop } from 'common'; import { bind, bindSubmit } from '../util'; import { StudyData } from './interfaces'; import { MaybeVNodes } from '../interfaces'; -import { RelayCtrl } from './relay/relayCtrl'; +import RelayCtrl from './relay/relayCtrl'; export interface StudyFormCtrl { open: Prop;