local chat moderation
parent
55ae490078
commit
5781e5614b
|
@ -15,10 +15,11 @@ object ChatJsData {
|
|||
timeout: Boolean,
|
||||
public: Boolean, // game players chat is not public
|
||||
withNote: Boolean = false,
|
||||
writeable: Boolean = true
|
||||
writeable: Boolean = true,
|
||||
localMod: Boolean = false
|
||||
)(implicit ctx: Context) =
|
||||
json(
|
||||
chat.chat, name = name, timeout = timeout, withNote = withNote, writeable = writeable, public = public, restricted = chat.restricted
|
||||
chat.chat, name = name, timeout = timeout, withNote = withNote, writeable = writeable, public = public, restricted = chat.restricted, localMod = localMod
|
||||
)
|
||||
|
||||
def json(
|
||||
|
@ -28,7 +29,8 @@ object ChatJsData {
|
|||
public: Boolean, // game players chat is not public
|
||||
withNote: Boolean = false,
|
||||
writeable: Boolean = true,
|
||||
restricted: Boolean = false
|
||||
restricted: Boolean = false,
|
||||
localMod: Boolean = false
|
||||
)(implicit ctx: Context) = Json.obj(
|
||||
"data" -> Json.obj(
|
||||
"id" -> chat.id,
|
||||
|
@ -44,6 +46,7 @@ object ChatJsData {
|
|||
"public" -> public,
|
||||
"kobold" -> ctx.troll,
|
||||
"permissions" -> Json.obj(
|
||||
"local" -> localMod,
|
||||
"timeout" -> isGranted(_.ChatTimeout).option(true),
|
||||
"shadowban" -> isGranted(_.MarkTroll).option(true)
|
||||
).noNull,
|
||||
|
|
|
@ -11,7 +11,13 @@ i18n: @board.userAnalysisI18n(),
|
|||
tagTypes: '@lila.study.PgnTags.typesToString',
|
||||
userId: @jsUserId,
|
||||
chat: @jsOrNull(chatOption map { c =>
|
||||
chat.ChatJsData.json(c.chat, name = trans.chatRoom.txt(), timeout = c.timeout, writeable = ctx.userId.??(s.canChat), public = false)
|
||||
chat.ChatJsData.json(
|
||||
c.chat,
|
||||
name = trans.chatRoom.txt(),
|
||||
timeout = c.timeout,
|
||||
writeable = ctx.userId.??(s.canChat),
|
||||
public = false,
|
||||
localMod = ctx.userId.??(s.canContribute))
|
||||
}),
|
||||
explorer: {
|
||||
endpoint: "@explorerEndpoint",
|
||||
|
|
|
@ -63,9 +63,9 @@ final class ChatApi(
|
|||
lilaBus.publish(actorApi.ChatLine(chatId, line), channelOf(chatId)) inject line.some
|
||||
}
|
||||
|
||||
def timeout(chatId: ChatId, modId: String, userId: String, reason: ChatTimeout.Reason): Funit =
|
||||
def timeout(chatId: ChatId, modId: String, userId: String, reason: ChatTimeout.Reason, local: Boolean): Funit =
|
||||
coll.byId[UserChat](chatId) zip UserRepo.byId(modId) zip UserRepo.byId(userId) flatMap {
|
||||
case ((Some(chat), Some(mod)), Some(user)) if isMod(mod) => doTimeout(chat, mod, user, reason)
|
||||
case Some(chat) ~ Some(mod) ~ Some(user) if isMod(mod) || local => doTimeout(chat, mod, user, reason)
|
||||
case _ => fuccess(none)
|
||||
}
|
||||
|
||||
|
@ -87,18 +87,18 @@ final class ChatApi(
|
|||
chatTimeout.add(c, mod, user, reason) >>- {
|
||||
lilaBus.publish(actorApi.OnTimeout(user.username), channelOf(chat.id))
|
||||
lilaBus.publish(actorApi.ChatLine(chat.id, line), channelOf(chat.id))
|
||||
modLog ! lila.hub.actorApi.mod.ChatTimeout(
|
||||
if (isMod(mod)) modLog ! lila.hub.actorApi.mod.ChatTimeout(
|
||||
mod = mod.id, user = user.id, reason = reason.key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def isMod(user: User) = lila.security.Granter(_.ChatTimeout)(user)
|
||||
|
||||
def reinstate(list: List[ChatTimeout.Reinstate]) = list.foreach { r =>
|
||||
lilaBus.publish(actorApi.OnReinstate(r.user), Symbol(s"chat-${r.chat}"))
|
||||
}
|
||||
|
||||
private def isMod(user: User) = lila.security.Granter(_.ChatTimeout)(user)
|
||||
|
||||
private[ChatApi] def makeLine(chatId: String, userId: String, t1: String): Fu[Option[UserLine]] =
|
||||
UserRepo.byId(userId) zip chatTimeout.isActive(chatId, userId) map {
|
||||
case (Some(user), false) if !user.disabled => Writer cut t1 flatMap { t2 =>
|
||||
|
|
|
@ -15,7 +15,7 @@ private[chat] final class FrontActor(api: ChatApi) extends Actor {
|
|||
|
||||
case SystemTalk(chatId, text) => api.userChat.system(chatId, text)
|
||||
|
||||
case Timeout(chatId, modId, userId, reason) => api.userChat.timeout(chatId, modId, userId, reason)
|
||||
case Timeout(chatId, modId, userId, reason, local) => api.userChat.timeout(chatId, modId, userId, reason, local)
|
||||
|
||||
case Remove(chatId) => api remove chatId
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ object Socket {
|
|||
chatId: String,
|
||||
member: SocketMember,
|
||||
socket: ActorRef,
|
||||
chat: ActorSelection
|
||||
chat: ActorSelection,
|
||||
canTimeout: Option[() => Fu[Boolean]] = None
|
||||
): Handler.Controller = {
|
||||
|
||||
case ("talk", o) => for {
|
||||
|
@ -25,7 +26,9 @@ object Socket {
|
|||
modId <- member.userId
|
||||
userId <- data.str("userId")
|
||||
reason <- data.str("reason") flatMap ChatTimeout.Reason.apply
|
||||
} chat ! actorApi.Timeout(chatId, modId, userId, reason)
|
||||
} canTimeout.??(ct => ct()) foreach { localTimeout =>
|
||||
chat ! actorApi.Timeout(chatId, modId, userId, reason, local = localTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
type Send = (String, JsValue, Boolean) => Unit
|
||||
|
|
|
@ -5,7 +5,7 @@ case class UserTalk(chatId: String, userId: String, text: String, public: Boolea
|
|||
case class PlayerTalk(chatId: String, white: Boolean, text: String)
|
||||
case class SystemTalk(chatId: String, text: String)
|
||||
case class ChatLine(chatId: String, line: Line)
|
||||
case class Timeout(chatId: String, modId: String, userId: String, reason: ChatTimeout.Reason)
|
||||
case class Timeout(chatId: String, mod: String, userId: String, reason: ChatTimeout.Reason, local: Boolean)
|
||||
|
||||
case class OnTimeout(username: String)
|
||||
case class OnReinstate(userId: String)
|
||||
|
|
|
@ -225,7 +225,8 @@ private[study] final class SocketHandler(
|
|||
chatId = studyId.value,
|
||||
member = member,
|
||||
socket = socket,
|
||||
chat = chat
|
||||
chat = chat,
|
||||
canTimeout = Some(() => user.?? { u => api.isContributor(studyId, u.id) })
|
||||
)
|
||||
|
||||
private def reading[A](o: JsValue)(f: A => Unit)(implicit reader: Reads[A]): Unit =
|
||||
|
|
|
@ -249,6 +249,8 @@ final class StudyApi(
|
|||
} >>- onMembersChange(study)
|
||||
}
|
||||
|
||||
def isContributor = studyRepo.isContributor _
|
||||
|
||||
private def onMembersChange(study: Study) = {
|
||||
lightStudyCache.refresh(study.id)
|
||||
sendTo(study, Socket.ReloadAll)
|
||||
|
|
|
@ -126,6 +126,9 @@ final class StudyRepo(private[study] val coll: Coll) {
|
|||
.sort($sort desc "updatedAt")
|
||||
.list[Study.IdName](nb, ReadPreference.secondaryPreferred)
|
||||
|
||||
def isContributor(studyId: Study.Id, userId: User.ID) =
|
||||
coll.exists($id(studyId) ++ $doc(s"members.$userId.role" -> "w"))
|
||||
|
||||
def like(studyId: Study.Id, userId: User.ID, v: Boolean): Fu[Study.Likes] =
|
||||
countLikes(studyId).flatMap {
|
||||
case None => fuccess(Study.Likes(0))
|
||||
|
|
|
@ -91,6 +91,7 @@ module.exports = function(data, ctrl, tagTypes, practiceData) {
|
|||
var configureAnalysis = function() {
|
||||
if (ctrl.embed) return;
|
||||
lichess.pubsub.emit('chat.writeable')(data.features.chat);
|
||||
lichess.pubsub.emit('chat.permissions')({local: members.canContribute()});
|
||||
var computer = data.chapter.features.computer || data.chapter.practice;
|
||||
if (!computer) ctrl.getCeval().enabled(false);
|
||||
ctrl.getCeval().allowed(computer);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Ctrl, ChatOpts, Line, Tab, ViewModel, Redraw } from './interfaces'
|
||||
import { Ctrl, ChatOpts, Line, Tab, ViewModel, Redraw, Permissions, ModerationCtrl } from './interfaces'
|
||||
import { presetCtrl } from './preset'
|
||||
import { noteCtrl } from './note'
|
||||
import { moderationCtrl } from './moderation'
|
||||
|
@ -9,6 +9,8 @@ export default function(opts: ChatOpts, redraw: Redraw): Ctrl {
|
|||
|
||||
const pubsub = window.lichess.pubsub;
|
||||
|
||||
let moderation: ModerationCtrl | undefined;
|
||||
|
||||
const vm: ViewModel = {
|
||||
tab: 'discussion',
|
||||
enabled: !window.lichess.storage.get('nochat'),
|
||||
|
@ -52,12 +54,20 @@ export default function(opts: ChatOpts, redraw: Redraw): Ctrl {
|
|||
|
||||
const trans = window.lichess.trans(opts.i18n);
|
||||
|
||||
const moderation = opts.permissions.timeout && opts.timeoutReasons ? moderationCtrl({
|
||||
reasons: opts.timeoutReasons,
|
||||
permissions: opts.permissions,
|
||||
send: window.lichess.pubsub.emit('socket.send'),
|
||||
redraw: redraw
|
||||
}) : undefined;
|
||||
function canMod() {
|
||||
return opts.permissions.timeout || opts.permissions.local;
|
||||
}
|
||||
|
||||
function instanciateModeration() {
|
||||
moderation = canMod() ? moderationCtrl({
|
||||
reasons: opts.timeoutReasons || ([{key: 'other', name: 'Inappropriate behavior'}]),
|
||||
permissions: opts.permissions,
|
||||
send: window.lichess.pubsub.emit('socket.send'),
|
||||
redraw: redraw
|
||||
}) : undefined;
|
||||
if (canMod()) opts.loadCss('/assets/stylesheets/chat.mod.css');
|
||||
}
|
||||
instanciateModeration();
|
||||
|
||||
const note = data.userId && opts.noteId ? noteCtrl({
|
||||
id: opts.noteId,
|
||||
|
@ -78,6 +88,12 @@ export default function(opts: ChatOpts, redraw: Redraw): Ctrl {
|
|||
vm.writeable = v;
|
||||
redraw();
|
||||
});
|
||||
pubsub.on('chat.permissions', function(obj: Permissions) {
|
||||
let p: keyof Permissions;
|
||||
for (p in obj) opts.permissions[p] = obj[p];
|
||||
instanciateModeration();
|
||||
redraw();
|
||||
});
|
||||
|
||||
const emitEnabled = () => pubsub.emit('chat.enabled')(vm.enabled);
|
||||
emitEnabled();
|
||||
|
@ -88,9 +104,9 @@ export default function(opts: ChatOpts, redraw: Redraw): Ctrl {
|
|||
vm: vm,
|
||||
setTab(t: Tab) {
|
||||
vm.tab = t
|
||||
redraw()
|
||||
redraw()
|
||||
},
|
||||
moderation: moderation,
|
||||
moderation: () => moderation,
|
||||
note: note,
|
||||
preset: preset,
|
||||
post: post,
|
||||
|
|
|
@ -25,10 +25,15 @@ export default function(ctrl: Ctrl): Array<VNode | undefined> {
|
|||
h('ol.messages.content.scroll-shadow-soft', {
|
||||
hook: {
|
||||
insert(vnode) {
|
||||
$(vnode.elm as HTMLElement).on('click', 'a.jump', (e: Event) => {
|
||||
window.lichess.pubsub.emit('jump')((e.target as HTMLElement).getAttribute('data-ply'));
|
||||
});
|
||||
scrollCb(vnode);
|
||||
$(vnode.elm as HTMLElement)
|
||||
.on('click', 'a.jump', (e: Event) => {
|
||||
window.lichess.pubsub.emit('jump')((e.target as HTMLElement).getAttribute('data-ply'));
|
||||
})
|
||||
.on('click', '.mod', (e: Event) => {
|
||||
const m = ctrl.moderation();
|
||||
if (m) m.open(((e.target as HTMLElement).getAttribute('data-username') as string).split(' ')[0]);
|
||||
});
|
||||
scrollCb(vnode);
|
||||
},
|
||||
postpatch: (_, vnode) => scrollCb(vnode)
|
||||
}
|
||||
|
@ -124,15 +129,11 @@ function renderLine(ctrl: Ctrl, line: Line) {
|
|||
h('span', '[' + line.c + ']'),
|
||||
textNode
|
||||
]);
|
||||
var userNode = thunk('a', line.u, userLink, [line.u]);
|
||||
|
||||
const userNode = thunk('a', line.u, userLink, [line.u]);
|
||||
|
||||
return h('li', {
|
||||
hook: ctrl.moderation ? bind('click', (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (ctrl.moderation && target.classList.contains('mod'))
|
||||
ctrl.moderation.open((target.getAttribute('data-username') as string).split(' ')[0]);
|
||||
}) : {}
|
||||
}, ctrl.moderation ? [
|
||||
}, ctrl.moderation() ? [
|
||||
line.u ? lineAction(line.u) : null,
|
||||
userNode,
|
||||
textNode
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface ChatOpts {
|
|||
i18n: Object
|
||||
preset?: string
|
||||
noteId?: string
|
||||
loadCss: (url: string) => void
|
||||
}
|
||||
|
||||
interface ChatData {
|
||||
|
@ -29,7 +30,8 @@ export interface Line {
|
|||
r?: boolean // troll
|
||||
}
|
||||
|
||||
interface Permissions {
|
||||
export interface Permissions {
|
||||
local?: boolean
|
||||
timeout?: boolean
|
||||
shadowban?: boolean
|
||||
}
|
||||
|
@ -44,7 +46,7 @@ export interface Ctrl {
|
|||
vm: ViewModel
|
||||
preset: PresetCtrl
|
||||
note?: NoteCtrl
|
||||
moderation?: ModerationCtrl
|
||||
moderation(): ModerationCtrl | undefined
|
||||
post(text: string): boolean
|
||||
trans: Trans
|
||||
setTab(tab: Tab): void
|
||||
|
@ -112,7 +114,7 @@ export interface ModerationCtrl {
|
|||
loading(): boolean
|
||||
data(): ModerationData | undefined
|
||||
reasons: ModerationReason[]
|
||||
permissions: Permissions
|
||||
permissions(): Permissions
|
||||
open(username: string): void
|
||||
close(): void
|
||||
timeout(reason: ModerationReason): void
|
||||
|
@ -122,11 +124,11 @@ export interface ModerationCtrl {
|
|||
export interface ModerationData {
|
||||
id: string
|
||||
username: string
|
||||
games: number
|
||||
troll: boolean
|
||||
engine: boolean
|
||||
booster: boolean
|
||||
history: ModerationHistoryEntry[]
|
||||
games?: number
|
||||
troll?: boolean
|
||||
engine?: boolean
|
||||
booster?: boolean
|
||||
history?: ModerationHistoryEntry[]
|
||||
}
|
||||
|
||||
export interface ModerationReason {
|
||||
|
|
|
@ -14,6 +14,8 @@ const patch = init([klass, attributes]);
|
|||
|
||||
export default function LichessChat(element: Element, opts: ChatOpts) {
|
||||
|
||||
opts.loadCss('/assets/stylesheets/chat.css');
|
||||
|
||||
const container = element.parentNode as HTMLElement;
|
||||
|
||||
let vnode: VNode, ctrl: Ctrl
|
||||
|
|
|
@ -14,12 +14,19 @@ export function moderationCtrl(opts: ModerationOpts): ModerationCtrl {
|
|||
let loading = false;
|
||||
|
||||
const open = (username: string) => {
|
||||
loading = true;
|
||||
userModInfo(username).then(d => {
|
||||
data = d;
|
||||
loading = false;
|
||||
opts.redraw();
|
||||
});
|
||||
if (opts.permissions.timeout) {
|
||||
loading = true;
|
||||
userModInfo(username).then(d => {
|
||||
data = d;
|
||||
loading = false;
|
||||
opts.redraw();
|
||||
});
|
||||
} else {
|
||||
data = {
|
||||
id: username,
|
||||
username: username
|
||||
};
|
||||
}
|
||||
opts.redraw();
|
||||
};
|
||||
|
||||
|
@ -33,7 +40,7 @@ export function moderationCtrl(opts: ModerationOpts): ModerationCtrl {
|
|||
loading: () => loading,
|
||||
data: () => data,
|
||||
reasons: opts.reasons,
|
||||
permissions: opts.permissions,
|
||||
permissions: () => opts.permissions,
|
||||
open: open,
|
||||
close: close,
|
||||
timeout(reason: ModerationReason) {
|
||||
|
@ -67,73 +74,85 @@ export function moderationView(ctrl?: ModerationCtrl): VNode[] | undefined {
|
|||
if (ctrl.loading()) return [h('div.loading', spinner())];
|
||||
const data = ctrl.data();
|
||||
if (!data) return;
|
||||
return [
|
||||
h('div.top', {
|
||||
key: 'mod-' + data.id,
|
||||
}, [
|
||||
h('span.text', {
|
||||
attrs: {'data-icon': '' },
|
||||
}, [userLink(data.username)]),
|
||||
h('span.toggle_chat', {
|
||||
attrs: {'data-icon': 'L'},
|
||||
hook: bind('click', ctrl.close)
|
||||
})
|
||||
]),
|
||||
h('div.content.moderation', [
|
||||
h('div.infos.block', [
|
||||
window.lichess.numberFormat(data.games) + ' games',
|
||||
data.troll ? 'TROLL' : undefined,
|
||||
data.engine ? 'ENGINE' : undefined,
|
||||
data.booster ? 'BOOSTER' : undefined
|
||||
].map(t => t && h('span', t)).concat([
|
||||
h('a', {
|
||||
attrs: {
|
||||
href: '/@/' + data.username + '?mod'
|
||||
}
|
||||
}, 'profile')
|
||||
]).concat(
|
||||
ctrl.permissions.shadowban ? [
|
||||
h('a', {
|
||||
const perms = ctrl.permissions();
|
||||
|
||||
const infos = data.history ? h('div.infos.block', [
|
||||
window.lichess.numberFormat(data.games || 0) + ' games',
|
||||
data.troll ? 'TROLL' : undefined,
|
||||
data.engine ? 'ENGINE' : undefined,
|
||||
data.booster ? 'BOOSTER' : undefined
|
||||
].map(t => t && h('span', t)).concat([
|
||||
h('a', {
|
||||
attrs: {
|
||||
href: '/@/' + data.username + '?mod'
|
||||
}
|
||||
}, 'profile')
|
||||
]).concat(
|
||||
perms.shadowban ? [
|
||||
h('a', {
|
||||
attrs: {
|
||||
href: '/mod/' + data.username + '/communication'
|
||||
}
|
||||
}, 'coms')
|
||||
] : [])) : undefined;
|
||||
|
||||
const timeout = perms.timeout ? h('div.timeout.block', [
|
||||
h('h2', 'Timeout 10 minutes for'),
|
||||
...ctrl.reasons.map(r => {
|
||||
return h('a.text', {
|
||||
attrs: { 'data-icon': 'p' },
|
||||
hook: bind('click', () => ctrl.timeout(r))
|
||||
}, r.name);
|
||||
}),
|
||||
...(
|
||||
(data.troll || !perms.shadowban) ? [] : [h('div.shadowban', [
|
||||
'Or ',
|
||||
h('button.button', {
|
||||
hook: bind('click', ctrl.shadowban)
|
||||
}, 'shadowban')
|
||||
])])
|
||||
]) : h('div.timeout.block', [
|
||||
h('h2', 'Moderation'),
|
||||
h('a.text', {
|
||||
attrs: { 'data-icon': 'p' },
|
||||
hook: bind('click', () => ctrl.timeout(ctrl.reasons[0]))
|
||||
}, 'Timeout 10 minutes')
|
||||
]);
|
||||
|
||||
const history = data.history ? h('div.history.block', [
|
||||
h('h2', 'Timeout history'),
|
||||
h('table', h('tbody.slist', {
|
||||
hook: {
|
||||
insert: () => window.lichess.pubsub.emit('content_loaded')()
|
||||
}
|
||||
}, data.history.map(function(e) {
|
||||
return h('tr', [
|
||||
h('td.reason', e.reason),
|
||||
h('td.mod', e.mod),
|
||||
h('td', h('time.moment', {
|
||||
attrs: {
|
||||
href: '/mod/' + data.username + '/communication'
|
||||
'data-format': isToday(e.date) ? 'LT' : 'DD/MM/YY',
|
||||
datetime: new Date(e.date).toISOString()
|
||||
}
|
||||
}, 'coms')
|
||||
] : [])),
|
||||
h('div.timeout.block', [
|
||||
h('h2', 'Timeout 10 minutes for'),
|
||||
...ctrl.reasons.map(r => {
|
||||
return h('a.text', {
|
||||
attrs: { 'data-icon': 'p' },
|
||||
hook: bind('click', () => ctrl.timeout(r))
|
||||
}, r.name);
|
||||
}),
|
||||
...(
|
||||
(data.troll || !ctrl.permissions.shadowban) ? [] : [h('div.shadowban', [
|
||||
'Or ',
|
||||
h('button.button', {
|
||||
hook: bind('click', ctrl.shadowban)
|
||||
}, 'shadowban')
|
||||
])])
|
||||
}))
|
||||
]);
|
||||
})))
|
||||
]) : undefined;
|
||||
|
||||
return [
|
||||
h('div.top', { key: 'mod-' + data.id }, [
|
||||
h('span.text', {
|
||||
attrs: {'data-icon': '' },
|
||||
}, [userLink(data.username)]),
|
||||
h('span.toggle_chat', {
|
||||
attrs: {'data-icon': 'L'},
|
||||
hook: bind('click', ctrl.close)
|
||||
})
|
||||
]),
|
||||
h('div.history.block', [
|
||||
h('h2', 'Timeout history'),
|
||||
h('table', h('tbody.slist', {
|
||||
hook: {
|
||||
insert: () => window.lichess.pubsub.emit('content_loaded')()
|
||||
}
|
||||
}, data.history.map(function(e) {
|
||||
return h('tr', [
|
||||
h('td.reason', e.reason),
|
||||
h('td.mod', e.mod),
|
||||
h('td', h('time.moment', {
|
||||
attrs: {
|
||||
'data-format': isToday(e.date) ? 'LT' : 'DD/MM/YY',
|
||||
datetime: new Date(e.date).toISOString()
|
||||
}
|
||||
}))
|
||||
]);
|
||||
})))
|
||||
h('div.content.moderation', [
|
||||
infos,
|
||||
timeout,
|
||||
history
|
||||
])
|
||||
])
|
||||
];
|
||||
];
|
||||
};
|
||||
|
|
|
@ -7,11 +7,13 @@ import { bind } from './util'
|
|||
|
||||
export default function(ctrl: Ctrl) {
|
||||
|
||||
const mod = ctrl.moderation();
|
||||
|
||||
return h('div#chat.side_box.mchat', {
|
||||
class: {
|
||||
mod: !!ctrl.opts.permissions.timeout
|
||||
mod: !!mod
|
||||
}
|
||||
}, moderationView(ctrl.moderation) || normalView(ctrl))
|
||||
}, moderationView(mod) || normalView(ctrl))
|
||||
}
|
||||
|
||||
function normalView(ctrl: Ctrl) {
|
||||
|
|
|
@ -196,9 +196,12 @@ lichess.assetUrl = function(url, opts) {
|
|||
var version = document.body.getAttribute('data-asset-version');
|
||||
return baseUrl + url + (opts.noVersion ? '' : '?v=' + version);
|
||||
};
|
||||
lichess.loadedCss = {};
|
||||
lichess.loadCss = function(url) {
|
||||
if (lichess.loadedCss[url]) return;
|
||||
lichess.loadedCss[url] = true;
|
||||
$('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', lichess.assetUrl(url)));
|
||||
}
|
||||
};
|
||||
lichess.loadScript = function(url, opts) {
|
||||
return $.ajax({
|
||||
dataType: "script",
|
||||
|
@ -226,8 +229,7 @@ lichess.shepherd = function(f) {
|
|||
};
|
||||
lichess.makeChat = function(id, data, callback) {
|
||||
var isDev = document.body.getAttribute('data-dev');
|
||||
lichess.loadCss('/assets/stylesheets/chat.css');
|
||||
if (data.permissions.timeout) lichess.loadCss('/assets/stylesheets/chat.mod.css');
|
||||
data.loadCss = lichess.loadCss;
|
||||
lichess.loadScript("/assets/compiled/lichess.chat" + (isDev ? '' : '.min') + '.js').done(function() {
|
||||
(callback || $.noop)(LichessChat.default(document.getElementById(id), data));
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue