edit relay

pull/3627/head
Thibault Duplessis 2017-10-01 17:56:57 -05:00
parent a31a4c655e
commit 7a366bb597
16 changed files with 111 additions and 51 deletions

View File

@ -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)
}

View File

@ -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") {
<div class="relay_form content_box small_box no_padding">
<h1 class="lichess_title">New live broadcast</h1>
<form class="content_box_content material form" action="@routes.Relay.create" method="POST">

View File

@ -0,0 +1,10 @@
@(r: lila.relay.Relay, form: Form[_])(implicit ctx: Context)
@formLayout(r.name) {
<div class="relay_form content_box small_box no_padding">
<h1 class="lichess_title">Edit "@r.name"</h1>
<form class="content_box_content material form" action="@routes.Relay.update(r.slug, r.id.value)" method="POST">
@inForm(form)
</form>
</div>
}

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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 >>- {

View File

@ -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,

View File

@ -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;

View File

@ -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[]

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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<boolean>;