diff --git a/modules/relay/src/main/Relay.scala b/modules/relay/src/main/Relay.scala index 9dae03507b..2080b4a03e 100644 --- a/modules/relay/src/main/Relay.scala +++ b/modules/relay/src/main/Relay.scala @@ -24,11 +24,7 @@ case class Relay( if (s.isEmpty) "-" else s } - def setSync(v: Boolean) = copy( - sync = sync.copy( - until = v option DateTime.now.plusHours(3) - ) - ) + def setSync(v: Boolean) = copy(sync = sync set v) override def toString = s"id:$id sync:$sync" } @@ -44,6 +40,11 @@ object Relay { def seconds: Option[Int] = until map { until => (until.getSeconds - nowSeconds).toInt } filter (0<) + + def set(v: Boolean) = copy( + until = v option DateTime.now.plusHours(3), + log = SyncLog(Vector.empty) + ) } object Sync { diff --git a/modules/relay/src/main/RelayFetch.scala b/modules/relay/src/main/RelayFetch.scala index f5ac906ffb..89178828e5 100644 --- a/modules/relay/src/main/RelayFetch.scala +++ b/modules/relay/src/main/RelayFetch.scala @@ -23,7 +23,7 @@ private final class RelayFetch( case object Tick def scheduleNext = - context.system.scheduler.scheduleOnce(3 seconds, self, Tick) + context.system.scheduler.scheduleOnce(5 seconds, self, Tick) def receive = { @@ -34,10 +34,10 @@ private final class RelayFetch( case Tick => val startAt = nowMillis - getSyncable().flatMap { + getSyncable().map(_ filter RelayFetch.shouldFetchNow).flatMap { _.map { relay => RelayFetch(relay.sync.upstream) - .withTimeout(3 seconds, LilaException(s"Request timeout"))(context.system) flatMap { + .withTimeout(3 seconds, LilaException("Request timeout"))(context.system) flatMap { sync(relay, _) } flatMap { res => addLog(relay.id, SyncLog.Event(none, DateTime.now)) inject res @@ -53,6 +53,10 @@ private final class RelayFetch( private object RelayFetch { + def shouldFetchNow(r: Relay) = !r.sync.log.alwaysFails || { + r.sync.log.updatedAt ?? { DateTime.now.minusSeconds(30).isAfter } + } + import Relay.Sync.Upstream import RelaySync.MultiPgn diff --git a/modules/relay/src/main/RelaySync.scala b/modules/relay/src/main/RelaySync.scala index 8babb82275..e1eb94562d 100644 --- a/modules/relay/src/main/RelaySync.scala +++ b/modules/relay/src/main/RelaySync.scala @@ -44,14 +44,15 @@ private final class RelaySync( } case (found, _) => found } match { - case (path, None) => // no new nodes were found - !Path.isMainline(chapter.root, path) ?? studyApi.promote( - userId = chapter.ownerId, - studyId = study.id, - position = Position(chapter, path).ref, - toMainline = true, - uid = socketUid - ) + // fix mainline, and call it a day + case (path, _) if !Path.isMainline(chapter.root, path) => studyApi.promote( + userId = chapter.ownerId, + studyId = study.id, + position = Position(chapter, path).ref, + toMainline = true, + uid = socketUid + ) + case (path, None) => funit // no new nodes were found case (path, Some(node)) => // append new nodes to the chapter lila.common.Future.fold(node.mainline)(Position(chapter, path)) { case (position, n) => studyApi.doAddNode( diff --git a/modules/relay/src/main/SyncLog.scala b/modules/relay/src/main/SyncLog.scala index 1ae2002d3b..afdd8cf9d4 100644 --- a/modules/relay/src/main/SyncLog.scala +++ b/modules/relay/src/main/SyncLog.scala @@ -5,6 +5,10 @@ import org.joda.time.DateTime case class SyncLog(events: Vector[SyncLog.Event]) extends AnyVal { def isOk = events.lastOption ?? (_.isOk) + + def alwaysFails = events.size == SyncLog.historySize && events.forall(_.isKo) + + def updatedAt = events.lastOption.map(_.at) } object SyncLog { @@ -16,5 +20,6 @@ object SyncLog { at: DateTime ) { def isOk = error.isEmpty + def isKo = error.nonEmpty } } diff --git a/public/stylesheets/relay.css b/public/stylesheets/relay.css index ded5cb92e1..37362af7ff 100644 --- a/public/stylesheets/relay.css +++ b/public/stylesheets/relay.css @@ -1,8 +1,8 @@ .relay_edit .lichess_game .lichess_ground { - margin-bottom: -337px; + margin-bottom: -237px; } .relay_edit .relay_wrap { - height: 300px; + height: 200px; } .relay_edit .relay_wrap h2 { background: #666; @@ -15,7 +15,7 @@ display: flex; flex-flow: column; border: 1px solid #ccc; - border-width: 0 1px; + border-top-width: 0; } .relay_edit .relay .state { display: flex; @@ -55,10 +55,12 @@ } .relay_edit .relay .log { + overflow-x: hidden; overflow-y: auto; + max-height: 150px; } .relay_edit .relay .log > div { - margin: 3px 0; + margin: 4px 0; display: flex; } .relay_edit .relay .log time { diff --git a/ui/analyse/src/study/chapterNewForm.ts b/ui/analyse/src/study/chapterNewForm.ts index 40b5e08ebf..d3a44220b5 100644 --- a/ui/analyse/src/study/chapterNewForm.ts +++ b/ui/analyse/src/study/chapterNewForm.ts @@ -140,6 +140,7 @@ export function view(ctrl): VNode { const currentChapterSetup = ctrl.root.study.data.chapter.setup; return dialog.form({ + class: 'chapter-new', onClose() { ctrl.close(); ctrl.redraw(); diff --git a/ui/analyse/src/study/relay/relayView.ts b/ui/analyse/src/study/relay/relayView.ts index d482593487..c7500d3189 100644 --- a/ui/analyse/src/study/relay/relayView.ts +++ b/ui/analyse/src/study/relay/relayView.ts @@ -22,7 +22,7 @@ function renderLog(log: LogEvent[]) { }, [ iconTag(err ? 'j' : 'E'), h('div', [ - err || 'Synchronisation successful', + err || 'Success', h('time', window.lichess.timeago.absolute(e.at)) ]) ]); @@ -33,7 +33,7 @@ function stateOn(ctrl: RelayCtrl) { return h('div.state.on.clickable', { hook: bind('click', _ => ctrl.setSync(false)) }, [ - iconTag('E'), + iconTag('B'), h('div', [ 'Connected to PGN source', h('div.timer', { diff --git a/ui/analyse/src/study/studyCtrl.ts b/ui/analyse/src/study/studyCtrl.ts index 9d88559b25..618f2d46c0 100644 --- a/ui/analyse/src/study/studyCtrl.ts +++ b/ui/analyse/src/study/studyCtrl.ts @@ -55,7 +55,7 @@ export default function(data: StudyData, ctrl: AnalyseCtrl, tagTypes: TagTypes, 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) + if (isNew && data.chapter.setup.variant.key === 'standard' && ctrl.mainline.length === 1 && !data.chapter.setup.fromFen && !relay) chapters.newForm.openInitial(); }, () => data, redraw, relay); diff --git a/ui/analyse/src/study/studyForm.ts b/ui/analyse/src/study/studyForm.ts index c5637e19ae..98f0b387c5 100644 --- a/ui/analyse/src/study/studyForm.ts +++ b/ui/analyse/src/study/studyForm.ts @@ -98,6 +98,7 @@ export function view(ctrl: StudyFormCtrl): VNode { } } return dialog.form({ + class: 'study-edit', onClose: function() { ctrl.open(false); ctrl.redraw(); @@ -108,7 +109,8 @@ export function view(ctrl: StudyFormCtrl): VNode { hook: bindSubmit(e => { const obj: FormData = {}; 'name visibility computer explorer cloneable chat sticky'.split(' ').forEach(n => { - obj[n] = ((e.target as HTMLElement).querySelector('#study-' + n) as HTMLInputElement).value; + const el = ((e.target as HTMLElement).querySelector('#study-' + n) as HTMLInputElement); + if (el) obj[n] = el.value; }); ctrl.save(obj, isNew); }, ctrl.redraw)