mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 05:15:54 +00:00
4.20.73
* Added: Option for changing the chat timestamp font size. * Changed: Allow chat room actions to be spread across multiple lines. * Fixed: The chat action editor not properly displaying spacers. * Fixed: Make `switchboard` wait for `web_munch` before trying to load a route.
This commit is contained in:
parent
9086230686
commit
add9f7a7d5
11 changed files with 212 additions and 24 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.20.72",
|
"version": "4.20.73",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
28
src/modules/chat/actions/components/edit-emoji.vue
Normal file
28
src/modules/chat/actions/components/edit-emoji.vue
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="tw-flex tw-align-items-start">
|
||||||
|
<label class="tw-mg-y-05">
|
||||||
|
{{ t('setting.actions.emoji', 'Emoji') }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<emoji-picker
|
||||||
|
:value="value.emoji"
|
||||||
|
class="tw-full-width"
|
||||||
|
@input="change"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['value'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change(val) {
|
||||||
|
this.value.emoji = val;
|
||||||
|
this.$emit('input', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
|
@ -158,7 +158,7 @@ export default class Actions extends Module {
|
||||||
path: 'Chat > Actions > Room @{"description": "Here, you can define custom actions that will appear above the chat input box."}',
|
path: 'Chat > Actions > Room @{"description": "Here, you can define custom actions that will appear above the chat input box."}',
|
||||||
component: 'chat-actions',
|
component: 'chat-actions',
|
||||||
context: ['room', 'room-mode'],
|
context: ['room', 'room-mode'],
|
||||||
inline: true,
|
inline: false,
|
||||||
|
|
||||||
data: () => {
|
data: () => {
|
||||||
const chat = this.resolve('site.chat');
|
const chat = this.resolve('site.chat');
|
||||||
|
@ -420,11 +420,38 @@ export default class Actions extends Module {
|
||||||
|
|
||||||
|
|
||||||
renderRoom(mod_icons, current_user, current_room, is_above, createElement) {
|
renderRoom(mod_icons, current_user, current_room, is_above, createElement) {
|
||||||
const actions = [],
|
const lines = [],
|
||||||
chat = this.resolve('site.chat');
|
chat = this.resolve('site.chat');
|
||||||
|
let line = null;
|
||||||
|
|
||||||
for(const data of this.parent.context.get('chat.actions.room')) {
|
for(const data of this.parent.context.get('chat.actions.room')) {
|
||||||
if ( ! data || ! data.action || ! data.appearance )
|
if ( ! data )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const type = data.type;
|
||||||
|
if ( type ) {
|
||||||
|
if ( type === 'new-line' ) {
|
||||||
|
line = null;
|
||||||
|
|
||||||
|
} else if ( type === 'space' ) {
|
||||||
|
if ( ! line )
|
||||||
|
lines.push(line = []);
|
||||||
|
|
||||||
|
line.push(<div class="tw-flex-grow-1" />);
|
||||||
|
|
||||||
|
} else if ( type === 'space-small' ) {
|
||||||
|
if ( ! line )
|
||||||
|
lines.push(line = []);
|
||||||
|
|
||||||
|
line.push(<div class="tw-mg-x-1" />);
|
||||||
|
|
||||||
|
} else
|
||||||
|
this.log.warn('Unknown action type', type);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! data.action || ! data.appearance )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let ap = data.appearance || {};
|
let ap = data.appearance || {};
|
||||||
|
@ -460,7 +487,10 @@ export default class Actions extends Module {
|
||||||
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
|
||||||
contents = def.render.call(this, ap, createElement, color);
|
contents = def.render.call(this, ap, createElement, color);
|
||||||
|
|
||||||
actions.push(<button
|
if ( ! line )
|
||||||
|
lines.push(line = []);
|
||||||
|
|
||||||
|
line.push(<button
|
||||||
class={`ffz-tooltip tw-pd-x-05 mod-icon ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
class={`ffz-tooltip tw-pd-x-05 mod-icon ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
||||||
data-tooltip-type="action"
|
data-tooltip-type="action"
|
||||||
data-action={data.action}
|
data-action={data.action}
|
||||||
|
@ -473,13 +503,18 @@ export default class Actions extends Module {
|
||||||
</button>);
|
</button>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! actions.length )
|
if ( ! lines.length )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const room = current_room && JSON.stringify(current_room);
|
const room = current_room && JSON.stringify(current_room),
|
||||||
|
multi_line = lines.length > 1;
|
||||||
|
|
||||||
|
const actions = multi_line ?
|
||||||
|
lines.map((line, idx) => <div key={idx} class="tw-flex tw-full-width tw-flex-row tw-flex-wrap">{line}</div>) :
|
||||||
|
lines[0];
|
||||||
|
|
||||||
return (<div
|
return (<div
|
||||||
class={`ffz--room-actions ffz-action-data tw-flex tw-flex-grow-1 tw-flex-wrap tw-align-items-center ${is_above ? 'tw-pd-y-05 tw-border-t' : 'tw-mg-x-05'}`}
|
class={`ffz--room-actions${multi_line ? ' tw-flex-column' : ''} ffz-action-data tw-flex tw-flex-grow-1 tw-flex-wrap tw-align-items-center ${is_above ? 'tw-pd-y-05 tw-border-t' : 'tw-mg-x-05'}`}
|
||||||
data-room={room}
|
data-room={room}
|
||||||
>
|
>
|
||||||
{actions}
|
{actions}
|
||||||
|
|
|
@ -91,3 +91,29 @@ export const image = {
|
||||||
return <figure class="mod-icon__image"><img src={data.image} /></figure>;
|
return <figure class="mod-icon__image"><img src={data.image} /></figure>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Emoji
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/*export const emoji = {
|
||||||
|
title: 'Emoji',
|
||||||
|
title_i18n: 'setting.actions.appearance.emoji',
|
||||||
|
|
||||||
|
colored: false,
|
||||||
|
editor: () => import(/* webpackChunkName: 'main-menu' * / './components/edit-emoji.vue'),
|
||||||
|
|
||||||
|
load(data) {
|
||||||
|
if ( data.icon && data.icon.startsWith('ffz-fa') )
|
||||||
|
loadFontAwesome();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
component: () => import(/* webpackChunkName: 'main-menu' * / './components/preview-emoji.vue'),
|
||||||
|
|
||||||
|
render(data, createElement) {
|
||||||
|
return <figure class="mod-icon__image"><img src={data.image} /></figure>;
|
||||||
|
}
|
||||||
|
}*/
|
|
@ -98,6 +98,23 @@ export default class Chat extends Module {
|
||||||
force_seen: true
|
force_seen: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.timestamp-size', {
|
||||||
|
default: null,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Appearance >> General',
|
||||||
|
title: 'Timestamp Font Size',
|
||||||
|
description: 'How large should timestamps be, in pixels. Defaults to Font Size if not set.',
|
||||||
|
component: 'setting-text-box',
|
||||||
|
process(val) {
|
||||||
|
val = parseInt(val, 10);
|
||||||
|
if ( isNaN(val) || ! isFinite(val) || val <= 0 )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.font-size', {
|
this.settings.add('chat.font-size', {
|
||||||
default: 13,
|
default: 13,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -108,7 +125,7 @@ export default class Chat extends Module {
|
||||||
process(val) {
|
process(val) {
|
||||||
val = parseInt(val, 10);
|
val = parseInt(val, 10);
|
||||||
if ( isNaN(val) || ! isFinite(val) || val <= 0 )
|
if ( isNaN(val) || ! isFinite(val) || val <= 0 )
|
||||||
return 12;
|
return 13;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,8 +193,19 @@
|
||||||
:key="idx"
|
:key="idx"
|
||||||
class="tw-flex tw-align-items-center tw-justify-content-center"
|
class="tw-flex tw-align-items-center tw-justify-content-center"
|
||||||
>
|
>
|
||||||
|
<template v-for="act in actions">
|
||||||
|
<div
|
||||||
|
v-if="act.type === 'space'"
|
||||||
|
:key="act.id"
|
||||||
|
class="tw-flex-grow-1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="act.type === 'space-small'"
|
||||||
|
:key="act.id"
|
||||||
|
class="tw-mg-x-1"
|
||||||
|
/>
|
||||||
<action-preview
|
<action-preview
|
||||||
v-for="act in actions"
|
v-else
|
||||||
:key="act.id"
|
:key="act.id"
|
||||||
:act="act.v"
|
:act="act.v"
|
||||||
:color="color(act.v.appearance.color)"
|
:color="color(act.v.appearance.color)"
|
||||||
|
@ -202,6 +213,7 @@
|
||||||
tooltip="true"
|
tooltip="true"
|
||||||
pad="true"
|
pad="true"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,7 +266,7 @@
|
||||||
<div v-else class="tw-pd-y-1">
|
<div v-else class="tw-pd-y-1">
|
||||||
<button
|
<button
|
||||||
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
||||||
@click="add_pasting = true"
|
@click="preparePaste"
|
||||||
>
|
>
|
||||||
<div class="tw-flex tw-align-items-center tw-pd-y-05 tw-pd-x-1">
|
<div class="tw-flex tw-align-items-center tw-pd-y-05 tw-pd-x-1">
|
||||||
<div class="tw-flex-grow-1 tw-mg-r-1">
|
<div class="tw-flex-grow-1 tw-mg-r-1">
|
||||||
|
@ -532,6 +544,9 @@ export default {
|
||||||
out.push(current);
|
out.push(current);
|
||||||
current = [];
|
current = [];
|
||||||
|
|
||||||
|
} else if ( type === 'space' || type === 'small-space' ) {
|
||||||
|
current.push(val.v);
|
||||||
|
|
||||||
} else if ( this.displayAction(val.v) )
|
} else if ( this.displayAction(val.v) )
|
||||||
current.push(val);
|
current.push(val);
|
||||||
}
|
}
|
||||||
|
@ -593,6 +608,14 @@ export default {
|
||||||
this.add_pasting = false;
|
this.add_pasting = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
preparePaste() {
|
||||||
|
this.add_open = true;
|
||||||
|
this.add_pasting = true;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.$refs.paste.focus()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
addFromJSON() {
|
addFromJSON() {
|
||||||
let value = this.$refs.paste.value;
|
let value = this.$refs.paste.value;
|
||||||
this.closeAdd();
|
this.closeAdd();
|
||||||
|
|
|
@ -662,6 +662,7 @@ export default class ChatHook extends Module {
|
||||||
|
|
||||||
const width = this.chat.context.get('chat.width'),
|
const width = this.chat.context.get('chat.width'),
|
||||||
action_size = this.chat.context.get('chat.actions.size'),
|
action_size = this.chat.context.get('chat.actions.size'),
|
||||||
|
ts_size = this.chat.context.get('chat.timestamp-size'),
|
||||||
size = this.chat.context.get('chat.font-size'),
|
size = this.chat.context.get('chat.font-size'),
|
||||||
emote_alignment = this.chat.context.get('chat.lines.emote-alignment'),
|
emote_alignment = this.chat.context.get('chat.lines.emote-alignment'),
|
||||||
lh = Math.round((20/12) * size);
|
lh = Math.round((20/12) * size);
|
||||||
|
@ -670,6 +671,11 @@ export default class ChatHook extends Module {
|
||||||
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
|
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
|
||||||
font = `"${font}"`;
|
font = `"${font}"`;
|
||||||
|
|
||||||
|
if ( ts_size )
|
||||||
|
this.css_tweaks.set('ts-size', `.chat-line__timestamp{font-size:${ts_size/10}rem}`);
|
||||||
|
else
|
||||||
|
this.css_tweaks.delete('ts-size');
|
||||||
|
|
||||||
this.css_tweaks.setVariable('chat-actions-size', `${action_size/10}rem`);
|
this.css_tweaks.setVariable('chat-actions-size', `${action_size/10}rem`);
|
||||||
this.css_tweaks.setVariable('chat-font-size', `${size/10}rem`);
|
this.css_tweaks.setVariable('chat-font-size', `${size/10}rem`);
|
||||||
this.css_tweaks.setVariable('chat-line-height', `${lh/10}rem`);
|
this.css_tweaks.setVariable('chat-line-height', `${lh/10}rem`);
|
||||||
|
@ -796,6 +802,7 @@ export default class ChatHook extends Module {
|
||||||
this.settings.main_context.on('changed:chat.use-width', this.updateChatCSS, this);
|
this.settings.main_context.on('changed:chat.use-width', this.updateChatCSS, this);
|
||||||
this.chat.context.on('changed:chat.actions.size', this.updateChatCSS, this);
|
this.chat.context.on('changed:chat.actions.size', this.updateChatCSS, this);
|
||||||
this.chat.context.on('changed:chat.font-size', this.updateChatCSS, this);
|
this.chat.context.on('changed:chat.font-size', this.updateChatCSS, this);
|
||||||
|
this.chat.context.on('changed:chat.timestamp-size', this.updateChatCSS, this);
|
||||||
this.chat.context.on('changed:chat.font-family', this.updateChatCSS, this);
|
this.chat.context.on('changed:chat.font-family', this.updateChatCSS, this);
|
||||||
this.chat.context.on('changed:chat.lines.emote-alignment', this.updateChatCSS, this);
|
this.chat.context.on('changed:chat.lines.emote-alignment', this.updateChatCSS, this);
|
||||||
this.chat.context.on('changed:chat.adjustment-mode', this.updateColors, this);
|
this.chat.context.on('changed:chat.adjustment-mode', this.updateColors, this);
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.clmgr-table__row,
|
.clmgr-table__row,
|
||||||
|
.sunlight-expanded-nav-drop-down-menu-layout__scrollable-area,
|
||||||
.stream-manager--page-view .mosaic-window-body,
|
.stream-manager--page-view .mosaic-window-body,
|
||||||
|
.ach-card,
|
||||||
|
.ach-card--expanded .ach-card__inner,
|
||||||
.room-upsell,
|
.room-upsell,
|
||||||
.chat-room,
|
.chat-room,
|
||||||
.qa-vod-chat,
|
.qa-vod-chat,
|
||||||
|
@ -59,6 +62,18 @@
|
||||||
background-color: var(--ffz-color-accent-8) !important;
|
background-color: var(--ffz-color-accent-8) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ach-q-card {
|
||||||
|
box-shadow: 0 0 0 1px var(--color-border-button) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ach-link:active .ach-card, .ach-link:focus .ach-card {
|
||||||
|
outline: .5rem auto var(--color-border-input-focus) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ach-card--expanded .ach-card__inner {
|
||||||
|
border-color: var(--color-border-brand) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|
|
@ -70,7 +70,12 @@ export default class Switchboard extends Module {
|
||||||
this.location = router.props.location.pathname;
|
this.location = router.props.location.pathname;
|
||||||
//const location = router.props.location.pathname;
|
//const location = router.props.location.pathname;
|
||||||
|
|
||||||
|
this.web_munch.waitForLoader().then(() => {
|
||||||
|
if ( this.web_munch._require )
|
||||||
|
return;
|
||||||
|
|
||||||
this.loadOne();
|
this.loadOne();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadOne() {
|
loadOne() {
|
||||||
|
|
|
@ -11,7 +11,8 @@ import { DEBUG } from '../constants';
|
||||||
|
|
||||||
const NAMES = [
|
const NAMES = [
|
||||||
'webpackJsonp',
|
'webpackJsonp',
|
||||||
'webpackChunktwitch_twilight'
|
'webpackChunktwitch_twilight',
|
||||||
|
'webpackChunktwitch_sunlight'
|
||||||
];
|
];
|
||||||
|
|
||||||
const HARD_MODULES = [
|
const HARD_MODULES = [
|
||||||
|
@ -46,6 +47,23 @@ export default class WebMunch extends Module {
|
||||||
// Loaded Modules
|
// Loaded Modules
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
|
waitForLoader() {
|
||||||
|
if ( this._original_loader )
|
||||||
|
return Promise.resolve();
|
||||||
|
|
||||||
|
const waiters = this._load_waiters = this._load_waiters || [];
|
||||||
|
return new Promise((s,f) => waiters.push([s,f]));
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolveLoadWait(errored) {
|
||||||
|
const waiters = this._load_waiters;
|
||||||
|
this._load_waiters = null;
|
||||||
|
|
||||||
|
if ( waiters )
|
||||||
|
for(const pair of waiters)
|
||||||
|
pair[errored ? 1 : 0]();
|
||||||
|
}
|
||||||
|
|
||||||
hookLoader(attempts = 0) {
|
hookLoader(attempts = 0) {
|
||||||
if ( this._original_loader )
|
if ( this._original_loader )
|
||||||
return this.log.warn('Attempted to call hookLoader twice.');
|
return this.log.warn('Attempted to call hookLoader twice.');
|
||||||
|
@ -58,8 +76,11 @@ export default class WebMunch extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! name ) {
|
if ( ! name ) {
|
||||||
if ( attempts > 500 )
|
if ( attempts > 240 ) {
|
||||||
return this.log.error("Unable to find webpack's loader after two minutes.");
|
this.log.error("Unable to find webpack's loader after one minute.");
|
||||||
|
this._resolveLoadWait(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return setTimeout(this.hookLoader.bind(this, attempts + 1), 250);
|
return setTimeout(this.hookLoader.bind(this, attempts + 1), 250);
|
||||||
}
|
}
|
||||||
|
@ -75,6 +96,7 @@ export default class WebMunch extends Module {
|
||||||
window[name] = this.webpackJsonpv3.bind(this);
|
window[name] = this.webpackJsonpv3.bind(this);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log.warn('Unable to wrap webpackJsonp due to write protection.');
|
this.log.warn('Unable to wrap webpackJsonp due to write protection.');
|
||||||
|
this._resolveLoadWait(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,14 +115,17 @@ export default class WebMunch extends Module {
|
||||||
thing.push = this.webpackJsonpv4.bind(this);
|
thing.push = this.webpackJsonpv4.bind(this);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.log.warn('Unable to wrap webpackJsonp (v4) due to write protection.');
|
this.log.warn('Unable to wrap webpackJsonp (v4) due to write protection.');
|
||||||
|
this._resolveLoadWait(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.log.error('webpackJsonp is of an unknown value. Unable to wrap.');
|
this.log.error('webpackJsonp is of an unknown value. Unable to wrap.');
|
||||||
|
this._resolveLoadWait(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._resolveLoadWait();
|
||||||
this.log.info(`Found and wrapped webpack's loader after ${(attempts||0)*250}ms.`);
|
this.log.info(`Found and wrapped webpack's loader after ${(attempts||0)*250}ms.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,13 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz--room-actions {
|
||||||
|
.chat-input__buttons-container &.tw-flex-column {
|
||||||
|
margin-top: -1rem;
|
||||||
|
margin-bottom: -1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.ffz--modifier-actions {
|
.ffz--modifier-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue