2017-11-14 22:13:30 -05:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// Chat Line
|
|
|
|
// ============================================================================
|
|
|
|
|
2018-03-14 13:58:04 -04:00
|
|
|
import Twilight from 'site';
|
2017-11-14 22:13:30 -05:00
|
|
|
import Module from 'utilities/module';
|
|
|
|
|
2018-04-03 19:28:06 -04:00
|
|
|
import RichContent from './rich_content';
|
|
|
|
|
2017-11-23 02:49:23 -05:00
|
|
|
const SUB_TIERS = {
|
2018-03-11 14:04:55 -04:00
|
|
|
1000: 1,
|
|
|
|
2000: 2,
|
|
|
|
3000: 3
|
2017-11-23 02:49:23 -05:00
|
|
|
};
|
|
|
|
|
2017-11-14 22:13:30 -05:00
|
|
|
export default class ChatLine extends Module {
|
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
|
|
|
|
this.inject('settings');
|
|
|
|
this.inject('i18n');
|
|
|
|
this.inject('chat');
|
2018-04-28 17:56:03 -04:00
|
|
|
this.inject('site');
|
2017-11-14 22:13:30 -05:00
|
|
|
this.inject('site.fine');
|
|
|
|
this.inject('site.web_munch');
|
2018-04-30 20:27:14 +02:00
|
|
|
this.inject('site.apollo');
|
2018-04-03 19:28:06 -04:00
|
|
|
this.inject(RichContent);
|
2017-11-14 22:13:30 -05:00
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
this.inject('viewer_cards');
|
2018-05-10 19:56:39 -04:00
|
|
|
|
2018-04-28 17:56:03 -04:00
|
|
|
this.inject('chat.actions');
|
|
|
|
|
2017-11-14 22:13:30 -05:00
|
|
|
this.ChatLine = this.fine.define(
|
|
|
|
'chat-line',
|
2018-05-18 02:10:00 -04:00
|
|
|
n => n.renderMessageBody && n.props && ! n.props.roomID,
|
2018-03-14 13:58:04 -04:00
|
|
|
Twilight.CHAT_ROUTES
|
2018-02-22 18:23:44 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
this.ChatRoomLine = this.fine.define(
|
|
|
|
'chat-room-line',
|
2018-05-18 02:10:00 -04:00
|
|
|
n => n.renderMessageBody && n.props && n.props.roomID,
|
2018-03-14 13:58:04 -04:00
|
|
|
Twilight.CHAT_ROUTES
|
2017-11-14 22:13:30 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
onEnable() {
|
2018-04-12 20:30:00 -04:00
|
|
|
this.chat.context.on('changed:chat.emoji.style', this.updateLines, this);
|
2017-11-14 22:13:30 -05:00
|
|
|
this.chat.context.on('changed:chat.bits.stack', this.updateLines, this);
|
2018-02-02 16:00:58 -05:00
|
|
|
this.chat.context.on('changed:chat.badges.style', this.updateLines, this);
|
2018-03-22 22:39:27 -04:00
|
|
|
this.chat.context.on('changed:chat.badges.hidden', this.updateLines, this);
|
2018-03-15 03:31:30 -04:00
|
|
|
this.chat.context.on('changed:chat.badges.custom-mod', this.updateLines, this);
|
2018-02-02 16:00:58 -05:00
|
|
|
this.chat.context.on('changed:chat.rituals.show', this.updateLines, this);
|
2018-04-03 19:28:06 -04:00
|
|
|
this.chat.context.on('changed:chat.rich.enabled', this.updateLines, this);
|
|
|
|
this.chat.context.on('changed:chat.rich.hide-tokens', this.updateLines, this);
|
2018-04-28 17:56:03 -04:00
|
|
|
this.chat.context.on('changed:chat.actions.inline', this.updateLines, this);
|
2017-11-14 22:13:30 -05:00
|
|
|
|
|
|
|
const t = this,
|
|
|
|
React = this.web_munch.getModule('react');
|
|
|
|
if ( ! React )
|
|
|
|
return;
|
|
|
|
|
2018-04-03 19:28:06 -04:00
|
|
|
const e = React.createElement,
|
|
|
|
FFZRichContent = this.rich_content && this.rich_content.RichContent;
|
2017-11-14 22:13:30 -05:00
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
|
|
|
|
/*this.ChatRoomLine.ready(cls => {
|
|
|
|
cls.prototype.render = function() {
|
|
|
|
const msg = t.chat.standardizeMessage(this.props.message),
|
|
|
|
is_action = msg.is_action,
|
|
|
|
user = msg.user,
|
|
|
|
color = t.parent.colors.process(user.color),
|
|
|
|
bg_css = null,
|
|
|
|
show = this._ffz_show = this.state.shouldShowDeletedBody || ! msg.deletedAt;
|
|
|
|
|
|
|
|
let room = msg.roomLogin ? msg.roomLogin : msg.channel ? msg.channel.slice(1) : null;
|
|
|
|
|
|
|
|
if ( ! room && this.props.channelID ) {
|
|
|
|
const r = t.chat.getRoom(this.props.channelID, null, true);
|
|
|
|
if ( r && r.login )
|
|
|
|
room = msg.roomLogin = r.login;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u = t.site.getUser(),
|
|
|
|
r = {id: this.props.channelID, login: room};
|
|
|
|
|
|
|
|
if ( u ) {
|
|
|
|
u.moderator = this.props.isCurrentUserModerator;
|
|
|
|
u.staff = this.props.isCurrentUserStaff;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
|
|
|
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg);
|
|
|
|
|
|
|
|
if ( ! this.ffz_user_click_handler )
|
|
|
|
this.ffz_user_click_handler = event =>
|
|
|
|
event.ctrlKey ?
|
|
|
|
this.props.onUsernameClick(user.login, null, msg.id, event.currentTarget.getBoundingClientRect().bottom) :
|
|
|
|
t.viewer_cards.openCard(r, user, event);
|
|
|
|
|
|
|
|
let cls = 'chat-line__message',
|
|
|
|
out = (tokens.length || ! msg.ffz_type) ? [
|
|
|
|
this.props.showTimestamps && e('span', {
|
|
|
|
className: 'chat-line__timestamp'
|
|
|
|
}, t.chat.formatTime(msg.timestamp)),
|
|
|
|
t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
|
|
|
e('span', {
|
|
|
|
className: 'chat-line__message--badges'
|
|
|
|
}, t.chat.badges.render(msg, e)),
|
|
|
|
e('a', {
|
|
|
|
className: 'chat-author__display-name notranslate',
|
|
|
|
style: { color },
|
|
|
|
onClick: this.ffz_user_click_handler
|
|
|
|
}, [
|
|
|
|
user.displayName,
|
|
|
|
user.isIntl && e('span', {
|
|
|
|
className: 'chat-author__intl-login'
|
|
|
|
}, ` (${user.login})`)
|
|
|
|
]),
|
|
|
|
e('span', null, is_action ? ' ' : ': '),
|
|
|
|
show ?
|
|
|
|
e('span', {
|
|
|
|
className: 'message',
|
|
|
|
style: is_action ? { color } : null
|
|
|
|
}, t.chat.renderTokens(tokens, e))
|
|
|
|
:
|
|
|
|
e('span', {
|
|
|
|
className: 'chat-line__message--deleted'
|
|
|
|
}, e('a', {
|
|
|
|
href: '',
|
|
|
|
onClick: this.showDeleted
|
|
|
|
}, t.i18n.t('chat.message-deleted', '<message deleted>'))),
|
|
|
|
|
|
|
|
show && rich_content && e(FFZRichContent, rich_content)
|
|
|
|
] : null;
|
|
|
|
|
|
|
|
if ( ! out )
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return e('div', {
|
|
|
|
className: `${cls}${msg.mentioned ? ' ffz-mentioned' : ''}`,
|
|
|
|
style: {backgroundColor: bg_css},
|
|
|
|
id: msg.id,
|
|
|
|
'data-room-id': this.props.channelID,
|
|
|
|
'data-room': room,
|
|
|
|
'data-user-id': user.id,
|
|
|
|
'data-user': user.login && user.login.toLowerCase()
|
|
|
|
}, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do this after a short delay to hopefully reduce the chance of React
|
|
|
|
// freaking out on us.
|
|
|
|
setTimeout(() => this.ChatRoomLine.forceUpdate());
|
|
|
|
});*/
|
|
|
|
|
|
|
|
|
2018-03-14 13:58:04 -04:00
|
|
|
this.ChatLine.ready(cls => {
|
2017-11-14 22:13:30 -05:00
|
|
|
cls.prototype.shouldComponentUpdate = function(props, state) {
|
|
|
|
const show = state.alwaysShowMessage || ! props.message.deleted,
|
|
|
|
old_show = this._ffz_show;
|
|
|
|
|
|
|
|
// We can't just compare props.message.deleted to this.props.message.deleted
|
|
|
|
// because the message object is the same object. So, store the old show
|
|
|
|
// state for later reference.
|
|
|
|
this._ffz_show = show;
|
|
|
|
|
|
|
|
return show !== old_show ||
|
|
|
|
//state.renderDebug !== this.state.renderDebug ||
|
|
|
|
props.message !== this.props.message ||
|
|
|
|
props.isCurrentUserModerator !== this.props.isCurrentUserModerator ||
|
|
|
|
props.showModerationIcons !== this.props.showModerationIcons ||
|
|
|
|
props.showTimestamps !== this.props.showTimestamps;
|
|
|
|
}
|
|
|
|
|
|
|
|
cls.prototype.render = function() {
|
2018-04-28 17:56:03 -04:00
|
|
|
const types = t.parent.message_types || {},
|
2017-11-17 14:59:46 -05:00
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
msg = t.chat.standardizeMessage(this.props.message),
|
2018-02-27 01:41:19 -05:00
|
|
|
is_action = msg.messageType === types.Action;
|
2018-02-22 18:23:44 -05:00
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
/*if ( msg.content && ! msg.message )
|
2018-02-22 18:23:44 -05:00
|
|
|
msg.message = msg.content.text;
|
|
|
|
|
|
|
|
if ( msg.sender && ! msg.user ) {
|
|
|
|
msg.user = msg.sender;
|
|
|
|
msg.user.color = msg.user.color || msg.user.chatColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! msg.badges && msg.user.displayBadges ) {
|
|
|
|
const b = msg.badges = {};
|
|
|
|
for(const item of msg.user.displayBadges)
|
|
|
|
b[item.setID] = item.version;
|
2018-05-18 02:10:00 -04:00
|
|
|
}*/
|
2018-02-22 18:23:44 -05:00
|
|
|
|
|
|
|
const user = msg.user,
|
2017-11-14 22:13:30 -05:00
|
|
|
color = t.parent.colors.process(user.color),
|
2018-04-28 17:56:03 -04:00
|
|
|
bg_css = null, //Math.random() > .7 ? t.parent.inverse_colors.process(user.color) : null,
|
2018-05-18 02:10:00 -04:00
|
|
|
show = this._ffz_show = this.state.alwaysShowMessage || ! msg.deleted;
|
2017-11-14 22:13:30 -05:00
|
|
|
|
2018-04-29 01:28:19 -04:00
|
|
|
let room = msg.roomLogin ? msg.roomLogin : msg.channel ? msg.channel.slice(1) : undefined;
|
|
|
|
|
|
|
|
if ( ! room && this.props.channelID ) {
|
|
|
|
const r = t.chat.getRoom(this.props.channelID, null, true);
|
|
|
|
if ( r && r.login )
|
|
|
|
room = msg.roomLogin = r.login;
|
|
|
|
}
|
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
//if ( ! msg.message && msg.messageParts )
|
|
|
|
// t.chat.detokenizeMessage(msg);
|
2017-11-14 22:13:30 -05:00
|
|
|
|
2018-04-28 17:56:03 -04:00
|
|
|
const u = t.site.getUser(),
|
|
|
|
r = {id: this.props.channelID, login: room};
|
|
|
|
|
|
|
|
if ( u ) {
|
|
|
|
u.moderator = this.props.isCurrentUserModerator;
|
|
|
|
u.staff = this.props.isCurrentUserStaff;
|
|
|
|
}
|
|
|
|
|
|
|
|
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u, r),
|
2018-04-03 19:28:06 -04:00
|
|
|
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg);
|
2017-11-14 22:13:30 -05:00
|
|
|
|
2018-05-10 19:56:39 -04:00
|
|
|
if ( ! this.ffz_user_click_handler )
|
2018-05-18 02:10:00 -04:00
|
|
|
this.ffz_user_click_handler = event => event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event);
|
2018-05-10 19:56:39 -04:00
|
|
|
|
2017-11-23 02:49:23 -05:00
|
|
|
let cls = 'chat-line__message',
|
2018-03-01 04:13:52 -05:00
|
|
|
out = (tokens.length || ! msg.ffz_type) ? [
|
2017-11-23 02:49:23 -05:00
|
|
|
this.props.showTimestamps && e('span', {
|
|
|
|
className: 'chat-line__timestamp'
|
|
|
|
}, t.chat.formatTime(msg.timestamp)),
|
2018-04-28 17:56:03 -04:00
|
|
|
t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
2017-11-23 02:49:23 -05:00
|
|
|
e('span', {
|
|
|
|
className: 'chat-line__message--badges'
|
2017-11-28 02:03:59 -05:00
|
|
|
}, t.chat.badges.render(msg, e)),
|
2017-11-23 02:49:23 -05:00
|
|
|
e('a', {
|
2017-11-28 02:03:59 -05:00
|
|
|
className: 'chat-author__display-name notranslate',
|
2017-11-23 02:49:23 -05:00
|
|
|
style: { color },
|
2018-05-10 19:56:39 -04:00
|
|
|
onClick: this.usernameClickHandler, // this.ffz_user_click_handler
|
2017-11-28 02:03:59 -05:00
|
|
|
}, [
|
|
|
|
user.userDisplayName,
|
|
|
|
user.isIntl && e('span', {
|
|
|
|
className: 'chat-author__intl-login'
|
|
|
|
}, ` (${user.userLogin})`)
|
|
|
|
]),
|
2017-11-23 02:49:23 -05:00
|
|
|
e('span', null, is_action ? ' ' : ': '),
|
|
|
|
show ?
|
|
|
|
e('span', {
|
|
|
|
className:'message',
|
|
|
|
style: is_action ? { color } : null
|
2017-11-28 02:03:59 -05:00
|
|
|
}, t.chat.renderTokens(tokens, e))
|
2017-11-23 02:49:23 -05:00
|
|
|
:
|
|
|
|
e('span', {
|
|
|
|
className: 'chat-line__message--deleted',
|
|
|
|
}, e('a', {
|
|
|
|
href: '',
|
|
|
|
onClick: this.alwaysShowMessage
|
|
|
|
}, `<message deleted>`)),
|
|
|
|
|
2018-04-03 19:28:06 -04:00
|
|
|
show && rich_content && e(FFZRichContent, rich_content),
|
|
|
|
|
2017-11-23 02:49:23 -05:00
|
|
|
/*this.state.renderDebug === 2 && e('div', {
|
|
|
|
className: 'border mg-t-05'
|
|
|
|
}, old_render.call(this)),
|
|
|
|
|
|
|
|
this.state.renderDebug === 1 && e('div', {
|
|
|
|
className: 'message--debug',
|
|
|
|
style: {
|
|
|
|
fontFamily: 'monospace',
|
|
|
|
whiteSpace: 'pre-wrap',
|
|
|
|
lineHeight: '1.1em'
|
|
|
|
}
|
|
|
|
}, JSON.stringify([tokens, msg.emotes], null, 2))*/
|
|
|
|
] : null;
|
|
|
|
|
|
|
|
if ( msg.ffz_type === 'resub' ) {
|
|
|
|
const plan = msg.sub_plan || {},
|
|
|
|
months = msg.sub_months || 1,
|
2018-03-11 14:04:55 -04:00
|
|
|
tier = SUB_TIERS[plan.plan] || 1;
|
2017-11-23 02:49:23 -05:00
|
|
|
|
2018-04-10 21:13:34 -04:00
|
|
|
cls = 'user-notice-line tw-pd-y-05 tw-pd-r-2 ffz--subscribe-line';
|
2017-11-23 02:49:23 -05:00
|
|
|
out = [
|
2018-04-10 21:13:34 -04:00
|
|
|
e('div', {className: 'tw-c-text-alt-2'}, [
|
2017-11-23 02:49:23 -05:00
|
|
|
t.i18n.t('chat.sub.main', '%{user} just subscribed with %{plan}!', {
|
|
|
|
user: user.userDisplayName,
|
|
|
|
plan: plan.prime ?
|
|
|
|
t.i18n.t('chat.sub.twitch-prime', 'Twitch Prime') :
|
2018-03-11 14:04:55 -04:00
|
|
|
t.i18n.t('chat.sub.plan', 'a Tier %{tier} sub', {tier})
|
2017-11-23 02:49:23 -05:00
|
|
|
}),
|
|
|
|
months > 1 ?
|
|
|
|
` ${t.i18n.t(
|
|
|
|
'chat.sub.months',
|
|
|
|
'%{user} subscribed for %{count} months in a row!',
|
|
|
|
{
|
|
|
|
user: user.userDisplayName,
|
|
|
|
count: months
|
|
|
|
})}` : null
|
|
|
|
]),
|
|
|
|
out && e('div', {
|
2018-04-10 21:13:34 -04:00
|
|
|
className: 'chat-line--inline chat-line__message',
|
2017-11-23 02:49:23 -05:00
|
|
|
'data-room-id': this.props.channelID,
|
|
|
|
'data-room': room,
|
|
|
|
'data-user-id': user.userID,
|
|
|
|
'data-user': user.userLogin && user.userLogin.toLowerCase(),
|
|
|
|
}, out)
|
|
|
|
];
|
|
|
|
|
2018-02-02 16:00:58 -05:00
|
|
|
} else if ( msg.ffz_type === 'ritual' && t.chat.context.get('chat.rituals.show') ) {
|
|
|
|
let system_msg;
|
|
|
|
if ( msg.ritual === 'new_chatter' )
|
2018-04-10 21:13:34 -04:00
|
|
|
system_msg = e('div', {className: 'tw-c-text-alt-2'}, [
|
|
|
|
t.i18n.t('chat.ritual', '%{user} is new here. Say hello!', {
|
|
|
|
user: user.userDisplayName
|
|
|
|
})
|
|
|
|
]);
|
2018-02-02 16:00:58 -05:00
|
|
|
|
|
|
|
if ( system_msg ) {
|
2018-04-10 21:13:34 -04:00
|
|
|
cls = 'user-notice-line tw-pd-y-05 tw-pd-r-2 ffz--ritual-line';
|
2018-02-02 16:00:58 -05:00
|
|
|
out = [
|
|
|
|
system_msg,
|
|
|
|
out && e('div', {
|
2018-04-10 21:13:34 -04:00
|
|
|
className: 'chat-line--inline chat-line__message',
|
2018-02-02 16:00:58 -05:00
|
|
|
'data-room-id': this.props.channelID,
|
|
|
|
'data-room': room,
|
|
|
|
'data-user-id': user.userID,
|
|
|
|
'data-user': user.userLogin && user.userLogin.toLowerCase(),
|
|
|
|
}, out)
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! out )
|
2017-11-23 02:49:23 -05:00
|
|
|
return null;
|
|
|
|
|
|
|
|
return e('div', {
|
2017-11-23 14:32:30 -05:00
|
|
|
className: `${cls}${msg.mentioned ? ' ffz-mentioned' : ''}`,
|
2018-04-28 17:56:03 -04:00
|
|
|
style: {backgroundColor: bg_css},
|
2017-11-14 22:13:30 -05:00
|
|
|
'data-room-id': this.props.channelID,
|
|
|
|
'data-room': room,
|
|
|
|
'data-user-id': user.userID,
|
|
|
|
'data-user': user.userLogin && user.userLogin.toLowerCase(),
|
2017-11-23 02:49:23 -05:00
|
|
|
}, out);
|
2017-11-14 22:13:30 -05:00
|
|
|
}
|
|
|
|
|
2018-03-01 04:13:52 -05:00
|
|
|
// Do this after a short delay to hopefully reduce the chance of React
|
|
|
|
// freaking out on us.
|
|
|
|
setTimeout(() => this.ChatLine.forceUpdate());
|
2017-11-14 22:13:30 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updateLines() {
|
2017-12-01 15:33:06 -05:00
|
|
|
for(const inst of this.ChatLine.instances) {
|
|
|
|
const msg = inst.props.message;
|
|
|
|
if ( msg )
|
|
|
|
msg.ffz_tokens = null;
|
|
|
|
}
|
2018-03-01 04:13:52 -05:00
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
for(const inst of this.ChatRoomLine.instances) {
|
|
|
|
const msg = inst.props.message;
|
|
|
|
if ( msg )
|
|
|
|
msg.ffz_tokens = null;
|
2017-11-14 22:13:30 -05:00
|
|
|
}
|
|
|
|
|
2018-05-18 02:10:00 -04:00
|
|
|
this.ChatLine.forceUpdate();
|
|
|
|
this.ChatRoomLine.forceUpdate();
|
|
|
|
}
|
2017-11-14 22:13:30 -05:00
|
|
|
}
|