mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-15 18:40:54 +00:00
4.17.13
* Changed: Replace a few unnecessary menu components that only display markdown with a generic component. * Changed: Initial work for letting users override display names and user colors in chat. * Changed: The FFZ menu button no longer depends on add-ons being loaded to load itself. * Fixed: Rendering of highlighted messages in Chat on Videos. * Fixed: Featured channels menu not working when a bad channel is in the list.
This commit is contained in:
parent
c91822cdc9
commit
3cbe4ee2fc
19 changed files with 284 additions and 228 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.17.12",
|
"version": "4.17.13",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {timeout, has, glob_to_regex, escape_regex, split_chars} from 'utilities/
|
||||||
import Badges from './badges';
|
import Badges from './badges';
|
||||||
import Emotes from './emotes';
|
import Emotes from './emotes';
|
||||||
import Emoji from './emoji';
|
import Emoji from './emoji';
|
||||||
|
import Overrides from './overrides';
|
||||||
|
|
||||||
import Room from './room';
|
import Room from './room';
|
||||||
import User from './user';
|
import User from './user';
|
||||||
|
@ -40,6 +41,7 @@ export default class Chat extends Module {
|
||||||
this.inject(Emotes);
|
this.inject(Emotes);
|
||||||
this.inject(Emoji);
|
this.inject(Emoji);
|
||||||
this.inject(Actions);
|
this.inject(Actions);
|
||||||
|
this.inject(Overrides);
|
||||||
|
|
||||||
this._link_info = {};
|
this._link_info = {};
|
||||||
|
|
||||||
|
|
133
src/modules/chat/overrides.js
Normal file
133
src/modules/chat/overrides.js
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Name and Color Overrides
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
import Module from 'utilities/module';
|
||||||
|
|
||||||
|
|
||||||
|
export default class Overrides extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.inject('settings');
|
||||||
|
this.color_cache = null;
|
||||||
|
this.name_cache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnable() {
|
||||||
|
this.settings.provider.on('changed', this.onProviderChange, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onProviderChange(key) {
|
||||||
|
if ( key === 'overrides.colors' )
|
||||||
|
this.loadColors();
|
||||||
|
else if ( key === 'overrides.names' )
|
||||||
|
this.loadNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
get colors() {
|
||||||
|
if ( ! this.color_cache )
|
||||||
|
this.loadColors();
|
||||||
|
|
||||||
|
return this.color_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
get names() {
|
||||||
|
if ( ! this.name_cache )
|
||||||
|
this.loadNames();
|
||||||
|
|
||||||
|
return this.name_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadColors() {
|
||||||
|
let old_keys,
|
||||||
|
loaded = true;
|
||||||
|
if ( ! this.color_cache ) {
|
||||||
|
loaded = false;
|
||||||
|
this.color_cache = {};
|
||||||
|
old_keys = new Set;
|
||||||
|
} else
|
||||||
|
old_keys = new Set(Object.keys(this.color_cache));
|
||||||
|
|
||||||
|
for(const [key, val] of Object.entries(this.settings.provider.get('overrides.colors', {}))) {
|
||||||
|
old_keys.delete(key);
|
||||||
|
if ( this.color_cache[key] !== val ) {
|
||||||
|
this.color_cache[key] = val;
|
||||||
|
if ( loaded )
|
||||||
|
this.emit(':changed', key, 'color', val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const key of old_keys) {
|
||||||
|
this.color_cache[key] = undefined;
|
||||||
|
if ( loaded )
|
||||||
|
this.emit(':changed', key, 'color', undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadNames() {
|
||||||
|
let old_keys,
|
||||||
|
loaded = true;
|
||||||
|
if ( ! this.name_cache ) {
|
||||||
|
loaded = false;
|
||||||
|
this.name_cache = {};
|
||||||
|
old_keys = new Set;
|
||||||
|
} else
|
||||||
|
old_keys = new Set(Object.keys(this.name_cache));
|
||||||
|
|
||||||
|
for(const [key, val] of Object.entries(this.settings.provider.get('overrides.names', {}))) {
|
||||||
|
old_keys.delete(key);
|
||||||
|
if ( this.name_cache[key] !== val ) {
|
||||||
|
this.name_cache[key] = val;
|
||||||
|
if ( loaded )
|
||||||
|
this.emit(':changed', key, 'name', val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const key of old_keys) {
|
||||||
|
this.name_cache[key] = undefined;
|
||||||
|
if ( loaded )
|
||||||
|
this.emit(':changed', key, 'name', undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getColor(id) {
|
||||||
|
if ( this.colors[id] != null )
|
||||||
|
return this.colors[id];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(id) {
|
||||||
|
if ( this.names[id] != null )
|
||||||
|
return this.names[id];
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(id, color) {
|
||||||
|
if ( this.colors[id] !== color ) {
|
||||||
|
this.colors[id] = color;
|
||||||
|
this.settings.provider.set('overrides.colors', this.colors);
|
||||||
|
this.emit(':changed', id, 'color', color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(id, name) {
|
||||||
|
if ( this.names[id] !== name ) {
|
||||||
|
this.names[id] = name;
|
||||||
|
this.settings.provider.set('overrides.names', this.names);
|
||||||
|
this.emit(':changed', id, 'name', name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteColor(id) {
|
||||||
|
this.setColor(id, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteName(id) {
|
||||||
|
this.setName(id, undefined);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
<template lang="html">
|
|
||||||
<div class="ffz--home tw-border-t tw-pd-y-1">
|
|
||||||
<markdown :source="t('home.faq', md)" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import FAQ_MD from '../faq.md';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ['item', 'context'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
md: FAQ_MD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<template lang="html">
|
|
||||||
<div class="ffz--home tw-border-t tw-pd-y-1">
|
|
||||||
<markdown :source="t('home.feedback', md)" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import FEEDBACK_MD from '../feedback.md';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ['item', 'context'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
md: FEEDBACK_MD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<template lang="html">
|
|
||||||
<div class="ffz--home tw-border-t tw-pd-y-1">
|
|
||||||
<markdown :source="md" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import LEGAL_MD from '../legal.md';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: ['item', 'context'],
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
md: LEGAL_MD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
36
src/modules/main_menu/components/md-page.vue
Normal file
36
src/modules/main_menu/components/md-page.vue
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="ffz--home tw-border-t tw-pd-y-1">
|
||||||
|
<div v-if="loading" class="tw-align-center tw-pd-1">
|
||||||
|
<h1 class="tw-mg-5 ffz-i-zreknarf loading" />
|
||||||
|
</div>
|
||||||
|
<markdown v-else :source="t(`home.${key}`, md)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['item', 'context'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
const key = this.item.key;
|
||||||
|
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
loading: true,
|
||||||
|
md: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.load();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async load() {
|
||||||
|
this.md = (await import(/* webpackChunkName: 'menu-md' */ `../${this.key}.md`)).default;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -64,12 +64,21 @@ export default class MainMenu extends Module {
|
||||||
|
|
||||||
this.settings.addUI('faq', {
|
this.settings.addUI('faq', {
|
||||||
path: 'Home > FAQ @{"profile_warning": false}',
|
path: 'Home > FAQ @{"profile_warning": false}',
|
||||||
component: 'faq-page'
|
component: 'md-page',
|
||||||
|
key: 'faq'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*this.settings.addUI('privacy', {
|
||||||
|
path: 'Home > Privacy @{"profile_warning": false}',
|
||||||
|
component: 'md-page',
|
||||||
|
key: 'privacy',
|
||||||
|
force_seen: true
|
||||||
|
});*/
|
||||||
|
|
||||||
this.settings.addUI('feedback', {
|
this.settings.addUI('feedback', {
|
||||||
path: 'Home > Feedback @{"profile_warning": false}',
|
path: 'Home > Feedback @{"profile_warning": false}',
|
||||||
component: 'feedback-page'
|
component: 'md-page',
|
||||||
|
key: 'feedback'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.addUI('feedback.log', {
|
this.settings.addUI('feedback.log', {
|
||||||
|
@ -96,7 +105,8 @@ export default class MainMenu extends Module {
|
||||||
|
|
||||||
this.settings.addUI('legal', {
|
this.settings.addUI('legal', {
|
||||||
path: 'Home > Legal @{"sort": 1000}',
|
path: 'Home > Legal @{"sort": 1000}',
|
||||||
component: 'legal-page',
|
component: 'md-page',
|
||||||
|
key: 'legal',
|
||||||
force_seen: true
|
force_seen: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ export default class ChatHook extends Module {
|
||||||
|
|
||||||
this.GiftBanner = this.fine.define(
|
this.GiftBanner = this.fine.define(
|
||||||
'gift-banner',
|
'gift-banner',
|
||||||
n => n.getBannerText && n.handleCountdownEnd && n.getRemainingTime,
|
n => n.getBannerText && n.onGiftMoreClick,
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ export default class ChatLine extends Module {
|
||||||
this.inject(RichContent);
|
this.inject(RichContent);
|
||||||
|
|
||||||
this.inject('chat.actions');
|
this.inject('chat.actions');
|
||||||
|
this.inject('chat.overrides');
|
||||||
|
|
||||||
this.ChatLine = this.fine.define(
|
this.ChatLine = this.fine.define(
|
||||||
'chat-line',
|
'chat-line',
|
||||||
|
@ -46,18 +47,6 @@ export default class ChatLine extends Module {
|
||||||
Twilight.CHAT_ROUTES
|
Twilight.CHAT_ROUTES
|
||||||
);
|
);
|
||||||
|
|
||||||
/*this.ChatRoomLine = this.fine.define(
|
|
||||||
'chat-room-line',
|
|
||||||
n => n.renderMessageBody && n.props && ! n.onExtensionNameClick && has(n.props, 'hasModPermissions'),
|
|
||||||
Twilight.CHAT_ROUTES
|
|
||||||
);*/
|
|
||||||
|
|
||||||
/*this.ChatRoomContainer = this.fine.define(
|
|
||||||
'chat-room-container',
|
|
||||||
n => n.renderPlaceholders && n.sendRoomMessage && n.props && n.props.channel,
|
|
||||||
Twilight.CHAT_ROUTES
|
|
||||||
);*/
|
|
||||||
|
|
||||||
this.WhisperLine = this.fine.define(
|
this.WhisperLine = this.fine.define(
|
||||||
'whisper-line',
|
'whisper-line',
|
||||||
n => n.props && n.props.message && n.props.reportOutgoingWhisperRendered
|
n => n.props && n.props.message && n.props.reportOutgoingWhisperRendered
|
||||||
|
@ -65,6 +54,8 @@ export default class ChatLine extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
|
this.on('chat.overrides:changed', id => this.updateLinesByUser(id), this);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.emoji.style', this.updateLines, this);
|
this.chat.context.on('changed:chat.emoji.style', this.updateLines, this);
|
||||||
this.chat.context.on('changed:chat.bits.stack', this.updateLines, this);
|
this.chat.context.on('changed:chat.bits.stack', this.updateLines, this);
|
||||||
this.chat.context.on('changed:chat.badges.style', this.updateLines, this);
|
this.chat.context.on('changed:chat.badges.style', this.updateLines, this);
|
||||||
|
@ -99,121 +90,6 @@ export default class ChatLine extends Module {
|
||||||
FFZRichContent = this.rich_content && this.rich_content.RichContent;
|
FFZRichContent = this.rich_content && this.rich_content.RichContent;
|
||||||
|
|
||||||
|
|
||||||
/*this.ChatRoomLine.ready(cls => {
|
|
||||||
const old_render = cls.prototype.render;
|
|
||||||
|
|
||||||
cls.prototype.render = function() { try {
|
|
||||||
this._ffz_no_scan = true;
|
|
||||||
|
|
||||||
const msg = t.chat.standardizeMessage(this.props.message),
|
|
||||||
is_action = msg.is_action,
|
|
||||||
|
|
||||||
user = msg.user,
|
|
||||||
color = t.parent.colors.process(user.color),
|
|
||||||
show_deleted = t.chat.context.get('chat.filtering.show-deleted');
|
|
||||||
|
|
||||||
let show, show_class;
|
|
||||||
|
|
||||||
if ( show_deleted ) {
|
|
||||||
show = true;
|
|
||||||
show_class = msg.deleted;
|
|
||||||
} else {
|
|
||||||
show = this.state && this.state.shouldShowDeletedBody || ! msg.deleted;
|
|
||||||
show_class = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const u = t.site.getUser(),
|
|
||||||
r = {id: null, login: null};
|
|
||||||
|
|
||||||
if ( u ) {
|
|
||||||
u.moderator = this.props.hasModPermissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the parent element.
|
|
||||||
const parent = this._ffz_parent = this._ffz_parent || t.fine.searchParent(this,
|
|
||||||
n => (n.props && n.props.banStatusData && n.props.channelID) ||
|
|
||||||
(n.renderPlaceholders && n.sendRoomMessage && n.props && n.props.channel), 50);
|
|
||||||
|
|
||||||
if ( parent != null ) {
|
|
||||||
r.id = parent.props.channelID;
|
|
||||||
r.login = parent.props.channelLogin;
|
|
||||||
}
|
|
||||||
|
|
||||||
const 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 ( ! this.ffz_user_click_handler )
|
|
||||||
this.ffz_user_click_handler = this.props.onUsernameClick;
|
|
||||||
|
|
||||||
const cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}`,
|
|
||||||
out = (tokens.length || ! msg.ffz_type) ? [
|
|
||||||
this.props.showTimestamps && e('span', {
|
|
||||||
className: 'chat-line__timestamp'
|
|
||||||
}, t.chat.formatTime(msg.timestamp)),
|
|
||||||
this.renderModerationIcons(),
|
|
||||||
//t.actions.renderInline(msg, this.props.showModerationIcons, u, r, e),
|
|
||||||
e('span', {
|
|
||||||
className: 'chat-line__message--badges'
|
|
||||||
}, t.chat.badges.render(msg, e)),
|
|
||||||
e('span', {
|
|
||||||
className: 'chat-line__username notranslate',
|
|
||||||
role: 'button',
|
|
||||||
style: { color },
|
|
||||||
onClick: this.ffz_user_click_handler
|
|
||||||
}, [
|
|
||||||
e('span', {
|
|
||||||
className: 'chat-author__display-name'
|
|
||||||
}, user.displayName),
|
|
||||||
user.isIntl && e('span', {
|
|
||||||
className: 'chat-author__intl-login'
|
|
||||||
}, ` (${user.login})`)
|
|
||||||
]),
|
|
||||||
e('span', null, is_action ? ' ' : ': '),
|
|
||||||
show ?
|
|
||||||
e('span', {
|
|
||||||
className: 'message',
|
|
||||||
style: is_action ? { color } : null
|
|
||||||
}, t.chat.renderTokens(tokens, e))
|
|
||||||
:
|
|
||||||
e('span', {
|
|
||||||
className: 'chat-line__message--deleted'
|
|
||||||
}, e('a', {
|
|
||||||
href: '',
|
|
||||||
onClick: this.showDeleted
|
|
||||||
}, t.i18n.t('chat.message-deleted', '<message deleted>'))),
|
|
||||||
|
|
||||||
show && rich_content && e(FFZRichContent, rich_content)
|
|
||||||
] : null;
|
|
||||||
|
|
||||||
if ( ! out )
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return e('div', {
|
|
||||||
className: `${cls}${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`,
|
|
||||||
style: {backgroundColor: bg_css},
|
|
||||||
'data-room-id': r.id,
|
|
||||||
'data-room': r.login,
|
|
||||||
'data-user-id': user.id,
|
|
||||||
'data-user': user.login && user.login.toLowerCase()
|
|
||||||
}, out);
|
|
||||||
|
|
||||||
} catch(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.ChatRoomLine.forceUpdate());
|
|
||||||
});*/
|
|
||||||
|
|
||||||
|
|
||||||
this.WhisperLine.ready(cls => {
|
this.WhisperLine.ready(cls => {
|
||||||
const old_render = cls.prototype.render;
|
const old_render = cls.prototype.render;
|
||||||
|
|
||||||
|
@ -227,19 +103,22 @@ export default class ChatLine extends Module {
|
||||||
|
|
||||||
is_action = msg.is_action,
|
is_action = msg.is_action,
|
||||||
user = msg.user,
|
user = msg.user,
|
||||||
color = t.parent.colors.process(user.color),
|
raw_color = t.overrides.getColor(user.id) || user.color,
|
||||||
|
color = t.parent.colors.process(raw_color),
|
||||||
|
|
||||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, null, null),
|
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, null, null),
|
||||||
contents = t.chat.renderTokens(tokens, e);
|
contents = t.chat.renderTokens(tokens, e),
|
||||||
|
|
||||||
|
override_name = t.overrides.getName(user.id);
|
||||||
|
|
||||||
return e('div', {className: 'thread-message__message'},
|
return e('div', {className: 'thread-message__message'},
|
||||||
e('div', {className: 'tw-pd-x-1 tw-pd-y-05'}, [
|
e('div', {className: 'tw-pd-x-1 tw-pd-y-05'}, [
|
||||||
e('span', {
|
e('span', {
|
||||||
className: 'thread-message__message--user-name notranslate',
|
className: `thread-message__message--user-name notranslate${override_name ? ' ffz--name-override' : ''}`,
|
||||||
style: {
|
style: {
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
}, user.displayName),
|
}, override_name || user.displayName),
|
||||||
e('span', null, is_action ? ' ' : ': '),
|
e('span', null, is_action ? ' ' : ': '),
|
||||||
e('span', {
|
e('span', {
|
||||||
className: 'message',
|
className: 'message',
|
||||||
|
@ -290,7 +169,9 @@ export default class ChatLine extends Module {
|
||||||
is_action = msg.messageType === types.Action,
|
is_action = msg.messageType === types.Action,
|
||||||
|
|
||||||
user = msg.user,
|
user = msg.user,
|
||||||
color = t.parent.colors.process(user.color);
|
raw_color = t.overrides.getColor(user.id) || user.color,
|
||||||
|
|
||||||
|
color = t.parent.colors.process(raw_color);
|
||||||
|
|
||||||
let mod_mode = this.props.deletedMessageDisplay;
|
let mod_mode = this.props.deletedMessageDisplay;
|
||||||
let show, show_class, mod_action = null;
|
let show, show_class, mod_action = null;
|
||||||
|
@ -422,6 +303,19 @@ other {# messages were deleted by a moderator.}
|
||||||
this.ffz_user_click_handler = this.openViewerCard || this.usernameClickHandler; //event => event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event);
|
this.ffz_user_click_handler = this.openViewerCard || this.usernameClickHandler; //event => event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const user_block = [
|
||||||
|
e('span', {
|
||||||
|
className: 'chat-author__display-name'
|
||||||
|
}, user.displayName),
|
||||||
|
user.isIntl && e('span', {
|
||||||
|
className: 'chat-author__intl-login'
|
||||||
|
}, ` (${user.login})`)
|
||||||
|
];
|
||||||
|
|
||||||
|
const override_name = t.overrides.getName(user.id);
|
||||||
|
|
||||||
|
|
||||||
let cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}`,
|
let cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}`,
|
||||||
out = (tokens.length || ! msg.ffz_type) ? [
|
out = (tokens.length || ! msg.ffz_type) ? [
|
||||||
this.props.showTimestamps && e('span', {
|
this.props.showTimestamps && e('span', {
|
||||||
|
@ -432,19 +326,19 @@ other {# messages were deleted by a moderator.}
|
||||||
className: 'chat-line__message--badges'
|
className: 'chat-line__message--badges'
|
||||||
}, t.chat.badges.render(msg, e)),
|
}, t.chat.badges.render(msg, e)),
|
||||||
e('span', {
|
e('span', {
|
||||||
className: 'chat-line__username notranslate',
|
className: `chat-line__username notranslate${override_name ? ' ffz--name-override tw-relative tw-tooltip-wrapper' : ''}`,
|
||||||
role: 'button',
|
role: 'button',
|
||||||
style: { color },
|
style: { color },
|
||||||
onClick: this.ffz_user_click_handler,
|
onClick: this.ffz_user_click_handler,
|
||||||
onContextMenu: t.actions.handleUserContext
|
onContextMenu: t.actions.handleUserContext
|
||||||
}, [
|
}, override_name ? [
|
||||||
e('span', {
|
e('span', {
|
||||||
className: 'chat-author__display-name'
|
className: 'chat-author__display_name'
|
||||||
}, user.displayName),
|
}, override_name),
|
||||||
user.isIntl && e('span', {
|
e('div', {
|
||||||
className: 'chat-author__intl-login'
|
className: 'tw-tooltip tw-tooltip--down tw-tooltip--align-center'
|
||||||
}, ` (${user.login})`)
|
}, user_block)
|
||||||
]),
|
] : user_block),
|
||||||
e('span', null, is_action ? ' ' : ': '),
|
e('span', null, is_action ? ' ' : ': '),
|
||||||
show ?
|
show ?
|
||||||
e('span', {
|
e('span', {
|
||||||
|
@ -859,6 +753,23 @@ other {# messages were deleted by a moderator.}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateLinesByUser(id, login) {
|
||||||
|
for(const inst of this.ChatLine.instances) {
|
||||||
|
const msg = inst.props.message,
|
||||||
|
user = msg?.user;
|
||||||
|
if ( user && (id && id == user.id) || (login && login == user.login) )
|
||||||
|
inst.forceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const inst of this.WhisperLine.instances) {
|
||||||
|
const msg = inst.props.message?._ffz_message,
|
||||||
|
user = msg?.user;
|
||||||
|
if ( user && (id && id == user.id) || (login && login == user.login) )
|
||||||
|
inst.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
maybeUpdateLines() {
|
maybeUpdateLines() {
|
||||||
if ( this.chat.context.get('chat.rich.all-links') )
|
if ( this.chat.context.get('chat.rich.all-links') )
|
||||||
this.updateLines();
|
this.updateLines();
|
||||||
|
@ -881,14 +792,6 @@ other {# messages were deleted by a moderator.}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*for(const inst of this.ChatRoomLine.instances) {
|
|
||||||
const msg = inst.props.message;
|
|
||||||
if ( msg ) {
|
|
||||||
msg.ffz_tokens = null;
|
|
||||||
msg.mentioned = msg.mention_color = null;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
for(const inst of this.WhisperLine.instances) {
|
for(const inst of this.WhisperLine.instances) {
|
||||||
const msg = inst.props.message;
|
const msg = inst.props.message;
|
||||||
if ( msg && msg._ffz_message )
|
if ( msg && msg._ffz_message )
|
||||||
|
@ -897,7 +800,6 @@ other {# messages were deleted by a moderator.}
|
||||||
|
|
||||||
this.ChatLine.forceUpdate();
|
this.ChatLine.forceUpdate();
|
||||||
this.ExtensionLine.forceUpdate();
|
this.ExtensionLine.forceUpdate();
|
||||||
//this.ChatRoomLine.forceUpdate();
|
|
||||||
this.WhisperLine.forceUpdate();
|
this.WhisperLine.forceUpdate();
|
||||||
|
|
||||||
this.emit('chat:updated-lines');
|
this.emit('chat:updated-lines');
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video-chat__message-list-wrapper li:nth-child(2n+0) .ffz--points-highlight,
|
||||||
.ffz--points-highlight:nth-child(2n+0) {
|
.ffz--points-highlight:nth-child(2n+0) {
|
||||||
background-color: var(--ffz-channel-color-30);
|
background-color: var(--ffz-channel-color-30);
|
||||||
}
|
}
|
|
@ -109,7 +109,7 @@ export default class FeaturedFollow extends Module {
|
||||||
follows = {};
|
follows = {};
|
||||||
|
|
||||||
for (const user of ap_data.data.users) {
|
for (const user of ap_data.data.users) {
|
||||||
if ( ! user.id )
|
if ( ! user || ! user.id )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
follows[user.id] = {
|
follows[user.id] = {
|
||||||
|
|
|
@ -18,7 +18,7 @@ export default class MenuButton extends SiteModule {
|
||||||
this.inject('i18n');
|
this.inject('i18n');
|
||||||
this.inject('settings');
|
this.inject('settings');
|
||||||
this.inject('site.fine');
|
this.inject('site.fine');
|
||||||
this.inject('addons');
|
//this.inject('addons');
|
||||||
|
|
||||||
this.should_enable = true;
|
this.should_enable = true;
|
||||||
this._pill_content = null;
|
this._pill_content = null;
|
||||||
|
@ -158,13 +158,15 @@ export default class MenuButton extends SiteModule {
|
||||||
if ( this.has_strings )
|
if ( this.has_strings )
|
||||||
return this.i18n.formatNumber(this.i18n.new_strings + this.i18n.changed_strings);
|
return this.i18n.formatNumber(this.i18n.new_strings + this.i18n.changed_strings);
|
||||||
|
|
||||||
if ( DEBUG && this.addons.has_dev )
|
const addons = this.resolve('addons');
|
||||||
|
|
||||||
|
if ( DEBUG && addons.has_dev )
|
||||||
return this.i18n.t('site.menu_button.dev', 'dev');
|
return this.i18n.t('site.menu_button.dev', 'dev');
|
||||||
|
|
||||||
if ( DEBUG && ! this.addons.has_dev )
|
if ( DEBUG && ! addons.has_dev )
|
||||||
return this.i18n.t('site.menu_button.main-dev', 'm-dev');
|
return this.i18n.t('site.menu_button.main-dev', 'm-dev');
|
||||||
|
|
||||||
if ( ! DEBUG && this.addons.has_dev )
|
if ( ! DEBUG && addons.has_dev )
|
||||||
return this.i18n.t('site.menu_button.addon-dev', 'a-dev');
|
return this.i18n.t('site.menu_button.addon-dev', 'a-dev');
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -263,7 +265,8 @@ export default class MenuButton extends SiteModule {
|
||||||
if ( el )
|
if ( el )
|
||||||
el.remove();
|
el.remove();
|
||||||
|
|
||||||
const pill = this.formatPill(),
|
const addons = this.resolve('addons'),
|
||||||
|
pill = this.formatPill(),
|
||||||
extra_pill = this.formatExtraPill();
|
extra_pill = this.formatExtraPill();
|
||||||
|
|
||||||
el = (<div
|
el = (<div
|
||||||
|
@ -315,7 +318,7 @@ export default class MenuButton extends SiteModule {
|
||||||
{DEBUG && (<div class="tw-mg-t-1">
|
{DEBUG && (<div class="tw-mg-t-1">
|
||||||
{this.i18n.t('site.menu_button.main-dev-desc', 'You are running a developer build of FrankerFaceZ.')}
|
{this.i18n.t('site.menu_button.main-dev-desc', 'You are running a developer build of FrankerFaceZ.')}
|
||||||
</div>)}
|
</div>)}
|
||||||
{this.addons.has_dev && (<div class="tw-mg-t-1">
|
{addons.has_dev && (<div class="tw-mg-t-1">
|
||||||
{this.i18n.t('site.menu_button.addon-dev-desc', 'You have loaded add-on data from a local development server.')}
|
{this.i18n.t('site.menu_button.addon-dev-desc', 'You have loaded add-on data from a local development server.')}
|
||||||
</div>)}
|
</div>)}
|
||||||
</div>)}
|
</div>)}
|
||||||
|
|
|
@ -283,13 +283,13 @@ export default class VideoChatHook extends Module {
|
||||||
|
|
||||||
return (<div
|
return (<div
|
||||||
data-test-selector="message-layout"
|
data-test-selector="message-layout"
|
||||||
class={`tw-align-items-start tw-flex tw-flex-nowrap tw-full-width tw-pd-l-05 tw-pd-y-05 vod-message${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`}
|
class={`tw-align-items-start tw-flex tw-flex-nowrap tw-full-width tw-pd-l-05 tw-pd-y-05 vod-message${msg.highlight ? ' ffz-notice-line ffz--points-line ffz--points-highlight ffz-custom-color' : ''}${msg.mentioned ? ' ffz-mentioned' : ''}${bg_css ? ' ffz-custom-color' : ''}`}
|
||||||
style={{backgroundColor: bg_css}}
|
style={{backgroundColor: bg_css}}
|
||||||
>
|
>
|
||||||
{this.props.hideTimestamp || (<div data-test-selector="message-timestamp" class="tw-align-right tw-flex tw-flex-shrink-0 vod-message__header">
|
{this.props.hideTimestamp || (<div data-test-selector="message-timestamp" class="tw-align-right tw-flex tw-flex-shrink-0 vod-message__header">
|
||||||
<div class="tw-mg-r-05">
|
<div class="tw-mg-r-05">
|
||||||
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
<div class="tw-inline-flex tw-relative tw-tooltip-wrapper">
|
||||||
<button class="tw-block tw-full-width tw-interactable tw-interactable--inverted" onClick={this.onTimestampClickHandler}>
|
<button class="tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive" onClick={this.onTimestampClickHandler}>
|
||||||
<div class="tw-pd-x-05">
|
<div class="tw-pd-x-05">
|
||||||
<p class="tw-font-size-7">{print_duration(context.comment.contentOffset)}</p>
|
<p class="tw-font-size-7">{print_duration(context.comment.contentOffset)}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -377,7 +377,8 @@ export default class VideoChatHook extends Module {
|
||||||
messageParts: comment.message.tokens,
|
messageParts: comment.message.tokens,
|
||||||
is_action: comment.message.isAction,
|
is_action: comment.message.isAction,
|
||||||
more_replies: comment.moreReplies,
|
more_replies: comment.moreReplies,
|
||||||
timestamp: comment.createdAt
|
timestamp: comment.createdAt,
|
||||||
|
highlight: comment.message.userNoticeParams?.['msg-id'] === 'highlighted-message'
|
||||||
};
|
};
|
||||||
|
|
||||||
this.chat.detokenizeMessage(out);
|
this.chat.detokenizeMessage(out);
|
||||||
|
|
|
@ -16,6 +16,9 @@ export const State = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const ANONYMOUS_ID = '683b45e4-f853-4c45-bf96-7d799cc93e34';
|
||||||
|
|
||||||
|
|
||||||
export default class SocketClient extends Module {
|
export default class SocketClient extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -62,7 +65,7 @@ export default class SocketClient extends Module {
|
||||||
|
|
||||||
this.settings.on(':changed:socket.use-cluster', () => {
|
this.settings.on(':changed:socket.use-cluster', () => {
|
||||||
this._host = null;
|
this._host = null;
|
||||||
if ( this.disconnected)
|
if ( this.disconnected )
|
||||||
this.connect();
|
this.connect();
|
||||||
else
|
else
|
||||||
this.reconnect();
|
this.reconnect();
|
||||||
|
@ -252,14 +255,18 @@ export default class SocketClient extends Module {
|
||||||
this._ping_time = performance.now();
|
this._ping_time = performance.now();
|
||||||
this._send(
|
this._send(
|
||||||
'hello',
|
'hello',
|
||||||
[`ffz_${window.FrankerFaceZ.version_info}`, this.settings.provider.get('client-id')],
|
[`ffz_${window.FrankerFaceZ.version_info}`, ANONYMOUS_ID],
|
||||||
(success, data) => {
|
(success, data) => {
|
||||||
if ( ! success )
|
if ( ! success )
|
||||||
return this.log.warn('Error Saying Hello', data);
|
return this.log.warn('Error Saying Hello', data);
|
||||||
|
|
||||||
this._on_pong(false, success, data[1]);
|
this._on_pong(false, success, data[1]);
|
||||||
this.settings.provider.set('client-id', data[0]);
|
/*if ( data[0] === ANONYMOUS_ID )
|
||||||
this.log.info('Client ID:', data[0]);
|
this.log.info('Client ID: <Anonymous>');
|
||||||
|
else {
|
||||||
|
this.settings.provider.set('client-id', data[0]);
|
||||||
|
this.log.info('Client ID:', data[0]);
|
||||||
|
}*/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ query FFZ_FetchPoll($id: ID!) {
|
||||||
login
|
login
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
|
durationSeconds
|
||||||
endedAt
|
endedAt
|
||||||
startedAt
|
startedAt
|
||||||
totalVoters
|
totalVoters
|
||||||
|
|
|
@ -5,7 +5,9 @@ query FFZ_FetchUser($id: ID, $login: String) {
|
||||||
displayName
|
displayName
|
||||||
profileImageURL(width: 50)
|
profileImageURL(width: 50)
|
||||||
roles {
|
roles {
|
||||||
|
isAffiliate
|
||||||
isPartner
|
isPartner
|
||||||
|
isStaff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
9
src/utilities/data/user-self.gql
Normal file
9
src/utilities/data/user-self.gql
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
query FFZ_UserSelf($id: ID, $login: String) {
|
||||||
|
user(id: $id, login: $login) {
|
||||||
|
id
|
||||||
|
self {
|
||||||
|
isEditor
|
||||||
|
isModerator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -194,6 +194,15 @@ export default class TwitchData extends Module {
|
||||||
return get('data.user', data);
|
return get('data.user', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUserSelf(id, login) {
|
||||||
|
const data = await this.queryApollo(
|
||||||
|
await import(/* webpackChunkName: 'queries' */ './data/user-self.gql'),
|
||||||
|
{ id, login }
|
||||||
|
);
|
||||||
|
|
||||||
|
return get('data.user.self', data);
|
||||||
|
}
|
||||||
|
|
||||||
async getLastBroadcast(id, login) {
|
async getLastBroadcast(id, login) {
|
||||||
const data = await this.queryApollo(
|
const data = await this.queryApollo(
|
||||||
await import(/* webpackChunkName: 'queries' */ './data/last-broadcast.gql'),
|
await import(/* webpackChunkName: 'queries' */ './data/last-broadcast.gql'),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue