1
0
Fork 0
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:
SirStendec 2018-04-03 19:28:06 -04:00
parent 9aafff3a14
commit d1acc9f7cc
9 changed files with 419 additions and 5 deletions

View file

@ -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)),

View 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>);
}
}
}
}

View file

@ -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 }
}
}