mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-05 10:38:30 +00:00
4.0.0-rc17
* Added: Highlight messages based on usernames and badges. * Added: Block messages based on usernames and badges. * Fixed: Display the number of months a subscriber has subscribed in badge tool-tips. * Fixed: Rendering of chat messages sent from Twitch Extensions.
This commit is contained in:
parent
b9cca1053d
commit
1649294bde
10 changed files with 809 additions and 82 deletions
|
@ -149,7 +149,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-rc16.3',
|
major: 4, minor: 0, revision: 0, extra: '-rc17',
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
|
@ -155,67 +155,7 @@ export default class Badges extends Module {
|
||||||
path: 'Chat > Badges >> tabs ~> Visibility',
|
path: 'Chat > Badges >> tabs ~> Visibility',
|
||||||
title: 'Visibility',
|
title: 'Visibility',
|
||||||
component: 'badge-visibility',
|
component: 'badge-visibility',
|
||||||
data: () => {
|
data: () => this.getSettingsBadges(true)
|
||||||
const twitch = [],
|
|
||||||
game = [],
|
|
||||||
ffz = [],
|
|
||||||
addon = [];
|
|
||||||
|
|
||||||
for(const key in this.twitch_badges)
|
|
||||||
if ( has(this.twitch_badges, key) ) {
|
|
||||||
const badge = this.twitch_badges[key],
|
|
||||||
vs = [];
|
|
||||||
let v = badge && (badge[1] || badge[0]);
|
|
||||||
|
|
||||||
for(const key in badge)
|
|
||||||
if ( has(badge, key) ) {
|
|
||||||
const version = badge[key];
|
|
||||||
if ( ! v )
|
|
||||||
v = version;
|
|
||||||
|
|
||||||
if ( version && version.image1x )
|
|
||||||
vs.push({
|
|
||||||
version: key,
|
|
||||||
name: version.title,
|
|
||||||
image: version.image1x,
|
|
||||||
styleImage: `url("${version.image1x}")`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( v )
|
|
||||||
(badge.__game ? game : twitch).push({
|
|
||||||
id: key,
|
|
||||||
provider: 'twitch',
|
|
||||||
name: v.title,
|
|
||||||
color: 'transparent',
|
|
||||||
image: v.image2x,
|
|
||||||
versions: vs,
|
|
||||||
styleImage: `url("${v.image2x}")`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const key in this.badges)
|
|
||||||
if ( has(this.badges, key) ) {
|
|
||||||
const badge = this.badges[key],
|
|
||||||
image = badge.urls ? (badge.urls[2] || badge.urls[1]) : badge.image;
|
|
||||||
|
|
||||||
(/^addon/.test(key) ? addon : ffz).push({
|
|
||||||
id: key,
|
|
||||||
provider: 'ffz',
|
|
||||||
name: badge.title,
|
|
||||||
color: badge.color || 'transparent',
|
|
||||||
image,
|
|
||||||
styleImage: `url("${image}")`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
{title: 'Twitch', badges: twitch},
|
|
||||||
{title: 'Twitch: Game', key: 'game', badges: game},
|
|
||||||
{title: 'FrankerFaceZ', badges: ffz},
|
|
||||||
{title: 'Add-on', badges: addon}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -247,6 +187,69 @@ export default class Badges extends Module {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSettingsBadges(include_addons) {
|
||||||
|
const twitch = [],
|
||||||
|
game = [],
|
||||||
|
ffz = [],
|
||||||
|
addon = [];
|
||||||
|
|
||||||
|
for(const key in this.twitch_badges)
|
||||||
|
if ( has(this.twitch_badges, key) ) {
|
||||||
|
const badge = this.twitch_badges[key],
|
||||||
|
vs = [];
|
||||||
|
let v = badge && (badge[1] || badge[0]);
|
||||||
|
|
||||||
|
for(const key in badge)
|
||||||
|
if ( has(badge, key) ) {
|
||||||
|
const version = badge[key];
|
||||||
|
if ( ! v )
|
||||||
|
v = version;
|
||||||
|
|
||||||
|
if ( version && version.image1x )
|
||||||
|
vs.push({
|
||||||
|
version: key,
|
||||||
|
name: version.title,
|
||||||
|
image: version.image1x,
|
||||||
|
styleImage: `url("${version.image1x}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( v )
|
||||||
|
(badge.__game ? game : twitch).push({
|
||||||
|
id: key,
|
||||||
|
provider: 'twitch',
|
||||||
|
name: v.title,
|
||||||
|
color: 'transparent',
|
||||||
|
image: v.image2x,
|
||||||
|
versions: vs,
|
||||||
|
styleImage: `url("${v.image2x}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( include_addons )
|
||||||
|
for(const key in this.badges)
|
||||||
|
if ( has(this.badges, key) ) {
|
||||||
|
const badge = this.badges[key],
|
||||||
|
image = badge.urls ? (badge.urls[2] || badge.urls[1]) : badge.image;
|
||||||
|
|
||||||
|
(/^addon/.test(key) ? addon : ffz).push({
|
||||||
|
id: key,
|
||||||
|
provider: 'ffz',
|
||||||
|
name: badge.title,
|
||||||
|
color: badge.color || 'transparent',
|
||||||
|
image,
|
||||||
|
styleImage: `url("${image}")`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{title: 'Twitch', badges: twitch},
|
||||||
|
{title: 'Twitch: Game', key: 'game', badges: game},
|
||||||
|
{title: 'FrankerFaceZ', badges: ffz},
|
||||||
|
{title: 'Add-on', badges: addon}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
this.parent.context.on('changed:chat.badges.custom-mod', this.rebuildAllCSS, this);
|
this.parent.context.on('changed:chat.badges.custom-mod', this.rebuildAllCSS, this);
|
||||||
|
@ -278,9 +281,19 @@ export default class Badges extends Module {
|
||||||
if ( ! bd )
|
if ( ! bd )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
let title = bd.title || global_badge.title;
|
||||||
|
if ( d.data ) {
|
||||||
|
if ( d.badge === 'subscriber' ) {
|
||||||
|
title = this.i18n.t('badges.subscriber.months', '%{title} (%{count} Month%{count|en_plural})', {
|
||||||
|
title,
|
||||||
|
count: d.data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out.push(<div class="ffz-badge-tip">
|
out.push(<div class="ffz-badge-tip">
|
||||||
{show_previews && <img class="preview-image ffz-badge" src={bd.image4x} />}
|
{show_previews && <img class="preview-image ffz-badge" src={bd.image4x} />}
|
||||||
{bd.title || global_badge.title}
|
{title}
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
/*out.push(e('div', {className: 'ffz-badge-tip'}, [
|
/*out.push(e('div', {className: 'ffz-badge-tip'}, [
|
||||||
|
@ -332,6 +345,7 @@ export default class Badges extends Module {
|
||||||
out = [],
|
out = [],
|
||||||
slotted = {},
|
slotted = {},
|
||||||
twitch_badges = msg.badges || {},
|
twitch_badges = msg.badges || {},
|
||||||
|
dynamic_data = msg.badgeDynamicData || {},
|
||||||
|
|
||||||
user = msg.user || {},
|
user = msg.user || {},
|
||||||
user_id = user.id,
|
user_id = user.id,
|
||||||
|
@ -357,7 +371,8 @@ export default class Badges extends Module {
|
||||||
else
|
else
|
||||||
slot = last_slot++;
|
slot = last_slot++;
|
||||||
|
|
||||||
const urls = badge_id === 'moderator' && custom_mod && room && room.data && room.data.mod_urls,
|
const data = dynamic_data[badge_id],
|
||||||
|
urls = badge_id === 'moderator' && custom_mod && room && room.data && room.data.mod_urls,
|
||||||
badges = [];
|
badges = [];
|
||||||
|
|
||||||
if ( urls ) {
|
if ( urls ) {
|
||||||
|
@ -366,14 +381,16 @@ export default class Badges extends Module {
|
||||||
provider: 'ffz',
|
provider: 'ffz',
|
||||||
image: urls[4] || urls[2] || urls[1],
|
image: urls[4] || urls[2] || urls[1],
|
||||||
color: '#34ae0a',
|
color: '#34ae0a',
|
||||||
title: bd ? bd.title : 'Moderator'
|
title: bd ? bd.title : 'Moderator',
|
||||||
|
data
|
||||||
});
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
badges.push({
|
badges.push({
|
||||||
provider: 'twitch',
|
provider: 'twitch',
|
||||||
badge: badge_id,
|
badge: badge_id,
|
||||||
version
|
version,
|
||||||
|
data
|
||||||
});
|
});
|
||||||
|
|
||||||
slotted[slot] = {
|
slotted[slot] = {
|
||||||
|
|
|
@ -186,6 +186,7 @@ export default class Chat extends Module {
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Chat > Behavior >> Deleted Messages',
|
path: 'Chat > Behavior >> Deleted Messages',
|
||||||
title: 'Deleted Message Style',
|
title: 'Deleted Message Style',
|
||||||
|
description: 'This style will be applied to deleted messages showed in Detailed rendering mode to differentiate them from normal chat messages.',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
{value: 0, title: 'Faded'},
|
{value: 0, title: 'Faded'},
|
||||||
|
@ -200,8 +201,8 @@ export default class Chat extends Module {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Chat > Behavior >> Deleted Messages',
|
path: 'Chat > Behavior >> Deleted Messages',
|
||||||
title: 'Deleted Message Rendering',
|
title: 'Rendering Mode',
|
||||||
description: 'This, when set, overrides the mode selected in Twitch Chat settings. We do this to allow non-moderators access to the setting.',
|
description: 'This, when set, overrides the mode selected in Twitch chat settings. We do this to allow non-moderators access to the setting.',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
{value: false, title: 'Do Not Override'},
|
{value: false, title: 'Do Not Override'},
|
||||||
|
@ -216,7 +217,7 @@ export default class Chat extends Module {
|
||||||
default: 1,
|
default: 1,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Chat > Behavior >> Deleted Messages',
|
path: 'Chat > Behavior >> Deleted Messages',
|
||||||
title: 'Display Deletion Reason',
|
title: 'Display Reason',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
{value: 0, title: 'Never'},
|
{value: 0, title: 'Never'},
|
||||||
|
@ -285,6 +286,177 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-users', {
|
||||||
|
default: [],
|
||||||
|
type: 'array_merge',
|
||||||
|
always_inherit: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Filtering >> Highlight Users',
|
||||||
|
component: 'basic-terms',
|
||||||
|
colored: true,
|
||||||
|
words: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-users--color-regex', {
|
||||||
|
requires: ['chat.filtering.highlight-basic-users'],
|
||||||
|
process(ctx) {
|
||||||
|
const val = ctx.get('chat.filtering.highlight-basic-users');
|
||||||
|
if ( ! val || ! val.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const colors = new Map;
|
||||||
|
|
||||||
|
for(const item of val) {
|
||||||
|
const c = item.c || null,
|
||||||
|
t = item.t;
|
||||||
|
|
||||||
|
let v = item.v;
|
||||||
|
|
||||||
|
if ( t === 'glob' )
|
||||||
|
v = glob_to_regex(v);
|
||||||
|
|
||||||
|
else if ( t !== 'raw' )
|
||||||
|
v = escape_regex(v);
|
||||||
|
|
||||||
|
if ( ! v || ! v.length )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
new RegExp(v);
|
||||||
|
} catch(err) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( colors.has(c) )
|
||||||
|
colors.get(c).push(v);
|
||||||
|
else {
|
||||||
|
colors.set(c, [v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const [key, list] of colors) {
|
||||||
|
colors.set(key, new RegExp(`^${list.join('|')}$`, 'gi'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-users-blocked', {
|
||||||
|
default: [],
|
||||||
|
type: 'array_merge',
|
||||||
|
always_inherit: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Filtering >> Blocked Users',
|
||||||
|
component: 'basic-terms',
|
||||||
|
removable: true,
|
||||||
|
words: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-users-blocked--regex', {
|
||||||
|
requires: ['chat.filtering.highlight-basic-blocked'],
|
||||||
|
process(ctx) {
|
||||||
|
const val = ctx.get('chat.filtering.highlight-basic-users-blocked');
|
||||||
|
if ( ! val || ! val.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const out = [[], []];
|
||||||
|
|
||||||
|
for(const item of val) {
|
||||||
|
const t = item.t;
|
||||||
|
let v = item.v;
|
||||||
|
|
||||||
|
if ( t === 'glob' )
|
||||||
|
v = glob_to_regex(v);
|
||||||
|
|
||||||
|
else if ( t !== 'raw' )
|
||||||
|
v = escape_regex(v);
|
||||||
|
|
||||||
|
if ( ! v || ! v.length )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
out[item.remove ? 1 : 0].push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.map(data => {
|
||||||
|
if ( ! data.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new RegExp(`^${data.join('|')}$`, 'gi');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-badges', {
|
||||||
|
default: [],
|
||||||
|
type: 'array_merge',
|
||||||
|
always_inherit: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Filtering >> Highlight Badges',
|
||||||
|
component: 'badge-highlighting',
|
||||||
|
colored: true,
|
||||||
|
data: () => this.badges.getSettingsBadges()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-badges--colors', {
|
||||||
|
requires: ['chat.filtering.highlight-basic-badges'],
|
||||||
|
process(ctx) {
|
||||||
|
const val = ctx.get('chat.filtering.highlight-basic-badges');
|
||||||
|
if ( ! val || ! val.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const colors = new Map;
|
||||||
|
|
||||||
|
for(const item of val) {
|
||||||
|
const c = item.c || null,
|
||||||
|
v = item.v;
|
||||||
|
|
||||||
|
colors.set(v, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-badges-blocked', {
|
||||||
|
default: [],
|
||||||
|
type: 'array_merge',
|
||||||
|
always_inherit: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Filtering >> Blocked Badges',
|
||||||
|
component: 'badge-highlighting',
|
||||||
|
removable: true,
|
||||||
|
data: () => this.badges.getSettingsBadges()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.highlight-basic-badges-blocked--list', {
|
||||||
|
requires: ['chat.filtering.highlight-basic-badges-blocked'],
|
||||||
|
process(ctx) {
|
||||||
|
const val = ctx.get('chat.filtering.highlight-basic-badges-blocked');
|
||||||
|
if ( ! val || ! val.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const out = [[], []];
|
||||||
|
for(const item of val)
|
||||||
|
if ( item.v )
|
||||||
|
out[item.remove ? 1 : 0].push(item.v);
|
||||||
|
|
||||||
|
if ( ! out[0].length && ! out[1].length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.settings.add('chat.filtering.highlight-basic-terms', {
|
this.settings.add('chat.filtering.highlight-basic-terms', {
|
||||||
default: [],
|
default: [],
|
||||||
|
@ -297,7 +469,6 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.settings.add('chat.filtering.highlight-basic-terms--color-regex', {
|
this.settings.add('chat.filtering.highlight-basic-terms--color-regex', {
|
||||||
requires: ['chat.filtering.highlight-basic-terms'],
|
requires: ['chat.filtering.highlight-basic-terms'],
|
||||||
process(ctx) {
|
process(ctx) {
|
||||||
|
@ -636,10 +807,10 @@ export default class Chat extends Module {
|
||||||
if ( id && typeof id === 'number' )
|
if ( id && typeof id === 'number' )
|
||||||
id = `${id}`;
|
id = `${id}`;
|
||||||
|
|
||||||
if ( this.user_ids[id] )
|
if ( id && this.user_ids[id] )
|
||||||
user = this.user_ids[id];
|
user = this.user_ids[id];
|
||||||
|
|
||||||
else if ( this.users[login] && ! no_login )
|
else if ( login && this.users[login] && ! no_login )
|
||||||
user = this.users[login];
|
user = this.users[login];
|
||||||
|
|
||||||
if ( user && user.destroyed )
|
if ( user && user.destroyed )
|
||||||
|
@ -696,10 +867,10 @@ export default class Chat extends Module {
|
||||||
if ( id && typeof id === 'number' )
|
if ( id && typeof id === 'number' )
|
||||||
id = `${id}`;
|
id = `${id}`;
|
||||||
|
|
||||||
if ( this.room_ids[id] )
|
if ( id && this.room_ids[id] )
|
||||||
room = this.room_ids[id];
|
room = this.room_ids[id];
|
||||||
|
|
||||||
else if ( this.rooms[login] && ! no_login )
|
else if ( login && this.rooms[login] && ! no_login )
|
||||||
room = this.rooms[login];
|
room = this.rooms[login];
|
||||||
|
|
||||||
if ( room && room.destroyed )
|
if ( room && room.destroyed )
|
||||||
|
@ -863,7 +1034,9 @@ export default class Chat extends Module {
|
||||||
if ( ! user )
|
if ( ! user )
|
||||||
user = msg.user = {};
|
user = msg.user = {};
|
||||||
|
|
||||||
user.color = user.color || user.chatColor || null;
|
const ext = msg.extension || {};
|
||||||
|
|
||||||
|
user.color = user.color || user.chatColor || ext.chatColor || null;
|
||||||
user.type = user.type || user.userType || null;
|
user.type = user.type || user.userType || null;
|
||||||
user.id = user.id || user.userID || null;
|
user.id = user.id || user.userID || null;
|
||||||
user.login = user.login || user.userLogin || null;
|
user.login = user.login || user.userLogin || null;
|
||||||
|
@ -888,7 +1061,13 @@ export default class Chat extends Module {
|
||||||
// Standardize Badges
|
// Standardize Badges
|
||||||
if ( ! msg.badges && user.displayBadges ) {
|
if ( ! msg.badges && user.displayBadges ) {
|
||||||
const b = msg.badges = {};
|
const b = msg.badges = {};
|
||||||
for(const item of msg.user.displayBadges)
|
for(const item of user.displayBadges)
|
||||||
|
b[item.setID] = item.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! msg.badges && ext.displayBadges ) {
|
||||||
|
const b = msg.badges = {};
|
||||||
|
for(const item of ext.displayBadges)
|
||||||
b[item.setID] = item.version;
|
b[item.setID] = item.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,10 +185,10 @@ export default class Room {
|
||||||
if ( id && typeof id === 'number' )
|
if ( id && typeof id === 'number' )
|
||||||
id = `${id}`;
|
id = `${id}`;
|
||||||
|
|
||||||
if ( this.user_ids[id] )
|
if ( id && this.user_ids[id] )
|
||||||
user = this.user_ids[id];
|
user = this.user_ids[id];
|
||||||
|
|
||||||
else if ( this.users[login] && ! no_login )
|
else if ( login && this.users[login] && ! no_login )
|
||||||
user = this.users[login];
|
user = this.users[login];
|
||||||
|
|
||||||
if ( user && user.destroyed )
|
if ( user && user.destroyed )
|
||||||
|
|
|
@ -268,6 +268,119 @@ export const Mentions = {
|
||||||
// Custom Highlight Terms
|
// Custom Highlight Terms
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
export const UserHighlights = {
|
||||||
|
type: 'user_highlight',
|
||||||
|
priority: 90,
|
||||||
|
|
||||||
|
process(tokens, msg, user) {
|
||||||
|
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const colors = this.context.get('chat.filtering.highlight-basic-users--color-regex');
|
||||||
|
if ( ! colors || ! colors.size )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const u = msg.user;
|
||||||
|
for(const [color, regex] of colors) {
|
||||||
|
if ( regex.test(u.login) || regex.test(u.displayName) ) {
|
||||||
|
msg.mentioned = true;
|
||||||
|
if ( color ) {
|
||||||
|
msg.mention_color = color;
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlockedUsers = {
|
||||||
|
type: 'user_block',
|
||||||
|
priority: 100,
|
||||||
|
|
||||||
|
process(tokens, msg, user) {
|
||||||
|
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const u = msg.user,
|
||||||
|
regexes = this.context.get('chat.filtering.highlight-basic-users-blocked--regex');
|
||||||
|
if ( ! regexes )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
if ( regexes[1] && (regexes[1].test(u.login) || regexes[1].test(u.displayName)) ) {
|
||||||
|
msg.deleted = true;
|
||||||
|
msg.ffz_removed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! msg.deleted && regexes[0] && (regexes[0].test(u.login) || regexes[0].test(u.displayName)) )
|
||||||
|
msg.deleted = true;
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BadgeHighlights = {
|
||||||
|
type: 'badge_highlight',
|
||||||
|
priority: 80,
|
||||||
|
|
||||||
|
process(tokens, msg, user) {
|
||||||
|
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const badges = msg.badges;
|
||||||
|
if ( ! badges )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const colors = this.context.get('chat.filtering.highlight-basic-badges--colors');
|
||||||
|
if ( ! colors || ! colors.size )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
for(const badge of Object.keys(badges)) {
|
||||||
|
if ( colors.has(badge) ) {
|
||||||
|
const color = colors.get(badge);
|
||||||
|
msg.mentioned = true;
|
||||||
|
if ( color ) {
|
||||||
|
msg.mention_color = color;
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlockedBadges = {
|
||||||
|
type: 'badge_block',
|
||||||
|
priority: 100,
|
||||||
|
process(tokens, msg, user) {
|
||||||
|
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const badges = msg.badges;
|
||||||
|
if ( ! badges )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const list = this.context.get('chat.filtering.highlight-basic-badges-blocked--list');
|
||||||
|
if ( ! list || (! list[0].length && ! list[1].length) )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
for(const badge of Object.keys(badges)) {
|
||||||
|
if ( list[1].includes(badge) ) {
|
||||||
|
msg.deleted = true;
|
||||||
|
msg.ffz_removed = true;
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! msg.deleted && list[0].includes(badge) )
|
||||||
|
msg.deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const CustomHighlights = {
|
export const CustomHighlights = {
|
||||||
type: 'highlight',
|
type: 'highlight',
|
||||||
priority: 100,
|
priority: 100,
|
||||||
|
@ -310,7 +423,7 @@ export const CustomHighlights = {
|
||||||
out.push({type: 'text', text: text.slice(idx, nix)});
|
out.push({type: 'text', text: text.slice(idx, nix)});
|
||||||
|
|
||||||
msg.mentioned = true;
|
msg.mentioned = true;
|
||||||
msg.mention_color = color;
|
msg.mention_color = color || msg.mention_color;
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
type: 'highlight',
|
type: 'highlight',
|
||||||
|
|
99
src/modules/main_menu/components/badge-highlighting.vue
Normal file
99
src/modules/main_menu/components/badge-highlighting.vue
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<template lang="html">
|
||||||
|
<section class="ffz--widget ffz--badge-highlighting">
|
||||||
|
<badge-term-editor
|
||||||
|
:term="default_term"
|
||||||
|
:badges="data"
|
||||||
|
:colored="item.colored"
|
||||||
|
:removable="item.removable"
|
||||||
|
:adding="true"
|
||||||
|
@save="new_term"
|
||||||
|
/>
|
||||||
|
<div v-if="! val.length || val.length === 1 && hasInheritance" class="tw-mg-t-05 tw-c-text-alt-2 tw-font-size-4 tw-align-center tw-c-text-alt-2 tw-pd-05">
|
||||||
|
{{ t('setting.terms.no-terms', 'no terms are defined in this profile') }}
|
||||||
|
</div>
|
||||||
|
<ul v-else class="ffz--term-list tw-mg-t-05">
|
||||||
|
<badge-term-editor
|
||||||
|
v-for="term in val"
|
||||||
|
v-if="term.t !== 'inherit'"
|
||||||
|
:key="term.id"
|
||||||
|
:term="term.v"
|
||||||
|
:badges="data"
|
||||||
|
:colored="item.colored"
|
||||||
|
:removable="item.removable"
|
||||||
|
@remove="remove(term)"
|
||||||
|
@save="save(term, $event)"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import SettingMixin from '../setting-mixin';
|
||||||
|
import {deep_copy} from 'utilities/object';
|
||||||
|
|
||||||
|
let last_id = 0;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [SettingMixin],
|
||||||
|
props: ['item', 'context'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
default_term: {
|
||||||
|
v: 'broadcaster',
|
||||||
|
c: '',
|
||||||
|
remove: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hasInheritance() {
|
||||||
|
for(const val of this.val)
|
||||||
|
if ( val.t === 'inherit' )
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
val() {
|
||||||
|
if ( ! this.has_value )
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return this.value.map(x => {
|
||||||
|
x.id = x.id || `${Date.now()}-${Math.random()}-${last_id++}`;
|
||||||
|
return x;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
new_term(term) {
|
||||||
|
if ( ! term.v )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const vals = Array.from(this.val);
|
||||||
|
vals.push({v: term});
|
||||||
|
this.set(deep_copy(vals));
|
||||||
|
},
|
||||||
|
|
||||||
|
remove(val) {
|
||||||
|
const vals = Array.from(this.val),
|
||||||
|
idx = vals.indexOf(val);
|
||||||
|
|
||||||
|
if ( idx !== -1 ) {
|
||||||
|
vals.splice(idx, 1);
|
||||||
|
if ( vals.length )
|
||||||
|
this.set(deep_copy(vals));
|
||||||
|
else
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
save(val, new_val) {
|
||||||
|
val.v = new_val;
|
||||||
|
this.set(deep_copy(this.val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
213
src/modules/main_menu/components/badge-term-editor.vue
Normal file
213
src/modules/main_menu/components/badge-term-editor.vue
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="ffz--term ffz--badge-term">
|
||||||
|
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row tw-full-width">
|
||||||
|
<div class="tw-mg-r-1">
|
||||||
|
<img
|
||||||
|
v-if="current"
|
||||||
|
:src="current.image"
|
||||||
|
class="ffz--badge-term-image"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex-grow-1 tw-mg-r-05">
|
||||||
|
<h4 v-if="! editing && ! current" class="ffz-monospace">
|
||||||
|
<pre>{{ t('setting.terms.invalid-badge', 'unknown/unloaded badge') }}</pre>
|
||||||
|
</h4>
|
||||||
|
<h4 v-if="! editing && current">
|
||||||
|
{{ current.name }}
|
||||||
|
</h4>
|
||||||
|
<select
|
||||||
|
v-if="editing"
|
||||||
|
v-model="edit_data.v"
|
||||||
|
class="tw-block tw-full-width tw-border-radius-medium tw-font-size-6 tw-full-width tw-select tw-pd-x-1 tw-pd-y-05 tw-mg-y-05">
|
||||||
|
<optgroup
|
||||||
|
v-for="section in badges"
|
||||||
|
:key="section.title"
|
||||||
|
:label="section.title"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="badge in section.badges"
|
||||||
|
:key="badge.id"
|
||||||
|
:value="badge.id"
|
||||||
|
>
|
||||||
|
{{ badge.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div v-if="colored" class="tw-flex-shrink-0 tw-mg-r-05">
|
||||||
|
<color-picker v-if="editing" v-model="edit_data.c" :nullable="true" :show-input="false" :open-up="true" />
|
||||||
|
<div v-else-if="term.c" class="ffz-color-preview">
|
||||||
|
<figure :style="`background-color: ${term.c}`">
|
||||||
|
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="removable" class="tw-flex-shrink-0 tw-mg-r-05 tw-tooltip-wrapper">
|
||||||
|
<button
|
||||||
|
v-if="editing"
|
||||||
|
:class="{active: edit_data.remove}"
|
||||||
|
class="tw-button ffz-directory-toggle-block"
|
||||||
|
@click="toggleRemove"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="edit_data.remove ? 'ffz-i-eye-off' : 'ffz-i-eye'"
|
||||||
|
class="tw-button__text"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else-if="term.remove"
|
||||||
|
class="ffz-i-eye-off tw-pd-x-1"
|
||||||
|
/>
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
<span v-if="display.remove">
|
||||||
|
{{ t('setting.terms.remove.on', 'Remove matching messages from chat.') }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('setting.terms.remove.off', 'Do not remove matching messages from chat.') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="adding" class="tw-flex-shrink-0">
|
||||||
|
<button class="tw-button" @click="save">
|
||||||
|
<span class="tw-button__text">
|
||||||
|
{{ t('setting.terms.add-term', 'Add') }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="editing" class="tw-flex-shrink-0">
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="save">
|
||||||
|
<span class="tw-button__text ffz-i-floppy" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.save', 'Save') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="cancel">
|
||||||
|
<span class="tw-button__text ffz-i-cancel" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.cancel', 'Cancel') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="deleting" class="tw-flex-shrink-0">
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="$emit('remove', term)">
|
||||||
|
<span class="tw-button__text ffz-i-trash" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.delete', 'Delete') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="deleting = false">
|
||||||
|
<span class="tw-button__text ffz-i-cancel" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.cancel', 'Cancel') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="tw-flex-shrink-0">
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="edit">
|
||||||
|
<span class="tw-button__text ffz-i-cog" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.edit', 'Edit') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="deleting = true">
|
||||||
|
<span class="tw-button__text ffz-i-trash" />
|
||||||
|
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||||
|
{{ t('setting.delete', 'Delete') }}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import {deep_copy} from 'utilities/object';
|
||||||
|
|
||||||
|
let id = 0;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
badges: Array,
|
||||||
|
term: Object,
|
||||||
|
colored: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
removable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
adding: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
if ( this.adding )
|
||||||
|
return {
|
||||||
|
editor_id: id++,
|
||||||
|
deleting: false,
|
||||||
|
editing: true,
|
||||||
|
edit_data: deep_copy(this.term)
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
editor_id: id++,
|
||||||
|
deleting: false,
|
||||||
|
editing: false,
|
||||||
|
edit_data: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
display() {
|
||||||
|
return this.editing ? this.edit_data : this.term;
|
||||||
|
},
|
||||||
|
|
||||||
|
current() {
|
||||||
|
if ( ! this.badges || ! this.display || ! this.display.v )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const v = this.display.v;
|
||||||
|
|
||||||
|
for(const section of this.badges) {
|
||||||
|
if ( ! section || ! section.badges )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for(const badge of section.badges)
|
||||||
|
if ( badge.id === v )
|
||||||
|
return badge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
edit() {
|
||||||
|
this.editing = true;
|
||||||
|
this.edit_data = Object.assign({remove: false}, deep_copy(this.term));
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleRemove() {
|
||||||
|
if ( this.editing )
|
||||||
|
this.edit_data.remove = ! this.edit_data.remove;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
if ( this.adding )
|
||||||
|
this.edit_data = deep_copy(this.term);
|
||||||
|
else {
|
||||||
|
this.editing = false;
|
||||||
|
this.edit_data = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.$emit('save', this.edit_data);
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -3,6 +3,7 @@
|
||||||
<term-editor
|
<term-editor
|
||||||
:term="default_term"
|
:term="default_term"
|
||||||
:colored="item.colored"
|
:colored="item.colored"
|
||||||
|
:words="item.words"
|
||||||
:removable="item.removable"
|
:removable="item.removable"
|
||||||
:adding="true"
|
:adding="true"
|
||||||
@save="new_term"
|
@save="new_term"
|
||||||
|
@ -17,6 +18,7 @@
|
||||||
:key="term.id"
|
:key="term.id"
|
||||||
:term="term.v"
|
:term="term.v"
|
||||||
:colored="item.colored"
|
:colored="item.colored"
|
||||||
|
:words="item.words"
|
||||||
:removable="item.removable"
|
:removable="item.removable"
|
||||||
@remove="remove(term)"
|
@remove="remove(term)"
|
||||||
@save="save(term, $event)"
|
@save="save(term, $event)"
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
>
|
>
|
||||||
<option value="text">{{ t('setting.terms.type.text', 'Text') }}</option>
|
<option value="text">{{ t('setting.terms.type.text', 'Text') }}</option>
|
||||||
<option value="glob">{{ t('setting.terms.type.glob', 'Glob') }}</option>
|
<option value="glob">{{ t('setting.terms.type.glob', 'Glob') }}</option>
|
||||||
<option value="regex">{{ t('setting.terms.type.regex-word', 'Regex (Word)') }}</option>
|
<option v-if="words" value="regex">{{ t('setting.terms.type.regex-word', 'Regex (Word)') }}</option>
|
||||||
<option value="raw">{{ t('setting.terms.type.regex', 'Regex') }}</option>
|
<option value="raw">{{ t('setting.terms.type.regex', 'Regex') }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -137,6 +137,10 @@ let id = 0;
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
term: Object,
|
term: Object,
|
||||||
|
words: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
colored: {
|
colored: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
|
|
@ -36,13 +36,19 @@ export default class ChatLine extends Module {
|
||||||
|
|
||||||
this.ChatLine = this.fine.define(
|
this.ChatLine = this.fine.define(
|
||||||
'chat-line',
|
'chat-line',
|
||||||
n => n.renderMessageBody && n.props && !has(n.props, 'hasModPermissions'),
|
n => n.renderMessageBody && n.props && ! n.onExtensionNameClick && !has(n.props, 'hasModPermissions'),
|
||||||
|
Twilight.CHAT_ROUTES
|
||||||
|
);
|
||||||
|
|
||||||
|
this.ExtensionLine = this.fine.define(
|
||||||
|
'extension-line',
|
||||||
|
n => n.renderMessageBody && n.onExtensionNameClick,
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ChatRoomLine = this.fine.define(
|
this.ChatRoomLine = this.fine.define(
|
||||||
'chat-room-line',
|
'chat-room-line',
|
||||||
n => n.renderMessageBody && n.props && has(n.props, 'hasModPermissions'),
|
n => n.renderMessageBody && n.props && ! n.onExtensionNameClick && has(n.props, 'hasModPermissions'),
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -78,7 +84,11 @@ export default class ChatLine extends Module {
|
||||||
this.chat.context.on('changed:chat.filtering.process-own', this.updateLines, this);
|
this.chat.context.on('changed:chat.filtering.process-own', this.updateLines, this);
|
||||||
this.chat.context.on('changed:chat.timestamp-format', this.updateLines, this);
|
this.chat.context.on('changed:chat.timestamp-format', this.updateLines, this);
|
||||||
this.chat.context.on('changed:chat.filtering.highlight-basic-terms--color-regex', this.updateLines, this);
|
this.chat.context.on('changed:chat.filtering.highlight-basic-terms--color-regex', this.updateLines, this);
|
||||||
|
this.chat.context.on('changed:chat.filtering.highlight-basic-users--color-regex', this.updateLines, this);
|
||||||
|
this.chat.context.on('changed:chat.filtering.highlight-basic-badges--colors', this.updateLines, this);
|
||||||
this.chat.context.on('changed:chat.filtering.highlight-basic-blocked--regex', this.updateLines, this);
|
this.chat.context.on('changed:chat.filtering.highlight-basic-blocked--regex', this.updateLines, this);
|
||||||
|
this.chat.context.on('changed:chat.filtering.highlight-basic-users-blocked--regex', this.updateLines, this);
|
||||||
|
this.chat.context.on('changed:chat.filtering.highlight-basic-badges-blocked--list', this.updateLines, this);
|
||||||
|
|
||||||
const t = this,
|
const t = this,
|
||||||
React = await this.web_munch.findModule('react');
|
React = await this.web_munch.findModule('react');
|
||||||
|
@ -692,6 +702,8 @@ export default class ChatLine extends Module {
|
||||||
}, out);
|
}, out);
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
t.log.info(err);
|
||||||
|
|
||||||
t.log.capture(err, {
|
t.log.capture(err, {
|
||||||
extra: {
|
extra: {
|
||||||
props: this.props
|
props: this.props
|
||||||
|
@ -704,6 +716,85 @@ export default class ChatLine extends Module {
|
||||||
// Do this after a short delay to hopefully reduce the chance of React
|
// Do this after a short delay to hopefully reduce the chance of React
|
||||||
// freaking out on us.
|
// freaking out on us.
|
||||||
setTimeout(() => this.ChatLine.forceUpdate());
|
setTimeout(() => this.ChatLine.forceUpdate());
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ExtensionLine.ready(cls => {
|
||||||
|
const old_render = cls.prototype.render;
|
||||||
|
|
||||||
|
cls.prototype.render = function() { try {
|
||||||
|
if ( ! this.props.installedExtensions )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const msg = t.chat.standardizeMessage(this.props.message),
|
||||||
|
ext = msg && msg.extension;
|
||||||
|
if( ! ext )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if ( ! this.props.installedExtensions.some(val => {
|
||||||
|
const e = val.extension;
|
||||||
|
return e && e.clientID === ext.clientID && e.version === ext.version;
|
||||||
|
}) )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const color = t.parent.colors.process(ext.chatColor);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u = t.site.getUser(),
|
||||||
|
r = {id: this.props.channelID, login: room},
|
||||||
|
|
||||||
|
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u, r),
|
||||||
|
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg),
|
||||||
|
bg_css = msg.mentioned && msg.mention_color ? t.parent.inverse_colors.process(msg.mention_color) : null;
|
||||||
|
|
||||||
|
if ( ! tokens.length )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return e('div', {
|
||||||
|
className: `chat-line__message${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`,
|
||||||
|
style: {backgroundColor: bg_css},
|
||||||
|
'data-room-id': r.id,
|
||||||
|
'data-room': r.login,
|
||||||
|
'data-extension': ext.clientID
|
||||||
|
}, [
|
||||||
|
this.props.showTimestamps && e('span', {
|
||||||
|
className: 'chat-line__timestamp'
|
||||||
|
}, t.chat.formatTime(msg.timestamp)),
|
||||||
|
e('span', {
|
||||||
|
className: 'chat-line__message--badges'
|
||||||
|
}, t.chat.badges.render(msg, e)),
|
||||||
|
e('button', {
|
||||||
|
className: 'chat-line__username notranslate',
|
||||||
|
style: { color },
|
||||||
|
onClick: this.onExtensionNameClick
|
||||||
|
}, e('span', {
|
||||||
|
className: 'chat-author__display-name'
|
||||||
|
}, ext.displayName)),
|
||||||
|
e('span', null, ': '),
|
||||||
|
e('span', {
|
||||||
|
className: 'message'
|
||||||
|
}, t.chat.renderTokens(tokens, e)),
|
||||||
|
rich_content && e(FFZRichContent, rich_content)
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
t.log.info(err);
|
||||||
|
t.log.capture(err, {
|
||||||
|
extra: {
|
||||||
|
props: this.props
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return old_render.call(this);
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Do this after a short delay to hopefully reduce the chance of React
|
||||||
|
// freaking out on us.
|
||||||
|
setTimeout(() => this.ExtensionLine.forceUpdate());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +813,14 @@ export default class ChatLine extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(const inst of this.ExtensionLine.instances) {
|
||||||
|
const msg = inst.props.message;
|
||||||
|
if ( msg ) {
|
||||||
|
msg.ffz_tokens = null;
|
||||||
|
msg.mentioned = msg.mention_color = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(const inst of this.ChatRoomLine.instances) {
|
for(const inst of this.ChatRoomLine.instances) {
|
||||||
const msg = inst.props.message;
|
const msg = inst.props.message;
|
||||||
if ( msg ) {
|
if ( msg ) {
|
||||||
|
@ -737,6 +836,7 @@ export default class ChatLine extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ChatLine.forceUpdate();
|
this.ChatLine.forceUpdate();
|
||||||
|
this.ExtensionLine.forceUpdate();
|
||||||
this.ChatRoomLine.forceUpdate();
|
this.ChatRoomLine.forceUpdate();
|
||||||
this.WhisperLine.forceUpdate();
|
this.WhisperLine.forceUpdate();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue