mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-11 00:20:54 +00:00
4.20.28
* 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:
parent
6c0c421d0a
commit
463c9f9a45
30 changed files with 536 additions and 40 deletions
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
// ============================================================================
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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
|
||||
// ============================================================================
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue