more new chat WIP
parent
2f804acfec
commit
1f8528f8c7
|
@ -225,6 +225,13 @@ object Round extends LilaController with TheftPrevention {
|
|||
text => Env.round.noteApi.set(gameId, me.id, text.trim take 10000))
|
||||
}
|
||||
|
||||
def readNote(gameId: String) = Auth { implicit ctx =>
|
||||
me =>
|
||||
Env.round.noteApi.get(gameId, me.id) map { text =>
|
||||
Ok(text)
|
||||
}
|
||||
}
|
||||
|
||||
private def sides(pov: Pov, isPlayer: Boolean)(implicit ctx: Context) =
|
||||
myTour(pov.game.tournamentId, isPlayer) zip
|
||||
(pov.game.simulId ?? Env.simul.repo.find) zip
|
||||
|
|
|
@ -31,6 +31,9 @@ trait I18nHelper {
|
|||
def i18nJsObject(keys: I18nKey*)(implicit lang: Lang): JsObject =
|
||||
i18nEnv.jsDump.keysToObject(keys, lang)
|
||||
|
||||
def i18nOptionJsObject(keys: Option[I18nKey]*)(implicit lang: Lang): JsObject =
|
||||
i18nEnv.jsDump.keysToObject(keys.flatten, lang)
|
||||
|
||||
def langName(lang: Lang): Option[String] = langName(lang.language)
|
||||
def langName(lang: String): Option[String] = LangList name lang
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ tablebaseEndpoint: "@tablebaseEndpoint"
|
|||
};
|
||||
}
|
||||
@myChat.map { c =>
|
||||
@chat.js(c, name = trans.spectatorRoom.str(), notes = true)
|
||||
@chat.js(c, name = trans.spectatorRoom.str(), withNote = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
@(c: lila.chat.UserChat.Mine, name: String, notes: Boolean = false, writeable: Boolean = true)(implicit ctx: Context)
|
||||
@(c: lila.chat.UserChat.Mine, name: String, withNote: Boolean = false, writeable: Boolean = true)(implicit ctx: Context)
|
||||
@embedJs {
|
||||
lichess.makeChat('chat', {
|
||||
lines: @Html(J.stringify(lila.chat.JsonView(c.chat))),
|
||||
i18n: @Html(J.stringify(i18nJsObject(
|
||||
trans.talkInChat,
|
||||
trans.toggleTheChat
|
||||
))),
|
||||
i18n: @jsI18n(withNote = withNote),
|
||||
id: "@c.chat.id",
|
||||
name: "@name",
|
||||
userId: @Html(ctx.userId.fold("null")(id => s""""$id"""")),
|
||||
writeable: @writeable,
|
||||
noteId: @if(withNote) {"@c.chat.id.take(8)"} else {null},
|
||||
kobold: @ctx.troll,
|
||||
mod: @isGranted(_.MarkTroll),
|
||||
timeout: @c.timeout,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@(withNote: Boolean = false)(implicit ctx: Context)
|
||||
@Html(J.stringify(i18nOptionJsObject(
|
||||
trans.talkInChat.some,
|
||||
trans.toggleTheChat.some,
|
||||
withNote option trans.notes,
|
||||
withNote option trans.typePrivateNotesHere
|
||||
)))
|
|
@ -13,7 +13,7 @@ i18n: @jsI18n()
|
|||
});
|
||||
}
|
||||
@myChat.map { c =>
|
||||
@chat.js(c, name = trans.spectatorRoom.str(), notes = true)
|
||||
@chat.js(c, name = trans.spectatorRoom.str(), withNote = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ GET /$gameId<\w{8}>/$color<white|black>/sides/watcher controllers.Round.sidesW
|
|||
GET /$gameId<\w{8}>/$color<white|black>/sides/player controllers.Round.sidesPlayer(gameId: String, color: String)
|
||||
GET /$gameId<\w{8}>/others controllers.Round.others(gameId: String)
|
||||
GET /$gameId<\w{8}>/continue/:mode controllers.Round.continue(gameId: String, mode: String)
|
||||
GET /$gameId<\w{8}>/note controllers.Round.readNote(gameId: String)
|
||||
POST /$gameId<\w{8}>/note controllers.Round.writeNote(gameId: String)
|
||||
GET /$gameId<\w{8}>/mini controllers.Round.mini(gameId: String, color: String = "white")
|
||||
GET /$gameId<\w{8}>/$color<white|black>/mini controllers.Round.mini(gameId: String, color: String)
|
||||
|
|
|
@ -14,7 +14,6 @@ final class Env(
|
|||
system: ActorSystem,
|
||||
scheduler: lila.common.Scheduler,
|
||||
roundJsonView: lila.round.JsonView,
|
||||
noteApi: lila.round.NoteApi,
|
||||
forecastApi: lila.round.ForecastApi,
|
||||
relationApi: lila.relation.RelationApi,
|
||||
bookmarkApi: lila.bookmark.BookmarkApi,
|
||||
|
@ -96,7 +95,6 @@ final class Env(
|
|||
val roundApi = new RoundApiBalancer(
|
||||
api = new RoundApi(
|
||||
jsonView = roundJsonView,
|
||||
noteApi = noteApi,
|
||||
forecastApi = forecastApi,
|
||||
bookmarkApi = bookmarkApi,
|
||||
getTourAndRanks = getTourAndRanks,
|
||||
|
@ -135,7 +133,6 @@ object Env {
|
|||
getSimulName = lila.simul.Env.current.cached.name,
|
||||
getTournamentName = lila.tournament.Env.current.cached.name,
|
||||
roundJsonView = lila.round.Env.current.jsonView,
|
||||
noteApi = lila.round.Env.current.noteApi,
|
||||
forecastApi = lila.round.Env.current.forecastApi,
|
||||
relationApi = lila.relation.Env.current.api,
|
||||
bookmarkApi = lila.bookmark.Env.current.api,
|
||||
|
|
|
@ -15,7 +15,6 @@ import lila.user.User
|
|||
|
||||
private[api] final class RoundApi(
|
||||
jsonView: JsonView,
|
||||
noteApi: lila.round.NoteApi,
|
||||
forecastApi: lila.round.ForecastApi,
|
||||
bookmarkApi: lila.bookmark.BookmarkApi,
|
||||
getTourAndRanks: Game => Fu[Option[TourAndRanks]],
|
||||
|
@ -29,14 +28,12 @@ private[api] final class RoundApi(
|
|||
initialFen = initialFen) zip
|
||||
getTourAndRanks(pov.game) zip
|
||||
(pov.game.simulId ?? getSimul) zip
|
||||
(ctx.me ?? (me => noteApi.get(pov.gameId, me.id))) zip
|
||||
forecastApi.loadForDisplay(pov) map {
|
||||
case ((((json, tourOption), simulOption), note), forecast) => (
|
||||
case (((json, tourOption), simulOption), forecast) => (
|
||||
blindMode _ compose
|
||||
withTournament(pov, tourOption)_ compose
|
||||
withSimul(pov, simulOption)_ compose
|
||||
withSteps(pov, initialFen)_ compose
|
||||
withNote(note)_ compose
|
||||
withBookmark(ctx.me ?? { bookmarkApi.bookmarked(pov.game, _) })_ compose
|
||||
withForecastCount(forecast.map(_.steps.size))_
|
||||
)(json)
|
||||
|
@ -52,13 +49,11 @@ private[api] final class RoundApi(
|
|||
withMoveTimes = false,
|
||||
withDivision = false) zip
|
||||
getTourAndRanks(pov.game) zip
|
||||
(pov.game.simulId ?? getSimul) zip
|
||||
(ctx.me ?? (me => noteApi.get(pov.gameId, me.id))) map {
|
||||
case (((json, tourOption), simulOption), note) => (
|
||||
(pov.game.simulId ?? getSimul) map {
|
||||
case ((json, tourOption), simulOption) => (
|
||||
blindMode _ compose
|
||||
withTournament(pov, tourOption)_ compose
|
||||
withSimul(pov, simulOption)_ compose
|
||||
withNote(note)_ compose
|
||||
withBookmark(ctx.me ?? { bookmarkApi.bookmarked(pov.game, _) })_ compose
|
||||
withSteps(pov, initialFen)_
|
||||
)(json)
|
||||
|
@ -78,13 +73,11 @@ private[api] final class RoundApi(
|
|||
withMoveTimes = withMoveTimes,
|
||||
withDivision = withDivision) zip
|
||||
getTourAndRanks(pov.game) zip
|
||||
(pov.game.simulId ?? getSimul) zip
|
||||
(ctx.me ?? (me => noteApi.get(pov.gameId, me.id))) map {
|
||||
case (((json, tourOption), simulOption), note) => (
|
||||
(pov.game.simulId ?? getSimul) map {
|
||||
case ((json, tourOption), simulOption) => (
|
||||
blindMode _ compose
|
||||
withTournament(pov, tourOption)_ compose
|
||||
withSimul(pov, simulOption)_ compose
|
||||
withNote(note)_ compose
|
||||
withBookmark(ctx.me ?? { bookmarkApi.bookmarked(pov.game, _) })_ compose
|
||||
withTree(pov, analysis, initialFen, withOpening = withOpening)_ compose
|
||||
withAnalysis(pov.game, analysis)_
|
||||
|
@ -120,9 +113,6 @@ private[api] final class RoundApi(
|
|||
variant = pov.game.variant,
|
||||
initialFen = initialFen | pov.game.variant.initialFen))
|
||||
|
||||
private def withNote(note: String)(json: JsObject) =
|
||||
if (note.isEmpty) json else json + ("note" -> JsString(note))
|
||||
|
||||
private def withBookmark(v: Boolean)(json: JsObject) =
|
||||
if (v) json + ("bookmarked" -> JsBoolean(true)) else json
|
||||
|
||||
|
|
|
@ -115,8 +115,10 @@ lichess.shepherd = function(f) {
|
|||
});
|
||||
};
|
||||
lichess.makeChat = function(id, data) {
|
||||
var isDev = $('body').data('dev');
|
||||
lichess.loadCss('/assets/stylesheets/chat.css');
|
||||
if (data.mod) lichess.loadCss('/assets/stylesheets/chat.mod.css');
|
||||
lichess.loadScript('/assets/compiled/lichess.chat.js').then(function() {
|
||||
lichess.loadScript("/assets/compiled/lichess.chat" + (isDev ? '' : '.min') + '.js').done(function() {
|
||||
LichessChat(document.getElementById(id), data);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
div.mchat {
|
||||
height: 350px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
div.mchat .chat_tabs {
|
||||
display: flex;
|
||||
}
|
||||
div.mchat .chat_tabs .tab {
|
||||
flex: 1 1 auto;
|
||||
border: 1px solid #c0c0c0;
|
||||
border-width: 0 1px 1px 0;
|
||||
padding: 3px 8px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
div.mchat .chat_tabs .tab:not(.active) {
|
||||
background: #e0e0e0;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
}
|
||||
div.mchat .chat_tabs .tab:not(.active):hover {
|
||||
opacity: 1;
|
||||
}
|
||||
div.mchat .chat_tabs .tab input {
|
||||
cursor: pointer;
|
||||
}
|
||||
div.mchat .chat_tabs .tab.discussion {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
div.mchat .chat_tabs .tab span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
div.mchat .chat_tabs .tab:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
div.mchat .chat_tabs .tab.active {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
div.mchat .content {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
div.mchat .note textarea {
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: #707070;
|
||||
line-height: 1.7em;
|
||||
outline: none;
|
||||
}
|
||||
div.mchat .lichess_say {
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
div.mchat .deleted {
|
||||
opacity: 0.5;
|
||||
}
|
||||
div.mchat .system {
|
||||
display: block;
|
||||
opacity: 0.8;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
|
@ -32,7 +32,6 @@ body.dark div.mchat.mod li i.mod {
|
|||
}
|
||||
|
||||
div.mchat.mod .moderation {
|
||||
justify-content: flex-start;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -733,28 +733,6 @@ div.chat_panels .preferences a.prefs {
|
|||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
div.mchat {
|
||||
height: 350px;
|
||||
}
|
||||
div.mchat .discussion,
|
||||
div.mchat .moderation {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
div.mchat .lichess_say {
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
div.mchat .deleted {
|
||||
opacity: 0.5;
|
||||
}
|
||||
div.mchat .system {
|
||||
display: block;
|
||||
opacity: 0.8;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.notification .message_infos,
|
||||
.notification .game_infos,
|
||||
div.side_box .game_infos {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
var m = require('mithril');
|
||||
var makeModeration = require('./moderation').ctrl;
|
||||
var makeNote = require('./note').ctrl;
|
||||
|
||||
module.exports = function(opts) {
|
||||
|
||||
var lines = opts.lines;
|
||||
var data = {
|
||||
id: opts.id,
|
||||
name: opts.name,
|
||||
lines: opts.lines
|
||||
};
|
||||
|
||||
var vm = {
|
||||
chatName: opts.name,
|
||||
enabled: m.prop(!lichess.storage.get('nochat')),
|
||||
writeable: m.prop(opts.writeable),
|
||||
isTroll: opts.kobold,
|
||||
|
@ -14,11 +18,14 @@ module.exports = function(opts) {
|
|||
isTimeout: m.prop(opts.timeout),
|
||||
placeholderKey: 'talkInChat',
|
||||
moderating: m.prop(null),
|
||||
loading: m.prop(false),
|
||||
tab: m.prop('discussion'),
|
||||
loading: m.prop(false)
|
||||
};
|
||||
|
||||
var trans = lichess.trans(opts.i18n);
|
||||
|
||||
var onTimeout = function(username) {
|
||||
lines.forEach(function(l) {
|
||||
data.lines.forEach(function(l) {
|
||||
if (l.u === username) l.d = true;
|
||||
});
|
||||
if (username.toLowerCase() === opts.userId) vm.isTimeout(true);
|
||||
|
@ -33,8 +40,8 @@ module.exports = function(opts) {
|
|||
};
|
||||
|
||||
var onMessage = function(line) {
|
||||
if (lines.length > 64) lines.shift();
|
||||
lines.push(line);
|
||||
if (data.lines.length > 64) data.lines.shift();
|
||||
data.lines.push(line);
|
||||
m.redraw();
|
||||
};
|
||||
|
||||
|
@ -43,6 +50,11 @@ module.exports = function(opts) {
|
|||
send: lichess.pubsub.emit('socket.send')
|
||||
}) : null;
|
||||
|
||||
var note = opts.noteId ? makeNote({
|
||||
id: opts.noteId,
|
||||
trans: trans
|
||||
}) : null;
|
||||
|
||||
var setWriteable = function(v) {
|
||||
vm.writeable(v);
|
||||
m.redraw();
|
||||
|
@ -54,8 +66,10 @@ module.exports = function(opts) {
|
|||
lichess.pubsub.on('chat.writeable', setWriteable);
|
||||
|
||||
return {
|
||||
lines: lines,
|
||||
data: data,
|
||||
vm: vm,
|
||||
moderation: moderation,
|
||||
note: note,
|
||||
post: function(text) {
|
||||
text = $.trim(text);
|
||||
if (!text) return false;
|
||||
|
@ -66,8 +80,7 @@ module.exports = function(opts) {
|
|||
lichess.pubsub.emit('socket.send')('talk', text);
|
||||
return false;
|
||||
},
|
||||
moderation: moderation,
|
||||
trans: lichess.trans(opts.i18n),
|
||||
trans: trans,
|
||||
setEnabled: function(v) {
|
||||
vm.enabled(v);
|
||||
if (!v) lichess.storage.set('nochat', 1);
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
var m = require('mithril');
|
||||
var moderationView = require('./moderation').view;
|
||||
|
||||
function renderLine(ctrl) {
|
||||
return function(line) {
|
||||
if (line.u === 'lichess') return m('li', m('em.system', line.t));
|
||||
return m('li', {
|
||||
'data-username': line.u
|
||||
}, [
|
||||
ctrl.vm.isMod ? moderationView.lineAction() : null,
|
||||
m.trust($.userLinkLimit(line.u, 14)),
|
||||
line.d ? m('em.deleted', '<deleted>') : line.t
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
function sameLines(l1, l2) {
|
||||
return l1.d && l2.d && l1.u === l2.u;
|
||||
}
|
||||
|
||||
function selectLines(lines) {
|
||||
var prev, ls = [];
|
||||
lines.forEach(function(l) {
|
||||
if (!prev || !sameLines(prev, l))
|
||||
if (!l.r || ctrl.vm.isTroll) ls.push(l);
|
||||
prev = l;
|
||||
});
|
||||
return ls;
|
||||
}
|
||||
|
||||
function input(ctrl) {
|
||||
var placeholder;
|
||||
if (ctrl.vm.isTimeout()) placeholder = 'You have been timed out.';
|
||||
else if (!ctrl.vm.writeable()) placeholder = 'Invited members only.';
|
||||
else placeholder = ctrl.trans(ctrl.vm.placeholderKey);
|
||||
return m('input', {
|
||||
class: 'lichess_say',
|
||||
placeholder: placeholder,
|
||||
autocomplete: 'off',
|
||||
maxlength: 140,
|
||||
disabled: ctrl.vm.isTimeout() || !ctrl.vm.writeable(),
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) el.addEventListener('keypress', function(e) {
|
||||
if (e.which == 10 || e.which == 13) {
|
||||
ctrl.post(e.target.value);
|
||||
e.target.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
view: function(ctrl) {
|
||||
if (!ctrl.vm.enabled()) return null;
|
||||
return [
|
||||
m('ol.messages.content.scroll-shadow-soft', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
if (!isUpdate && ctrl.moderation) $(el).on('click', 'i.mod', function(e) {
|
||||
ctrl.moderation.open($(e.target).parent().data('username'));
|
||||
});
|
||||
var autoScroll = (el.scrollTop === 0 || (el.scrollTop > (el.scrollHeight - el.clientHeight - 100)));
|
||||
el.scrollTop = 999999;
|
||||
if (autoScroll) setTimeout(function() {
|
||||
el.scrollTop = 999999;
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
selectLines(ctrl.data.lines).map(renderLine(ctrl))
|
||||
),
|
||||
input(ctrl)
|
||||
];
|
||||
}
|
||||
};
|
|
@ -50,7 +50,7 @@ module.exports = {
|
|||
if (ctrl.vm.loading()) return m.trust(lichess.spinnerHtml);
|
||||
var data = ctrl.vm.data();
|
||||
if (!data) return;
|
||||
return m('div.moderation', [
|
||||
return [
|
||||
m('div.top', [
|
||||
m('span.toggle_chat', {
|
||||
'data-icon': 'L',
|
||||
|
@ -60,66 +60,68 @@ module.exports = {
|
|||
'data-icon': '',
|
||||
}, m.trust($.userLink(data.username)))
|
||||
]),
|
||||
m('div.infos.block', [
|
||||
[
|
||||
data.games + ' games',
|
||||
data.troll ? 'TROLL' : null,
|
||||
data.engine ? 'ENGINE' : null,
|
||||
data.booster ? 'BOOSTER' : null
|
||||
].filter(function(x) {
|
||||
return x;
|
||||
}).join(' • '),
|
||||
' • ',
|
||||
m('a[href=/@/' + data.username + '?mod]', 'profile'),
|
||||
' • ',
|
||||
m('a[href=/mod/' + data.username + '/communication]', 'coms')
|
||||
]),
|
||||
m('div.timeout.block', [
|
||||
m('h2', 'Timeout 10 minutes for'),
|
||||
ctrl.reasons.map(function(r) {
|
||||
return m('a.text[data-icon=p]', {
|
||||
onclick: function() {
|
||||
ctrl.timeout(r.key)
|
||||
}
|
||||
}, r.name);
|
||||
}),
|
||||
data.troll ? null : m('div.shadowban', [
|
||||
'Or ',
|
||||
m('form', {
|
||||
action: '/mod/' + data.id + '/troll?set=1',
|
||||
method: 'post',
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) $(el).submit(function() {
|
||||
$.post($(this).attr('action'), function() {
|
||||
ctrl.open(data.username);
|
||||
m('div.content.moderation', [
|
||||
m('div.infos.block', [
|
||||
[
|
||||
data.games + ' games',
|
||||
data.troll ? 'TROLL' : null,
|
||||
data.engine ? 'ENGINE' : null,
|
||||
data.booster ? 'BOOSTER' : null
|
||||
].filter(function(x) {
|
||||
return x;
|
||||
}).join(' • '),
|
||||
' • ',
|
||||
m('a[href=/@/' + data.username + '?mod]', 'profile'),
|
||||
' • ',
|
||||
m('a[href=/mod/' + data.username + '/communication]', 'coms')
|
||||
]),
|
||||
m('div.timeout.block', [
|
||||
m('h2', 'Timeout 10 minutes for'),
|
||||
ctrl.reasons.map(function(r) {
|
||||
return m('a.text[data-icon=p]', {
|
||||
onclick: function() {
|
||||
ctrl.timeout(r.key)
|
||||
}
|
||||
}, r.name);
|
||||
}),
|
||||
data.troll ? null : m('div.shadowban', [
|
||||
'Or ',
|
||||
m('form', {
|
||||
action: '/mod/' + data.id + '/troll?set=1',
|
||||
method: 'post',
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) $(el).submit(function() {
|
||||
$.post($(this).attr('action'), function() {
|
||||
ctrl.open(data.username);
|
||||
});
|
||||
ctrl.vm.loading(true);
|
||||
m.redraw();
|
||||
return false;
|
||||
});
|
||||
ctrl.vm.loading(true);
|
||||
m.redraw();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}, m('button.button[type=submit]', 'shadowban'))
|
||||
])
|
||||
]),
|
||||
m('div.history.block', [
|
||||
m('h2', 'Timeout history'),
|
||||
m('table', m('tbody.slist', {
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) $('body').trigger('lichess.content_loaded');
|
||||
}
|
||||
}, m('button.button[type=submit]', 'Permanently shadowban'))
|
||||
}, data.history.map(function(e) {
|
||||
return m('tr', [
|
||||
m('td.reason', e.reason),
|
||||
m('td.mod', e.mod),
|
||||
m('td', m('time', {
|
||||
class: "moment",
|
||||
'data-format': isToday(e.date) ? 'LT' : 'DD/MM/YY',
|
||||
datetime: new Date(e.date).toISOString()
|
||||
}))
|
||||
]);
|
||||
})))
|
||||
])
|
||||
]),
|
||||
m('div.history.block', [
|
||||
m('h2', 'Timeout history'),
|
||||
m('table', m('tbody.slist', {
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) $('body').trigger('lichess.content_loaded');
|
||||
}
|
||||
}, data.history.map(function(e) {
|
||||
return m('tr', [
|
||||
m('td.reason', e.reason),
|
||||
m('td.mod', e.mod),
|
||||
m('td', m('time', {
|
||||
class: "moment",
|
||||
'data-format': isToday(e.date) ? 'LT' : 'DD/MM/YY',
|
||||
datetime: new Date(e.date).toISOString()
|
||||
}))
|
||||
]);
|
||||
})))
|
||||
])
|
||||
]);
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
var m = require('mithril');
|
||||
var xhr = require('./xhr');
|
||||
|
||||
module.exports = {
|
||||
ctrl: function(opts) {
|
||||
var id = opts.id;
|
||||
var vm = {
|
||||
text: m.prop(null)
|
||||
};
|
||||
var doPost = $.fp.debounce(function() {
|
||||
xhr.setNote(id, vm.text());
|
||||
}, 1000);
|
||||
return {
|
||||
id: id,
|
||||
vm: vm,
|
||||
trans: opts.trans,
|
||||
fetch: function() {
|
||||
xhr.getNote(id).then(function(t) {
|
||||
vm.text(t || '');
|
||||
m.redraw();
|
||||
});
|
||||
},
|
||||
post: function(text) {
|
||||
vm.text(text);
|
||||
doPost();
|
||||
}
|
||||
}
|
||||
},
|
||||
view: function(ctrl) {
|
||||
var text = ctrl.vm.text();
|
||||
if (text === null) return m('div.loading', {
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) ctrl.fetch();
|
||||
}
|
||||
}, m.trust(lichess.spinnerHtml));
|
||||
return m('textarea', {
|
||||
placeholder: ctrl.trans('typePrivateNotesHere'),
|
||||
config: function(el, isUpdate) {
|
||||
if (isUpdate) return;
|
||||
$(el).val(text).on('change keyup paste', function() {
|
||||
ctrl.post($(el).val());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,92 +1,52 @@
|
|||
var m = require('mithril');
|
||||
var moderationView = require('./moderation').view;
|
||||
var discussionView = require('./discussion').view;
|
||||
var noteView = require('./note').view;
|
||||
|
||||
var deletedDom = m('em.deleted', '<deleted>');
|
||||
|
||||
function renderLine(ctrl) {
|
||||
return function(line) {
|
||||
if (line.u === 'lichess') return m('li', m('em.system', line.t));
|
||||
return m('li', {
|
||||
'data-username': line.u
|
||||
}, [
|
||||
ctrl.vm.isMod ? moderationView.lineAction() : null,
|
||||
m.trust($.userLinkLimit(line.u, 14)),
|
||||
line.d ? deletedDom : line.t
|
||||
]);
|
||||
};
|
||||
function tabName(ctrl, t) {
|
||||
switch (t) {
|
||||
case 'discussion':
|
||||
return [
|
||||
m('span', ctrl.data.name),
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
class: 'toggle_chat',
|
||||
title: ctrl.trans('toggleTheChat'),
|
||||
onchange: m.withAttr('checked', ctrl.setEnabled),
|
||||
checked: ctrl.vm.enabled()
|
||||
})
|
||||
];
|
||||
case 'note':
|
||||
return ctrl.trans('notes');
|
||||
}
|
||||
}
|
||||
|
||||
function sameLines(l1, l2) {
|
||||
return l1.d && l2.d && l1.u === l2.u;
|
||||
function tabContent(ctrl, t) {
|
||||
if (t === 'note' && ctrl.note)
|
||||
return noteView(ctrl.note);
|
||||
return discussionView(ctrl);
|
||||
}
|
||||
|
||||
function selectLines(lines) {
|
||||
var prev, ls = [];
|
||||
lines.forEach(function(l) {
|
||||
if (!prev || !sameLines(prev, l))
|
||||
if (!l.r || ctrl.vm.isTroll) ls.push(l);
|
||||
prev = l;
|
||||
});
|
||||
return ls;
|
||||
}
|
||||
|
||||
function input(ctrl) {
|
||||
var placeholder;
|
||||
if (ctrl.vm.isTimeout()) placeholder = 'You have been timed out.';
|
||||
else if (!ctrl.vm.writeable()) placeholder = 'Invited members only.';
|
||||
else placeholder = ctrl.trans(ctrl.vm.placeholderKey);
|
||||
return m('input', {
|
||||
class: 'lichess_say',
|
||||
placeholder: placeholder,
|
||||
autocomplete: 'off',
|
||||
maxlength: 140,
|
||||
disabled: ctrl.vm.isTimeout() || !ctrl.vm.writeable(),
|
||||
config: function(el, isUpdate) {
|
||||
if (!isUpdate) el.addEventListener('keypress', function(e) {
|
||||
if (e.which == 10 || e.which == 13) {
|
||||
ctrl.post(e.target.value);
|
||||
e.target.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function discussion(ctrl) {
|
||||
return m('div.discussion', [
|
||||
m('div.top', [
|
||||
m('span', ctrl.vm.chatName),
|
||||
m('input', {
|
||||
type: 'checkbox',
|
||||
class: 'toggle_chat',
|
||||
title: ctrl.trans('toggleTheChat'),
|
||||
onchange: m.withAttr('checked', ctrl.setEnabled),
|
||||
checked: ctrl.vm.enabled()
|
||||
})
|
||||
]),
|
||||
ctrl.vm.enabled() ? [
|
||||
m('ol.messages.content.scroll-shadow-soft', {
|
||||
config: function(el, isUpdate, ctx) {
|
||||
if (!isUpdate && ctrl.moderation) $(el).on('click', 'i.mod', function(e) {
|
||||
ctrl.moderation.open($(e.target).parent().data('username'));
|
||||
});
|
||||
var autoScroll = (el.scrollTop === 0 || (el.scrollTop > (el.scrollHeight - el.clientHeight - 100)));
|
||||
el.scrollTop = 999999;
|
||||
if (autoScroll) setTimeout(function() {
|
||||
el.scrollTop = 999999;
|
||||
}, 500);
|
||||
}
|
||||
function normalView(ctrl) {
|
||||
var tab = ctrl.vm.tab();
|
||||
var tabs = ['discussion'];
|
||||
if (ctrl.note) tabs.push('note');
|
||||
return [
|
||||
m('div.chat_tabs', tabs.map(function(t) {
|
||||
return m('div', {
|
||||
onclick: function() {
|
||||
ctrl.vm.tab(t);
|
||||
},
|
||||
selectLines(ctrl.lines).map(renderLine(ctrl))
|
||||
),
|
||||
input(ctrl)
|
||||
] : null
|
||||
])
|
||||
class: 'tab ' + t + (tab === t ? ' active' : '')
|
||||
}, tabName(ctrl, t));
|
||||
})),
|
||||
m('div.content.' + tab, tabContent(ctrl, tab))
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
return m('div', {
|
||||
class: 'mchat' + (ctrl.vm.isMod ? ' mod' : '')
|
||||
},
|
||||
moderationView.ui(ctrl.moderation) || discussion(ctrl));
|
||||
moderationView.ui(ctrl.moderation) || normalView(ctrl));
|
||||
};
|
||||
|
|
|
@ -18,6 +18,28 @@ function userModInfo(username) {
|
|||
});
|
||||
}
|
||||
|
||||
function noteUrl(id) {
|
||||
return uncache('/' + id + '/note');
|
||||
}
|
||||
|
||||
function getNote(id) {
|
||||
return $.get(noteUrl(id));
|
||||
}
|
||||
|
||||
function setNote(id, text) {
|
||||
return m.request({
|
||||
background: true,
|
||||
method: 'POST',
|
||||
url: noteUrl(id),
|
||||
data: {
|
||||
text: text
|
||||
},
|
||||
config: xhrConfig
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
userModInfo: userModInfo
|
||||
userModInfo: userModInfo,
|
||||
getNote: getNote,
|
||||
setNote: setNote
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue