1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-11 00:20:54 +00:00
* Added: Support for Twitch's replies and threads system. The experiment is currently disabled, but if it returns we want to support it.
* Added: Option to automatically skip channel trailers.
* Fixed: Incorrect appearance of aspect-ratio controlled elements, due to Twitch removing their aspect ratio CSS.
* Fixed: Incorrect color applied to text buttons with a custom accent color set.
* API Added: `chat:get-tab-commands` event for adding custom chat commands to tab-completion.
* API Added: `reply` icon.
This commit is contained in:
SirStendec 2020-08-12 16:10:06 -04:00
parent 6c0c421d0a
commit 463c9f9a45
30 changed files with 536 additions and 40 deletions

View file

@ -65,6 +65,7 @@ export default class Actions extends Module {
),
default: [
{v: {action: 'reply', appearance: {type: 'icon', icon: 'ffz-i-reply'}, options: {}, display: {}}},
{v: {action: 'ban', appearance: {type: 'icon', icon: 'ffz-i-block'}, options: {}, display: {mod: true, mod_icons: true, deleted: false}}},
{v: {action: 'unban', appearance: {type: 'icon', icon: 'ffz-i-ok'}, options: {}, display: {mod: true, mod_icons: true, deleted: true}}},
{v: {action: 'timeout', appearance: {type: 'icon', icon: 'ffz-i-clock'}, display: {mod: true, mod_icons: true}}},
@ -418,6 +419,9 @@ export default class Actions extends Module {
(disp.followersOnly != null && disp.followersOnly !== current_room.followersOnly) )
continue;
if ( maybe_call(act.hidden, this, data, null, current_room, current_user, mod_icons) )
continue;
if ( act.override_appearance ) {
const out = act.override_appearance.call(this, Object.assign({}, ap), data, null, current_room, current_user, mod_icons);
if ( out )
@ -539,6 +543,9 @@ export default class Actions extends Module {
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
continue;
if ( maybe_call(act.hidden, this, data, msg, r, u, mod_icons) )
continue;
if ( act.override_appearance ) {
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, r, u, mod_icons);
if ( out )
@ -600,11 +607,9 @@ export default class Actions extends Module {
renderInline(msg, mod_icons, current_user, current_room, createElement) {
const actions = [];
if ( msg.user && current_user && current_user.login === msg.user.login )
return;
const current_level = this.getUserLevel(current_room, current_user),
msg_level = this.getUserLevel(current_room, msg.user);
msg_level = this.getUserLevel(current_room, msg.user),
is_self = msg.user && current_user && current_user.login === msg.user.login;
if ( current_level < 3 )
mod_icons = false;
@ -630,6 +635,12 @@ export default class Actions extends Module {
(disp.deleted != null && disp.deleted !== !!msg.deleted) )
continue;
if ( is_self && ! act.can_self )
continue;
if ( maybe_call(act.hidden, this, data, msg, current_room, current_user, mod_icons) )
continue;
if ( act.override_appearance ) {
const out = act.override_appearance.call(this, Object.assign({}, ap), data, msg, current_room, current_user, mod_icons);
if ( out )

View file

@ -1,5 +1,54 @@
'use strict';
// ============================================================================
// Send Reply
// ============================================================================
export const reply = {
presets: [{
appearance: {
type: 'icon',
icon: 'ffz-i-reply'
}
}],
required_context: ['message'],
title: 'Reply to Message',
description: 'Allows you to directly reply to another user\'s message.',
can_self: true,
tooltip() {
return this.i18n.t('chat.actions.reply', 'Reply to Message')
},
hidden(data, message, current_room, current_user) {
const id = message?.id;
if ( typeof id !== 'string' || ! /^[0-9a-f]+-[0-9a-f]+/.test(id) )
return true;
if ( ! message.message || message.deleted || (current_user && current_user.login === message.user?.login) || ! current_user?.can_reply )
return true;
if ( message?.reply )
return true;
},
click(event) {
const fine = this.resolve('site.fine'),
line = fine ? fine.searchParent(event.target, n => n.setMessageTray && n.props && n.props.message) : null;
if ( ! line )
return;
line.ffzOpenReply();
//line.setMessageTray(line.props.message, line.props.message.message);
}
}
// ============================================================================
// Edit Overrides
// ============================================================================

View file

@ -50,6 +50,7 @@ export default class Chat extends Module {
// Bind for JSX stuff
this.clickToReveal = this.clickToReveal.bind(this);
this.handleMentionClick = this.handleMentionClick.bind(this);
this.handleReplyClick = this.handleReplyClick.bind(this);
this.style = new ManagedStyle;
@ -1073,6 +1074,19 @@ export default class Chat extends Module {
}
handleReplyClick(event) {
const target = event.target,
fine = this.resolve('site.fine');
if ( ! target || ! fine )
return;
const chat = fine.searchParent(target, n => n.props && n.props.reply && n.setOPCardTray);
if ( chat )
chat.setOPCardTray(chat.props.reply);
}
handleMentionClick(event) {
if ( ! this.context.get('chat.filtering.clickable-mentions') )
return;
@ -1087,7 +1101,7 @@ export default class Chat extends Module {
if ( ! fine )
return;
const chat = fine.searchParent(event.target, n => n.props && n.props.onUsernameClick);
const chat = fine.searchParent(target, n => n.props && n.props.onUsernameClick);
if ( ! chat )
return;
@ -1174,6 +1188,25 @@ export default class Chat extends Module {
}
tokenizeReply(reply) {
if ( ! reply )
return null;
return [
{
type: 'reply',
text: reply.parentDisplayName,
color: this.color_cache ? this.color_cache.get(reply.parentUserLogin) : null,
recipient: reply.parentUserLogin
},
{
type: 'text',
text: ' '
}
];
}
standardizeMessage(msg) { // eslint-disable-line class-methods-use-this
if ( ! msg )
return msg;
@ -1487,7 +1520,7 @@ export default class Chat extends Module {
}
renderTokens(tokens, e) {
renderTokens(tokens, e, reply) {
if ( ! e )
e = createElement;
@ -1505,6 +1538,10 @@ export default class Chat extends Module {
let res;
// If we have a reply, skip the initial mention.
if ( reply && i === 0 && type === 'mention' && token.recipient && token.recipient === reply.parentUserLogin )
continue;
if ( type === 'text' )
res = e('span', {
className: 'text-fragment',
@ -1512,7 +1549,7 @@ export default class Chat extends Module {
}, token.text);
else if ( tk )
res = tk.render.call(this, token, e);
res = tk.render.call(this, token, e, reply);
else
res = e('em', {

View file

@ -232,6 +232,61 @@ Links.tooltip.delayHide = function(target) {
}*/
// ============================================================================
// Replies (Styled Like Mentions)
// ============================================================================
export const Replies = {
type: 'reply',
priority: 0,
component: () => null,
render(token, createElement) {
let color = token.color;
if ( color ) {
const chat = this.resolve('site.chat');
color = chat ? chat.colors.process(color) : color;
}
return (<strong
class={`chat-line__message-mention ffz-tooltip ffz--reply-mention ffz-i-reply${token.me ? ' ffz--mention-me' : ''}`}
style={{color}}
data-tooltip-type="reply"
data-login={token.recipient}
onClick={this.handleReplyClick}
>
{token.text}
</strong>)
},
tooltip(target) {
const fine = this.resolve('site.fine');
if ( ! target || ! fine )
return null;
const chat = fine.searchParent(target, n => n.props && n.props.reply && n.setOPCardTray),
reply = chat?.props?.reply;
if ( ! reply )
return null;
return [
createElement('strong', {}, this.i18n.t('chat.reply-to', 'Replying To:')),
'\n\n',
createElement('div', {className: 'tw-align-left'}, [
createElement('strong', {}, reply.parentDisplayName),
': ',
reply.parentMessageBody
])
];
},
process(tokens) {
return tokens;
}
}
// ============================================================================
// Mentions
// ============================================================================