1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00
* Added: `/ffz reload` command to reload emote and badge data without reloading the page. (Note: Add-ons will need to update to add support for the command.)
* Changed: Improve monitor support for Current Monitor, specifically when a user has multiple monitors with the same name.
* Fixed: Track the loaded status of global FFZ badges when loading chat data.
* Fixed: The page scrolling incorrectly when using the FFZ emote menu in some situations.
This commit is contained in:
SirStendec 2023-04-24 15:09:21 -04:00
parent 9381c17e71
commit e6ad12c937
11 changed files with 219 additions and 18 deletions

View file

@ -1,7 +1,7 @@
{
"name": "frankerfacez",
"author": "Dan Salvato LLC",
"version": "4.46.2",
"version": "4.47.0",
"description": "FrankerFaceZ is a Twitch enhancement suite.",
"private": true,
"license": "Apache-2.0",

View file

@ -185,6 +185,7 @@ export default class Badges extends Module {
this.inject('tooltips');
this.inject('experiments');
this.inject('staging');
this.inject('load_tracker');
this.style = new ManagedStyle('badges');
@ -424,6 +425,11 @@ export default class Badges extends Module {
this.rebuildAllCSS();
this.loadGlobalBadges();
this.on('chat:reload-data', flags => {
if ( ! flags || flags.badges )
this.loadGlobalBadges();
});
this.tooltips.types.badge = (target, tip) => {
tip.add_class = 'ffz__tooltip--badges';
@ -951,6 +957,8 @@ export default class Badges extends Module {
async loadGlobalBadges(tries = 0) {
this.load_tracker.schedule('chat-data', 'ffz-global-badges');
let response, data;
if ( this.experiments.getAssignment('api_load') && tries < 1 )
@ -966,16 +974,20 @@ export default class Badges extends Module {
return setTimeout(() => this.loadGlobalBadges(tries), 500 * tries);
this.log.error('Error loading global badge data.', err);
this.load_tracker.notify('chat-data', 'ffz-global-badges', false);
return false;
}
if ( ! response.ok )
if ( ! response.ok ) {
this.load_tracker.notify('chat-data', 'ffz-global-badges', false);
return false;
}
try {
data = await response.json();
} catch(err) {
this.log.error('Error parsing global badge data.', err);
this.load_tracker.notify('chat-data', 'ffz-global-badges', false);
return false;
}
@ -1017,6 +1029,7 @@ export default class Badges extends Module {
this.log.info(`Loaded ${badges} badges and assigned them to ${users} users.`);
this.buildBadgeCSS();
this.load_tracker.notify('chat-data', 'ffz-global-badges');
}

View file

@ -639,6 +639,11 @@ export default class Emotes extends Module {
this.on('socket:command:follow_sets', this.updateFollowSets, this);
this.on('chat:reload-data', flags => {
if ( ! flags || flags.emotes )
this.loadGlobalSets();
});
this.loadGlobalSets();
}
@ -1419,6 +1424,14 @@ export default class Emotes extends Module {
}
removeDefaultSet(provider, set_id) {
if ( ! set_id ) {
const sets = this.default_sets.get(provider);
if ( sets )
for(const set_id of Array.from(sets))
this.removeDefaultSet(provider, set_id);
return;
}
if ( typeof set_id === 'number' )
set_id = `${set_id}`;
@ -1530,6 +1543,9 @@ export default class Emotes extends Module {
const sets = data.sets || {};
// Remove existing global sets, in case we have any.
this.removeDefaultSet('ffz-global');
for(const set_id of data.default_sets)
this.addDefaultSet('ffz-global', set_id);

View file

@ -1263,6 +1263,44 @@ export default class Chat extends Module {
for(const key in LINK_PROVIDERS)
if ( has(LINK_PROVIDERS, key) )
this.addLinkProvider(LINK_PROVIDERS[key]);
this.on('chat:reload-data', flags => {
for(const room of this.iterateRooms())
room.load_data();
});
this.on('chat:get-tab-commands', event => {
event.commands.push({
name: 'ffz reload',
description: this.i18n.t('chat.command.reload', 'Reload FFZ and add-on chat data (emotes, badges, etc.)'),
permissionLevel: 0,
ffz_group: 'FrankerFaceZ'
});
});
this.triggered_reload = false;
this.on('chat:ffz-command:reload', event => {
if ( this.triggered_reload )
return;
const sc = this.resolve('site.chat');
if ( sc?.addNotice )
sc.addNotice('*', this.i18n.t('chat.command.reload.starting', 'FFZ is reloading data...'));
this.triggered_reload = true;
this.emit('chat:reload-data');
});
this.on('load_tracker:complete:chat-data', () => {
if ( this.triggered_reload ) {
const sc = this.resolve('site.chat');
if ( sc?.addNotice )
sc.addNotice('*', this.i18n.t('chat.command.reload.done', 'FFZ has finished reloading data.'));
}
this.triggered_reload = false;
});
}

View file

@ -314,10 +314,10 @@ export default class Room {
this.data = d;
this.removeAllSets('main');
if ( d.set )
this.addSet('main', d.set);
else
this.removeAllSets('main');
if ( data.sets )
for(const set_id in data.sets)

View file

@ -7,21 +7,26 @@
<select
:id="'label$' + id"
v-model="value.data.label"
v-model="selected"
class="tw-flex-grow-1 tw-mg-l-1 tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 ffz-select"
>
<template v-for="mon in monitors">
<option :value="mon.label">
{{ mon.label }} ({{ mon.width }}&times;{{ mon.height }})
<template v-for="(mon, idx) in monitors">
<option :value="mon">
{{ idx + 1 }}. {{ mon.label }} ({{ mon.width }}&times;{{ mon.height }})
</option>
</template>
</select>
</div>
<div class="tw-c-text-alt-2">
{{ t('setting.filter.monitor.about', 'This setting requires that this site has the Window Management permission. Please be sure that it is allowed.') }}
</div>
</section>
</template>
<script>
import { sortScreens, matchScreen } from 'utilities/object';
let last_id = 0;
export default {
@ -31,7 +36,9 @@ export default {
return {
id: last_id++,
has_monitors: true,
monitors: []
monitors: [],
ready: false,
selected: null
}
},
@ -39,6 +46,22 @@ export default {
this.detectMonitors();
},
watch: {
selected() {
if ( ! this.ready || ! this.selected )
return;
const data = this.value.data = this.value.data || {};
data.label = this.selected.label;
data.index = this.monitors.indexOf(this.selected);
data.top = this.selected.top;
data.left = this.selected.left;
data.width = this.selected.width;
data.height = this.selected.height;
}
},
methods: {
async detectMonitors() {
let data;
@ -54,10 +77,21 @@ export default {
this.monitors = [];
for(const mon of data.screens)
this.monitors.push({
top: mon.top,
left: mon.left,
label: mon.label,
width: mon.width,
height: mon.height
});
sortScreens(this.monitors);
if ( this.value.data )
this.selected = matchScreen(this.monitors, this.value.data);
this.ready = true;
if ( ! this.selected )
this.selected = this.monitors[0];
}
}
}

View file

@ -4,7 +4,7 @@
// Profile Filters for Settings
// ============================================================================
import {glob_to_regex, escape_regex} from 'utilities/object';
import {glob_to_regex, escape_regex, matchScreen, sortScreens} from 'utilities/object';
import {createTester} from 'utilities/filtering';
import { DEBUG } from 'utilities/constants';
@ -425,7 +425,10 @@ if ( window.getScreenDetails ) {
if ( ! screen )
return false;
return screen.label === config.label;
const sorted = sortScreens(Array.from(details.screens)),
matched = matchScreen(sorted, config);
return matched === screen;
};
},

View file

@ -35,6 +35,25 @@ const TONE_EMOJI = [
'love_you_gesture'
];
function scrollIntoView(el, container) {
if ( ! container )
container = el.closest('.simplebar-scroll-content') ?? el.parentElement;
const height = container.offsetHeight,
el_height = el.offsetHeight,
el_top = el.offsetTop;
if ( el_height >= height ) {
container.scrollTop = el_top;
return;
}
const pos = el_top + (el_height / 2) - (height / 2);
container.scrollTop = pos;
}
function maybe_date(val) {
if ( ! val )
return val;
@ -1212,7 +1231,8 @@ export default class EmoteMenu extends Module {
requestAnimationFrame(() => {
const el = this.nav_ref?.querySelector?.(`button[data-key="${this.state.active_nav}"]`);
if ( el )
el.scrollIntoView({block: 'nearest', inline: 'start'});
scrollIntoView(el);
//el.scrollIntoView({block: 'nearest', inline: 'start'});
});
}
@ -1342,7 +1362,8 @@ export default class EmoteMenu extends Module {
const el = this.ref?.querySelector?.(`section[data-key="${key}"]`);
if ( el ) {
this.lock_active = true;
el.scrollIntoView({block: 'nearest', inline: 'start'});
scrollIntoView(el);
//el.scrollIntoView({block: 'nearest', inline: 'start'});
this.setState({
active_nav: key
});
@ -1381,7 +1402,8 @@ export default class EmoteMenu extends Module {
el = set && this.ref?.querySelector?.(`section[data-key="${set.key}"]`);
if ( el )
el.scrollIntoView({block: 'nearest', inline: 'start'});
scrollIntoView(el);
//el.scrollIntoView({block: 'nearest', inline: 'start'});
return;
}

View file

@ -2161,7 +2161,7 @@ export default class ChatHook extends Module {
room = room.toLowerCase();
for(const inst of this.ChatService.instances) {
if ( inst.props.channelLogin.toLowerCase() === room ) {
if ( room === '*' || inst.props.channelLogin.toLowerCase() === room ) {
inst.addMessage({
type: this.chat_types.Notice,
message
@ -2228,11 +2228,39 @@ export default class ChatHook extends Module {
msg = msg.replace(/\s+/g, ' ');
if ( msg.startsWith('/ffz') ) {
inst.addMessage({
type: t.chat_types.Notice,
message: 'The /ffz command is not yet re-implemented.'
msg = msg.slice(5).trim();
const idx = msg.indexOf(' ');
let subcmd;
if ( idx === -1 ) {
subcmd = msg;
msg = '';
} else {
subcmd = msg.slice(0, idx);
msg = msg.slice(idx + 1).trimStart();
}
const event = new FFZEvent({
command: subcmd,
message: msg,
extra,
context: t.chat.context,
channel: inst.props.channelLogin,
_inst: inst,
addMessage,
sendMessage
});
const topic = `chat:ffz-command:${subcmd}`,
listeners = t.listeners(topic);
if ( listeners?.length > 0 )
t.emit(topic, event);
else
inst.addMessage({
type: t.chat_types.Notice,
message: t.i18n.t('chat.ffz-command.invalid', 'No such command: /ffz {subcmd}', {subcmd})
});
return false;
}

View file

@ -354,6 +354,7 @@ export default class ChatLine extends Module {
this.can_reprocess = true;
this.on('chat:reload-data', () => this.can_reprocess = true);
this.on('chat:room-add', () => this.can_reprocess = true);
this.on('load_tracker:complete:chat-data', () => {

View file

@ -45,6 +45,52 @@ export function generateUUID(input) {
}
export function sortScreens(screens) {
screens.sort((a,b) => {
if ( a.left < b.left ) return -1;
if ( a.left > b.left ) return 1;
if ( a.top < b.top ) return -1;
if ( a.top > b.top ) return 1;
return 0;
});
return screens;
}
export function matchScreen(screens, options) {
let match = undefined;
let mscore = 0;
for(let i = 0; i < screens.length; i++) {
const mon = screens[i];
if ( mon.label !== options.label )
continue;
let score = 1;
if ( options.left && options.left === mon.left )
score += 15;
if ( options.top && options.top === mon.top )
score += 15;
if ( options.width && options.width === mon.width )
score += 10;
if ( options.height && options.height === mon.height )
score += 10;
if ( options.index )
score -= Math.abs(options.index - i);
if ( score > mscore ) {
match = mon;
mscore = score;
}
}
return match;
}
export function has(object, key) {
return object ? HOP.call(object, key) : false;
}