mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-10-18 08:52:00 +00:00
Add support for asynchronously loaded rich content blocks in chat.
This commit is contained in:
parent
9aafff3a14
commit
d1acc9f7cc
9 changed files with 419 additions and 5 deletions
|
@ -8,6 +8,8 @@ import Twilight from 'site';
|
|||
import Module from 'utilities/module';
|
||||
//import {Color} from 'utilities/color';
|
||||
|
||||
import RichContent from './rich_content';
|
||||
|
||||
const SUB_TIERS = {
|
||||
1000: 1,
|
||||
2000: 2,
|
||||
|
@ -23,6 +25,7 @@ export default class ChatLine extends Module {
|
|||
this.inject('chat');
|
||||
this.inject('site.fine');
|
||||
this.inject('site.web_munch');
|
||||
this.inject(RichContent);
|
||||
|
||||
this.ChatLine = this.fine.define(
|
||||
'chat-line',
|
||||
|
@ -43,13 +46,16 @@ export default class ChatLine extends Module {
|
|||
this.chat.context.on('changed:chat.badges.hidden', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.badges.custom-mod', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.rituals.show', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.rich.enabled', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.rich.hide-tokens', this.updateLines, this);
|
||||
|
||||
const t = this,
|
||||
React = this.web_munch.getModule('react');
|
||||
if ( ! React )
|
||||
return;
|
||||
|
||||
const e = React.createElement;
|
||||
const e = React.createElement,
|
||||
FFZRichContent = this.rich_content && this.rich_content.RichContent;
|
||||
|
||||
this.ChatLine.ready(cls => {
|
||||
cls.prototype.shouldComponentUpdate = function(props, state) {
|
||||
|
@ -101,7 +107,12 @@ export default class ChatLine extends Module {
|
|||
if ( ! msg.message && msg.messageParts )
|
||||
detokenizeMessage(msg);
|
||||
|
||||
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, {login: this.props.currentUserLogin, display: this.props.currentUserDisplayName});
|
||||
const u = {
|
||||
login: this.props.currentUserLogin,
|
||||
display: this.props.currentUserDisplayName
|
||||
},
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
||||
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg);
|
||||
|
||||
let cls = 'chat-line__message',
|
||||
out = (tokens.length || ! msg.ffz_type) ? [
|
||||
|
@ -136,6 +147,8 @@ export default class ChatLine extends Module {
|
|||
onClick: this.alwaysShowMessage
|
||||
}, `<message deleted>`)),
|
||||
|
||||
show && rich_content && e(FFZRichContent, rich_content),
|
||||
|
||||
/*this.state.renderDebug === 2 && e('div', {
|
||||
className: 'border mg-t-05'
|
||||
}, old_render.call(this)),
|
||||
|
|
155
src/sites/twitch-twilight/modules/chat/rich_content.jsx
Normal file
155
src/sites/twitch-twilight/modules/chat/rich_content.jsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
'use strict';
|
||||
|
||||
// ============================================================================
|
||||
// RichContent Component
|
||||
// ============================================================================
|
||||
|
||||
import Module from 'utilities/module';
|
||||
import {timeout} from 'utilities/object';
|
||||
|
||||
const ERROR_IMAGE = 'https://static-cdn.jtvnw.net/emoticons/v1/58765/2.0';
|
||||
|
||||
export default class RichContent extends Module {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.inject('i18n');
|
||||
this.inject('site.web_munch');
|
||||
|
||||
this.RichContent = null;
|
||||
}
|
||||
|
||||
onEnable() {
|
||||
const t = this,
|
||||
React = this.web_munch.getModule('react');
|
||||
if ( ! React )
|
||||
return;
|
||||
|
||||
const createElement = React.createElement;
|
||||
|
||||
this.RichContent = class RichContent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loaded: false,
|
||||
error: false
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
try {
|
||||
let data = this.props.getData();
|
||||
if ( data instanceof Promise ) {
|
||||
const to_wait = this.props.timeout || 1000;
|
||||
if ( to_wait )
|
||||
data = await timeout(data, to_wait);
|
||||
else
|
||||
data = await data;
|
||||
}
|
||||
|
||||
this.setState(Object.assign({
|
||||
loaded: true
|
||||
}, data));
|
||||
|
||||
} catch(err) {
|
||||
this.setState({
|
||||
loaded: true,
|
||||
error: true,
|
||||
title: t.i18n.t('card.error', 'An error occured.'),
|
||||
desc_1: String(err)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderCardImage() {
|
||||
return (<div class="chat-card__preview-img tw-c-background-alt-2 tw-align-items-center tw-flex tw-flex-shrink-0 tw-justify-content-center">
|
||||
<div class="tw-card-img tw-flex-shrink-0 tw-flex tw-justify-content-center">
|
||||
{this.state.error ?
|
||||
(<img
|
||||
class="chat-card__error-img"
|
||||
data-test-selector="chat-card-error"
|
||||
src={ERROR_IMAGE}
|
||||
/>) :
|
||||
(<figure class="tw-aspect tw-aspect--16x9 tw-aspect--align-top">
|
||||
{this.state.loaded && this.state.image ?
|
||||
(<img
|
||||
class="tw-image"
|
||||
src={this.state.image}
|
||||
alt={this.state.title}
|
||||
/>)
|
||||
: null}
|
||||
</figure>)}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
renderCardDescription() {
|
||||
let title = this.state.title,
|
||||
desc_1 = this.state.desc_1,
|
||||
desc_2 = this.state.desc_2;
|
||||
|
||||
if ( ! this.state.loaded ) {
|
||||
desc_1 = t.i18n.t('card.loading', 'Loading...');
|
||||
desc_2 = '';
|
||||
title = '';
|
||||
}
|
||||
|
||||
return (<div class={`tw-overflow-hidden tw-align-items-center tw-flex${desc_2 ? ' ffz--two-line' : ''}`}>
|
||||
<div class="tw-full-width tw-pd-l-1">
|
||||
<div class="chat-card__title tw-ellipsis">
|
||||
<span
|
||||
class="tw-font-size-5"
|
||||
data-test-selector="chat-card-title"
|
||||
title={title}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tw-ellipsis">
|
||||
<span
|
||||
class="tw-c-text-alt-2 tw-font-size-6"
|
||||
data-test-selector="chat-card-description"
|
||||
title={desc_1}
|
||||
>
|
||||
{desc_1}
|
||||
</span>
|
||||
</div>
|
||||
{desc_2 && (<div class="tw-ellipsis">
|
||||
<span
|
||||
class="tw-c-text-alt-2 tw-font-size-6"
|
||||
data-test-selector="chat-card-description"
|
||||
title={desc_2}
|
||||
>
|
||||
{desc_2}
|
||||
</span>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
renderCard() {
|
||||
return (<div class="ffz--chat-card tw-elevation-1 tw-mg-t">
|
||||
<div class="tw-c-background tw-flex tw-flex-nowrap tw-pd-05">
|
||||
{this.renderCardImage()}
|
||||
{this.renderCardDescription()}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
render() {
|
||||
if ( ! this.state.url )
|
||||
return this.renderCard();
|
||||
|
||||
return (<a
|
||||
class="chat-card__link"
|
||||
target="_blank"
|
||||
rel="noreferer noopener"
|
||||
href={this.state.url}
|
||||
>
|
||||
{this.renderCard()}
|
||||
</a>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,11 +19,18 @@
|
|||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
span {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ffz--chat-card {
|
||||
.ffz--two-line {
|
||||
.tw-ellipsis { line-height: 1.4rem }
|
||||
.chat-card__title { line-height: 1.5rem }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue