mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-07 06:40:54 +00:00
Merge branch 'master' of https://github.com/FrankerFaceZ/FrankerFaceZ into #1121-disable-all-sub-gifting
This commit is contained in:
commit
434a8d5fed
18 changed files with 282 additions and 132 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.30.1",
|
"version": "4.31.1",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
@ -85,7 +85,10 @@ export default class AddonManager extends Module {
|
||||||
// main script's execution.
|
// main script's execution.
|
||||||
for(const id of this.enabled_addons)
|
for(const id of this.enabled_addons)
|
||||||
if ( this.hasAddon(id) && this.doesAddonTarget(id) )
|
if ( this.hasAddon(id) && this.doesAddonTarget(id) )
|
||||||
this._enableAddon(id);
|
this._enableAddon(id).catch(err => {
|
||||||
|
this.log.error(`An error occured while enabling the add-on "${id}":` , err);
|
||||||
|
this.log.capture(err);
|
||||||
|
});
|
||||||
|
|
||||||
this.emit(':ready');
|
this.emit(':ready');
|
||||||
});
|
});
|
||||||
|
@ -353,7 +356,10 @@ export default class AddonManager extends Module {
|
||||||
|
|
||||||
// Actually load it.
|
// Actually load it.
|
||||||
if ( this.doesAddonTarget(id) )
|
if ( this.doesAddonTarget(id) )
|
||||||
this._enableAddon(id);
|
this._enableAddon(id).catch(err => {
|
||||||
|
this.log.error(`An error occured while enabling the add-on "${id}":` , err);
|
||||||
|
this.log.capture(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async disableAddon(id, save = true) {
|
async disableAddon(id, save = true) {
|
||||||
|
|
|
@ -412,50 +412,17 @@ export default class Badges extends Module {
|
||||||
tip.add_class = 'ffz__tooltip--badges';
|
tip.add_class = 'ffz__tooltip--badges';
|
||||||
|
|
||||||
const show_previews = this.parent.context.get('tooltip.badge-images');
|
const show_previews = this.parent.context.get('tooltip.badge-images');
|
||||||
let container = target.parentElement.parentElement;
|
const ds = this.getBadgeData(target);
|
||||||
if ( ! container.dataset.roomId )
|
|
||||||
container = target.closest('[data-room-id]');
|
|
||||||
|
|
||||||
const room_id = container?.dataset?.roomId,
|
const out = [];
|
||||||
room_login = container?.dataset?.room,
|
|
||||||
out = [];
|
|
||||||
|
|
||||||
let data;
|
if ( ds.data == null )
|
||||||
if ( target.dataset.badgeData )
|
|
||||||
data = JSON.parse(target.dataset.badgeData);
|
|
||||||
else {
|
|
||||||
const badge_idx = target.dataset.badgeIdx;
|
|
||||||
let message;
|
|
||||||
|
|
||||||
if ( container.message )
|
|
||||||
message = container.message;
|
|
||||||
else {
|
|
||||||
const fine = this.resolve('site.fine');
|
|
||||||
|
|
||||||
if ( fine ) {
|
|
||||||
message = container[fine.accessor]?.return?.stateNode?.props?.message;
|
|
||||||
if ( ! message )
|
|
||||||
message = fine.searchParent(container, n => n.props?.message)?.props?.message;
|
|
||||||
if ( ! message )
|
|
||||||
message = fine.searchParent(container, n => n.props?.node)?.props?.node?._ffz_message;
|
|
||||||
if ( ! message )
|
|
||||||
message = fine.searchParent(container, n => n.props?.messageContext)?.props?.messageContext?.comment?._ffz_message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( message?._ffz_message)
|
|
||||||
message = message._ffz_message;
|
|
||||||
if ( message )
|
|
||||||
data = message.ffz_badge_cache?.[badge_idx]?.[1]?.badges;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( data == null )
|
|
||||||
return out;
|
return out;
|
||||||
|
|
||||||
for(const d of data) {
|
for(const d of ds.data) {
|
||||||
const p = d.provider;
|
const p = d.provider;
|
||||||
if ( p === 'twitch' ) {
|
if ( p === 'twitch' ) {
|
||||||
const bd = this.getTwitchBadge(d.badge, d.version, room_id, room_login),
|
const bd = this.getTwitchBadge(d.badge, d.version, ds.room_id, ds.room_login),
|
||||||
global_badge = this.getTwitchBadge(d.badge, d.version, null, null, true) || {};
|
global_badge = this.getTwitchBadge(d.badge, d.version, null, null, true) || {};
|
||||||
if ( ! bd )
|
if ( ! bd )
|
||||||
continue;
|
continue;
|
||||||
|
@ -489,14 +456,6 @@ export default class Badges extends Module {
|
||||||
{title}
|
{title}
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
/*out.push(e('div', {className: 'ffz-badge-tip'}, [
|
|
||||||
show_previews && e('img', {
|
|
||||||
className: 'preview-image ffz-badge',
|
|
||||||
src: bd.image4x
|
|
||||||
}),
|
|
||||||
bd.title
|
|
||||||
]));*/
|
|
||||||
|
|
||||||
} else if ( p === 'ffz' ) {
|
} else if ( p === 'ffz' ) {
|
||||||
out.push(<div class="ffz-badge-tip">
|
out.push(<div class="ffz-badge-tip">
|
||||||
{show_previews && d.image && <div
|
{show_previews && d.image && <div
|
||||||
|
@ -508,17 +467,6 @@ export default class Badges extends Module {
|
||||||
/>}
|
/>}
|
||||||
{d.title}
|
{d.title}
|
||||||
</div>);
|
</div>);
|
||||||
|
|
||||||
/*out.push(e('div', {className: 'ffz-badge-tip'}, [
|
|
||||||
show_previews && e('div', {
|
|
||||||
className: 'preview-image ffz-badge',
|
|
||||||
style: {
|
|
||||||
backgroundColor: d.color,
|
|
||||||
backgroundImage: `url("${d.image}")`
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
d.title
|
|
||||||
]));*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,31 +475,74 @@ export default class Badges extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getBadgeData(target) {
|
||||||
|
let container = target.parentElement?.parentElement;
|
||||||
|
if ( ! container?.dataset?.roomId )
|
||||||
|
container = target.closest('[data-room-id]');
|
||||||
|
|
||||||
|
const room_id = container?.dataset?.roomId,
|
||||||
|
room_login = container?.dataset?.room,
|
||||||
|
|
||||||
|
user_id = container?.dataset?.userId,
|
||||||
|
user_login = container?.dataset?.user;
|
||||||
|
|
||||||
|
let data;
|
||||||
|
if (target.dataset.badgeData )
|
||||||
|
data = JSON.parse(target.dataset.badgeData);
|
||||||
|
else {
|
||||||
|
const badge_idx = target.dataset.badgeIdx;
|
||||||
|
let message;
|
||||||
|
|
||||||
|
if ( container.message )
|
||||||
|
message = container.message;
|
||||||
|
else {
|
||||||
|
const fine = this.resolve('site.fine');
|
||||||
|
|
||||||
|
if ( fine ) {
|
||||||
|
message = container[fine.accessor]?.return?.stateNode?.props?.message;
|
||||||
|
if ( ! message )
|
||||||
|
message = fine.searchParent(container, n => n.props?.message)?.props?.message;
|
||||||
|
if ( ! message )
|
||||||
|
message = fine.searchParent(container, n => n.props?.node)?.props?.node?._ffz_message;
|
||||||
|
if ( ! message )
|
||||||
|
message = fine.searchParent(container, n => n.props?.messageContext)?.props?.messageContext?.comment?._ffz_message;
|
||||||
|
if ( ! message )
|
||||||
|
message = fine.searchParent(container, n => n._ffzIdentityMsg, 50)?._ffzIdentityMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( message?._ffz_message)
|
||||||
|
message = message._ffz_message;
|
||||||
|
if ( message )
|
||||||
|
data = message.ffz_badge_cache?.[badge_idx]?.[1]?.badges;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
room_id: room_id,
|
||||||
|
room_login: room_login,
|
||||||
|
user_id: user_id,
|
||||||
|
user_login: user_login,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
handleClick(event) {
|
handleClick(event) {
|
||||||
if ( ! this.parent.context.get('chat.badges.clickable') )
|
if ( ! this.parent.context.get('chat.badges.clickable') )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
let container = target.parentElement.parentElement;
|
const ds = this.getBadgeData(target);
|
||||||
if ( ! container.dataset.roomId )
|
|
||||||
container = target.closest('[data-room-id]');
|
|
||||||
|
|
||||||
const ds = container?.dataset,
|
if ( ds.data == null )
|
||||||
room_id = ds?.roomId,
|
|
||||||
room_login = ds?.room,
|
|
||||||
user_id = ds?.userId,
|
|
||||||
user_login = ds?.user,
|
|
||||||
data = JSON.parse(target.dataset.badgeData);
|
|
||||||
|
|
||||||
if ( data == null )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let url = null;
|
let url = null;
|
||||||
|
|
||||||
for(const d of data) {
|
for(const d of ds.data) {
|
||||||
const p = d.provider;
|
const p = d.provider;
|
||||||
if ( p === 'twitch' ) {
|
if ( p === 'twitch' ) {
|
||||||
const bd = this.getTwitchBadge(d.badge, d.version, room_id, room_login),
|
const bd = this.getTwitchBadge(d.badge, d.version, ds.room_id, ds.room_login),
|
||||||
global_badge = this.getTwitchBadge(d.badge, d.version, null, null, true) || {};
|
global_badge = this.getTwitchBadge(d.badge, d.version, null, null, true) || {};
|
||||||
if ( ! bd )
|
if ( ! bd )
|
||||||
continue;
|
continue;
|
||||||
|
@ -560,8 +551,8 @@ export default class Badges extends Module {
|
||||||
url = bd.click_url;
|
url = bd.click_url;
|
||||||
else if ( global_badge.click_url )
|
else if ( global_badge.click_url )
|
||||||
url = global_badge.click_url;
|
url = global_badge.click_url;
|
||||||
else if ( (bd.click_action === 'sub' || global_badge.click_action === 'sub') && room_login )
|
else if ( (bd.click_action === 'sub' || global_badge.click_action === 'sub') && ds.room_login )
|
||||||
url = `https://www.twitch.tv/subs/${room_login}`;
|
url = `https://www.twitch.tv/subs/${ds.room_login}`;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -570,7 +561,7 @@ export default class Badges extends Module {
|
||||||
} else if ( p === 'ffz' ) {
|
} else if ( p === 'ffz' ) {
|
||||||
const badge = this.badges[target.dataset.badge];
|
const badge = this.badges[target.dataset.badge];
|
||||||
if ( badge?.click_handler ) {
|
if ( badge?.click_handler ) {
|
||||||
url = badge.click_handler(user_id, user_login, room_id, room_login, data, event);
|
url = badge.click_handler(ds.user_id, ds.user_login, ds.room_id, ds.room_login, ds.data, event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,18 @@ let tokenizer;
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['data', 'url', 'events', 'forceFull', 'forceUnsafe', 'forceMedia'],
|
props: ['data', 'url', 'events', 'forceFull', 'forceUnsafe', 'forceMedia', 'forceMid'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
has_tokenizer: false,
|
has_tokenizer: false,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
|
version: null,
|
||||||
|
fragments: {},
|
||||||
error: null,
|
error: null,
|
||||||
accent: null,
|
accent: null,
|
||||||
short: null,
|
short: null,
|
||||||
|
mid: null,
|
||||||
full: null,
|
full: null,
|
||||||
unsafe: false,
|
unsafe: false,
|
||||||
urls: null,
|
urls: null,
|
||||||
|
@ -103,9 +106,12 @@ export default {
|
||||||
|
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
this.version = null;
|
||||||
this.accent = null;
|
this.accent = null;
|
||||||
this.short = null;
|
this.short = null;
|
||||||
|
this.mid = null;
|
||||||
this.full = null;
|
this.full = null;
|
||||||
|
this.fragments = {};
|
||||||
this.unsafe = false;
|
this.unsafe = false;
|
||||||
this.urls = null;
|
this.urls = null;
|
||||||
this.allow_media = false;
|
this.allow_media = false;
|
||||||
|
@ -164,10 +170,13 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
this.version = data.v;
|
||||||
this.error = data.error;
|
this.error = data.error;
|
||||||
this.accent = data.accent;
|
this.accent = data.accent;
|
||||||
this.short = data.short;
|
this.short = data.short;
|
||||||
|
this.mid = data.mid;
|
||||||
this.full = data.full;
|
this.full = data.full;
|
||||||
|
this.fragments = data.fragments ?? {};
|
||||||
this.unsafe = data.unsafe;
|
this.unsafe = data.unsafe;
|
||||||
this.urls = data.urls;
|
this.urls = data.urls;
|
||||||
this.allow_media = data.allow_media;
|
this.allow_media = data.allow_media;
|
||||||
|
@ -214,14 +223,22 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
renderBody(h) {
|
renderBody(h) {
|
||||||
if ( this.has_tokenizer && this.loaded && (this.forceFull ? this.full : this.short) ) {
|
let body = this.forceFull ? this.full :
|
||||||
|
this.forceMid ? this.mid : this.short;
|
||||||
|
|
||||||
|
if ( this.has_tokenizer && this.version && this.version > tokenizer.VERSION )
|
||||||
|
body = null;
|
||||||
|
|
||||||
|
if ( this.has_tokenizer && this.loaded && body ) {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
class: 'ffz--card-rich tw-full-width tw-overflow-hidden tw-flex tw-flex-column'
|
class: 'ffz--card-rich tw-full-width tw-overflow-hidden tw-flex tw-flex-column'
|
||||||
}, tokenizer.renderTokens(this.forceFull ? this.full : this.short, h, {
|
}, tokenizer.renderTokens(body, h, {
|
||||||
vue: true,
|
vue: true,
|
||||||
tList: (...args) => this.tList(...args),
|
tList: (...args) => this.tList(...args),
|
||||||
i18n: this.getI18n(),
|
i18n: this.getI18n(),
|
||||||
|
|
||||||
|
fragments: this.fragments,
|
||||||
|
|
||||||
allow_media: this.forceMedia ?? this.allow_media,
|
allow_media: this.forceMedia ?? this.allow_media,
|
||||||
allow_unsafe: this.forceUnsafe ?? this.allow_unsafe
|
allow_unsafe: this.forceUnsafe ?? this.allow_unsafe
|
||||||
}));
|
}));
|
||||||
|
@ -234,6 +251,9 @@ export default {
|
||||||
if ( this.loaded && this.forceFull && ! this.full ) {
|
if ( this.loaded && this.forceFull && ! this.full ) {
|
||||||
description = 'null';
|
description = 'null';
|
||||||
|
|
||||||
|
} else if ( this.loaded && this.forceMid && ! this.mid ) {
|
||||||
|
description = 'null -- will use short instead';
|
||||||
|
|
||||||
} else if ( this.error ) {
|
} else if ( this.error ) {
|
||||||
title = this.t('card.error', 'An error occurred.');
|
title = this.t('card.error', 'An error occurred.');
|
||||||
description = this.error;
|
description = this.error;
|
||||||
|
|
|
@ -224,6 +224,16 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.rich.want-mid', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Appearance >> Rich Content',
|
||||||
|
title: 'Display larger rich content in chat.',
|
||||||
|
description: 'This enables the use of bigger rich content embeds in chat. This is **not** recommended for most users and/or chats.\n\n**Note:** Enabling this may cause chat to scroll at inopportune times due to content loading. Moderators should not use this feature.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.rich.hide-tokens', {
|
this.settings.add('chat.rich.hide-tokens', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -1879,11 +1889,13 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
const providers = this.__rich_providers;
|
const providers = this.__rich_providers;
|
||||||
|
|
||||||
|
const want_mid = this.context.get('chat.rich.want-mid');
|
||||||
|
|
||||||
for(const token of tokens) {
|
for(const token of tokens) {
|
||||||
for(const provider of providers)
|
for(const provider of providers)
|
||||||
if ( provider.test.call(this, token, msg) ) {
|
if ( provider.test.call(this, token, msg) ) {
|
||||||
token.hidden = provider.can_hide_token && (this.context.get('chat.rich.hide-tokens') || provider.hide_token);
|
token.hidden = provider.can_hide_token && (this.context.get('chat.rich.hide-tokens') || provider.hide_token);
|
||||||
return provider.process.call(this, token);
|
return provider.process.call(this, token, want_mid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,12 @@ export const Links = {
|
||||||
return token.type === 'link'
|
return token.type === 'link'
|
||||||
},
|
},
|
||||||
|
|
||||||
process(token) {
|
process(token, want_mid) {
|
||||||
return {
|
return {
|
||||||
card_tooltip: true,
|
card_tooltip: true,
|
||||||
url: token.url,
|
url: token.url,
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
|
want_mid,
|
||||||
|
|
||||||
getData: async (refresh = false) => {
|
getData: async (refresh = false) => {
|
||||||
let data;
|
let data;
|
||||||
|
|
|
@ -56,8 +56,6 @@ function datasetBool(value) {
|
||||||
return value == null ? null : value === 'true';
|
return value == null ? null : value === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOOLTIP_VERSION = 5;
|
|
||||||
|
|
||||||
export const Links = {
|
export const Links = {
|
||||||
type: 'link',
|
type: 'link',
|
||||||
priority: 50,
|
priority: 50,
|
||||||
|
@ -91,12 +89,15 @@ export const Links = {
|
||||||
import(/* webpackChunkName: 'rich_tokens' */ 'utilities/rich_tokens'),
|
import(/* webpackChunkName: 'rich_tokens' */ 'utilities/rich_tokens'),
|
||||||
this.get_link_info(url)
|
this.get_link_info(url)
|
||||||
]).then(([rich_tokens, data]) => {
|
]).then(([rich_tokens, data]) => {
|
||||||
if ( ! data || (data.v || 1) > TOOLTIP_VERSION )
|
if ( ! data || (data.v || 1) > rich_tokens.VERSION )
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
tList: (...args) => this.i18n.tList(...args),
|
tList: (...args) => this.i18n.tList(...args),
|
||||||
i18n: this.i18n,
|
i18n: this.i18n,
|
||||||
|
|
||||||
|
fragments: data.fragments,
|
||||||
|
|
||||||
allow_media: show_images,
|
allow_media: show_images,
|
||||||
allow_unsafe: show_unsafe,
|
allow_unsafe: show_unsafe,
|
||||||
onload: () => requestAnimationFrame(() => tip.update())
|
onload: () => requestAnimationFrame(() => tip.update())
|
||||||
|
@ -111,12 +112,14 @@ export const Links = {
|
||||||
if ( data.full ) {
|
if ( data.full ) {
|
||||||
content = rich_tokens.renderTokens(data.full, createElement, ctx);
|
content = rich_tokens.renderTokens(data.full, createElement, ctx);
|
||||||
|
|
||||||
} else {
|
} else if ( data.mid ) {
|
||||||
if ( data.short ) {
|
content = rich_tokens.renderTokens(data.mid, createElement, ctx);
|
||||||
|
|
||||||
|
} else if ( data.short ) {
|
||||||
content = rich_tokens.renderTokens(data.short, createElement, ctx);
|
content = rich_tokens.renderTokens(data.short, createElement, ctx);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
content = this.i18n.t('card.empty', 'No data was returned.');
|
content = this.i18n.t('card.empty', 'No data was returned.');
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! data.urls )
|
if ( ! data.urls )
|
||||||
return content;
|
return content;
|
||||||
|
@ -1038,7 +1041,7 @@ export const CheerEmotes = {
|
||||||
|
|
||||||
if ( length > 12 ) {
|
if ( length > 12 ) {
|
||||||
out.push(<br />);
|
out.push(<br />);
|
||||||
out.push(this.i18n.t('tooltip.bits.more', '(and {count} more)', length-12));
|
out.push(this.i18n.t('tooltip.bits.more', '(and {count, number} more)', length-12));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,22 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
||||||
|
<label>
|
||||||
|
{{ t('debug.link-provider.mid-embed', 'Mid Embed') }}
|
||||||
|
</label>
|
||||||
|
<div class="tw-full-width tw-overflow-hidden">
|
||||||
|
<chat-rich
|
||||||
|
v-if="rich_data"
|
||||||
|
:data="rich_data"
|
||||||
|
:url="url"
|
||||||
|
:force-mid="true"
|
||||||
|
:force-media="force_media"
|
||||||
|
:force-unsafe="force_unsafe"
|
||||||
|
:events="events"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
||||||
<label>
|
<label>
|
||||||
{{ t('debug.link-provider.full-embed', 'Full Embed') }}
|
{{ t('debug.link-provider.full-embed', 'Full Embed') }}
|
||||||
|
@ -172,6 +188,14 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
||||||
|
<label>
|
||||||
|
{{ t('debug.link-provider.raw-length', 'Raw Length') }}
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
{{ tNumber(length) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
<div class="tw-flex tw-mg-b-1 tw-full-width">
|
||||||
<label>
|
<label>
|
||||||
{{ t('debug.link-provider.raw', 'Raw Data') }}
|
{{ t('debug.link-provider.raw', 'Raw Data') }}
|
||||||
|
@ -234,6 +258,7 @@ export default {
|
||||||
rich_data: null,
|
rich_data: null,
|
||||||
raw_loading: false,
|
raw_loading: false,
|
||||||
raw_data: null,
|
raw_data: null,
|
||||||
|
length: 0,
|
||||||
|
|
||||||
force_media: state?.ffz_lt_media ?? true,
|
force_media: state?.ffz_lt_media ?? true,
|
||||||
force_unsafe: state?.ffz_lt_unsafe ?? false,
|
force_unsafe: state?.ffz_lt_unsafe ?? false,
|
||||||
|
@ -436,6 +461,7 @@ export default {
|
||||||
|
|
||||||
async refreshRaw() {
|
async refreshRaw() {
|
||||||
this.raw_data = null;
|
this.raw_data = null;
|
||||||
|
this.length = 0;
|
||||||
if ( ! this.rich_data ) {
|
if ( ! this.rich_data ) {
|
||||||
this.raw_loading = false;
|
this.raw_loading = false;
|
||||||
return;
|
return;
|
||||||
|
@ -443,7 +469,9 @@ export default {
|
||||||
|
|
||||||
this.raw_loading = true;
|
this.raw_loading = true;
|
||||||
try {
|
try {
|
||||||
this.raw_data = JSON.stringify(await this.chat.get_link_info(this.url), null, '\t');
|
const data = await this.chat.get_link_info(this.url);
|
||||||
|
this.raw_data = JSON.stringify(data, null, '\t');
|
||||||
|
this.length = JSON.stringify(data).length;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.raw_data = `Error\n\n${err.toString()}`;
|
this.raw_data = `Error\n\n${err.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,7 +389,7 @@ export default class SettingsManager extends Module {
|
||||||
|
|
||||||
async generateBackupFile() {
|
async generateBackupFile() {
|
||||||
const now = new Date(),
|
const now = new Date(),
|
||||||
timestamp = `${now.getFullYear()}-${now.getMonth()}-${now.getDate()}`;
|
timestamp = `${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()}`;
|
||||||
|
|
||||||
if ( await this._needsZipBackup() ) {
|
if ( await this._needsZipBackup() ) {
|
||||||
const blob = await this._getZipBackup();
|
const blob = await this._getZipBackup();
|
||||||
|
|
|
@ -298,12 +298,13 @@ export default class Input extends Module {
|
||||||
const t = this;
|
const t = this;
|
||||||
|
|
||||||
const originalOnKeyDown = inst.onKeyDown,
|
const originalOnKeyDown = inst.onKeyDown,
|
||||||
originalOnMessageSend = inst.onMessageSend;
|
originalOnMessageSend = inst.onMessageSend,
|
||||||
//old_resize = inst.resizeInput;
|
old_resize = inst.resizeInput;
|
||||||
|
|
||||||
inst.resizeInput = function(msg) {
|
inst.resizeInput = function(msg, ...args) {
|
||||||
|
try {
|
||||||
if ( msg ) {
|
if ( msg ) {
|
||||||
if ( inst.chatInputRef ) {
|
if ( inst.chatInputRef instanceof Element ) {
|
||||||
const style = getComputedStyle(inst.chatInputRef),
|
const style = getComputedStyle(inst.chatInputRef),
|
||||||
height = style && parseFloat(style.lineHeight || 18) || 18,
|
height = style && parseFloat(style.lineHeight || 18) || 18,
|
||||||
t = height * 1 + 20,
|
t = height * 1 + 20,
|
||||||
|
@ -318,6 +319,10 @@ export default class Input extends Module {
|
||||||
inst.setState({
|
inst.setState({
|
||||||
numInputRows: 1
|
numInputRows: 1
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
t.log.error('Error in resizeInput', err);
|
||||||
|
return old_resize.call(this, msg, ...args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inst.messageHistory = [];
|
inst.messageHistory = [];
|
||||||
|
|
|
@ -176,7 +176,14 @@ export default class RichContent extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody() {
|
renderBody() {
|
||||||
const doc = this.props.force_full ? this.state.full : this.state.short;
|
let doc = this.props.force_full ? this.state.full :
|
||||||
|
this.props.force_mid ? this.state.mid :
|
||||||
|
((this.props.want_mid ? this.state.mid : null) ?? this.state.short);
|
||||||
|
|
||||||
|
if ( t.has_tokenizer && this.state.v && this.state.v > t.tokenizer.VERSION)
|
||||||
|
doc = null;
|
||||||
|
|
||||||
|
//const doc = (this.props.force_full ? this.state.full : null) ?? (this.props.force_mid ? this.state.mid : null) ?? this.state.short;
|
||||||
if ( t.has_tokenizer && this.state.loaded && doc ) {
|
if ( t.has_tokenizer && this.state.loaded && doc ) {
|
||||||
return (<div class="ffz-card-rich tw-full-width tw-overflow-hidden tw-flex tw-flex-column">
|
return (<div class="ffz-card-rich tw-full-width tw-overflow-hidden tw-flex tw-flex-column">
|
||||||
{t.tokenizer.renderTokens(doc, createElement, {
|
{t.tokenizer.renderTokens(doc, createElement, {
|
||||||
|
@ -184,6 +191,8 @@ export default class RichContent extends Module {
|
||||||
tList: (...args) => t.i18n.tList(...args),
|
tList: (...args) => t.i18n.tList(...args),
|
||||||
i18n: t.i18n,
|
i18n: t.i18n,
|
||||||
|
|
||||||
|
fragments: this.state.fragments,
|
||||||
|
|
||||||
allow_media: t.chat.context.get('tooltip.link-images'),
|
allow_media: t.chat.context.get('tooltip.link-images'),
|
||||||
allow_unsafe: t.chat.context.get('tooltip.link-nsfw-images')
|
allow_unsafe: t.chat.context.get('tooltip.link-nsfw-images')
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -145,6 +145,16 @@ export default class SettingsMenu extends Module {
|
||||||
this.props.onCloseSettings();
|
this.props.onCloseSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
user,
|
||||||
|
badges,
|
||||||
|
ffz_badges: t.badges.getBadges(user.id, user.login, this.props.channelID, this.props.channelLogin),
|
||||||
|
roomID: this.props.channelID,
|
||||||
|
roomLogin: this.props.channelLogin
|
||||||
|
};
|
||||||
|
|
||||||
|
this._ffzIdentityMsg = msg;
|
||||||
|
|
||||||
return (<div class="ffz-identity">
|
return (<div class="ffz-identity">
|
||||||
<div class="tw-mg-y-05 tw-pd-x-05">
|
<div class="tw-mg-y-05 tw-pd-x-05">
|
||||||
<p class="tw-c-text-alt-2 tw-font-size-6 tw-strong tw-upcase">
|
<p class="tw-c-text-alt-2 tw-font-size-6 tw-strong tw-upcase">
|
||||||
|
@ -162,15 +172,9 @@ export default class SettingsMenu extends Module {
|
||||||
<span
|
<span
|
||||||
class="ffz--editor-badges"
|
class="ffz--editor-badges"
|
||||||
data-room-id={this.props.channelID}
|
data-room-id={this.props.channelID}
|
||||||
data-room-login={this.props.channelLogin}
|
data-room={this.props.channelLogin}
|
||||||
>
|
>
|
||||||
{t.badges.render({
|
{t.badges.render(msg, createElement, true, true)}
|
||||||
user,
|
|
||||||
badges,
|
|
||||||
ffz_badges: t.badges.getBadges(user.id, user.login, this.props.channelID, this.props.channelLogin),
|
|
||||||
roomID: this.props.channelID,
|
|
||||||
roomLogin: this.props.channelLogin
|
|
||||||
}, createElement, true, true)}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="tw-strong notranslate" style={{color}}>
|
<span class="tw-strong notranslate" style={{color}}>
|
||||||
|
@ -192,7 +196,8 @@ export default class SettingsMenu extends Module {
|
||||||
const out = old_render.call(this);
|
const out = old_render.call(this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const children = out?.props?.children?.props?.children?.[1]?.props?.children?.props?.children;
|
const children = out?.props?.children?.props?.children?.props?.children?.[1]?.props?.children?.props?.children;
|
||||||
|
//const children = out?.props?.children?.props?.children?.[1]?.props?.children?.props?.children;
|
||||||
if ( Array.isArray(children) ) {
|
if ( Array.isArray(children) ) {
|
||||||
const extra = this.ffzRenderIdentity();
|
const extra = this.ffzRenderIdentity();
|
||||||
if ( extra )
|
if ( extra )
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input__textarea .tw-textarea {
|
.chat-input__textarea {
|
||||||
|
.chat-wysiwyg-input__editor,
|
||||||
|
.tw-textarea {
|
||||||
padding-left: 1rem !important;
|
padding-left: 1rem !important;
|
||||||
}
|
}
|
||||||
|
}
|
|
@ -4,6 +4,12 @@
|
||||||
--ffz-theater-height: calc(calc(100vw * 0.5625) + var(--ffz-portrait-extra-height));
|
--ffz-theater-height: calc(calc(100vw * 0.5625) + var(--ffz-portrait-extra-height));
|
||||||
--ffz-chat-height: calc(100vh - var(--ffz-player-height));
|
--ffz-chat-height: calc(100vh - var(--ffz-player-height));
|
||||||
|
|
||||||
|
.chat-shell .ffz--chat-card {
|
||||||
|
--width: max(30rem, min(50%, calc(1.5 * var(--ffz-chat-width))));
|
||||||
|
width: var(--width);
|
||||||
|
margin-left: min(2rem, calc(100% - calc(4rem + var(--width))));
|
||||||
|
}
|
||||||
|
|
||||||
& > div:first-child > div[class^="Layout-sc"] {
|
& > div:first-child > div[class^="Layout-sc"] {
|
||||||
.ffz--portrait-invert & {
|
.ffz--portrait-invert & {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -14,22 +14,34 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
md: getMD()
|
output: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
watch: {
|
||||||
output() {
|
source() {
|
||||||
if ( ! this.md )
|
this.rebuild();
|
||||||
return '';
|
|
||||||
|
|
||||||
return this.md.render(this.source);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
this.md = getMD();
|
||||||
if ( ! this.md )
|
if ( ! this.md )
|
||||||
awaitMD().then(md => this.md = md);
|
awaitMD().then(md => {
|
||||||
|
this.md = md;
|
||||||
|
this.rebuild();
|
||||||
|
});
|
||||||
|
else
|
||||||
|
this.rebuild();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
rebuild() {
|
||||||
|
if ( ! this.md )
|
||||||
|
this.output = '';
|
||||||
|
else
|
||||||
|
this.output = this.md.render(this.source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ export const UPDATE_TOKEN_SETTINGS = [
|
||||||
'chat.emoji.style',
|
'chat.emoji.style',
|
||||||
'chat.bits.stack',
|
'chat.bits.stack',
|
||||||
'chat.rich.enabled',
|
'chat.rich.enabled',
|
||||||
|
'chat.rich.want-mid',
|
||||||
'chat.rich.hide-tokens',
|
'chat.rich.hide-tokens',
|
||||||
'chat.rich.all-links',
|
'chat.rich.all-links',
|
||||||
'chat.rich.minimum-level',
|
'chat.rich.minimum-level',
|
||||||
|
|
|
@ -94,6 +94,39 @@ export function timeout(promise, delay) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class Mutex {
|
||||||
|
constructor(limit = 1) {
|
||||||
|
this.limit = limit;
|
||||||
|
this._active = 0;
|
||||||
|
this._waiting = [];
|
||||||
|
|
||||||
|
this._done = this._done.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get available() { return this._active < this.limit }
|
||||||
|
|
||||||
|
_done() {
|
||||||
|
this._active--;
|
||||||
|
|
||||||
|
while(this._active < this.limit && this._waiting.length > 0) {
|
||||||
|
this._active++;
|
||||||
|
const waiter = this._waiting.shift();
|
||||||
|
waiter(this._done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wait() {
|
||||||
|
if ( this._active < this.limit) {
|
||||||
|
this._active++;
|
||||||
|
return Promise.resolve(this._done);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(s => this._waiting.push(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a wrapper for a function that will only execute the function
|
* Return a wrapper for a function that will only execute the function
|
||||||
* a period of time after it has stopped being called.
|
* a period of time after it has stopped being called.
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {has} from 'utilities/object';
|
||||||
import Markdown from 'markdown-it';
|
import Markdown from 'markdown-it';
|
||||||
import MILA from 'markdown-it-link-attributes';
|
import MILA from 'markdown-it-link-attributes';
|
||||||
|
|
||||||
|
export const VERSION = 6;
|
||||||
|
|
||||||
export const TOKEN_TYPES = {};
|
export const TOKEN_TYPES = {};
|
||||||
|
|
||||||
const validate = (input, valid) => valid.includes(input) ? input : null;
|
const validate = (input, valid) => valid.includes(input) ? input : null;
|
||||||
|
@ -243,6 +245,17 @@ export function renderTokens(tokens, createElement, ctx, markdown) {
|
||||||
export default renderTokens;
|
export default renderTokens;
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Token Type: Reference
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
TOKEN_TYPES.ref = function(token, createElement, ctx) {
|
||||||
|
const frag = ctx.fragments?.[token.name];
|
||||||
|
if (frag )
|
||||||
|
return renderTokens(frag, createElement, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Token Type: Box
|
// Token Type: Box
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -667,9 +680,11 @@ function header_normal(token, createElement, ctx) {
|
||||||
content.appendChild(image);
|
content.appendChild(image);
|
||||||
else
|
else
|
||||||
content.insertBefore(image, content.firstChild);
|
content.insertBefore(image, content.firstChild);
|
||||||
} else {
|
} else if ( Array.isArray(content?.props?.children) ) {
|
||||||
console.warn('Add React support!');
|
if ( right )
|
||||||
console.log(content);
|
content.props.children.push(image);
|
||||||
|
else
|
||||||
|
content.props.children.unshift(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue