mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.71.0
* Added: Chat action to toggle a settings profile, for convenience. * Fixed: Missing message data in rich notices causing messages to not appear correctly when certain add-ons were enabled. Closes #1475 * API Changed: SettingsProfiles now have UUIDs that are stable and not reused, allowing things like chat actions to uniquely identify profiles.
This commit is contained in:
parent
10d35468a9
commit
a02c14d84c
10 changed files with 189 additions and 20 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.70.0",
|
||||
"version": "4.71.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -1218,8 +1218,6 @@ export default class Actions extends Module {
|
|||
if ( target._ffz_tooltip )
|
||||
target._ffz_tooltip.hide();
|
||||
|
||||
|
||||
|
||||
return data.definition.click.call(this, event, data);
|
||||
}
|
||||
|
||||
|
|
44
src/modules/chat/actions/components/edit-profile.vue
Normal file
44
src/modules/chat/actions/components/edit-profile.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<template lang="html">
|
||||
<div>
|
||||
<div class="tw-flex tw-align-items-center">
|
||||
<label for="edit_profile">
|
||||
{{ t('setting.actions.profile', 'Profile') }}
|
||||
</label>
|
||||
|
||||
<select
|
||||
id="edit_profile"
|
||||
v-model.trim="value.uuid"
|
||||
class="tw-border-radius-medium tw-font-size-6 tw-full-width ffz-select tw-pd-l-1 tw-pd-r-3 tw-pd-y-05 tw-mg-y-05"
|
||||
@input="$emit('input', value)"
|
||||
>
|
||||
<option
|
||||
v-for="profile in profiles"
|
||||
:key="profile.value"
|
||||
:value="profile.value"
|
||||
>{{ profile.i18n_key ? t(profile.i18n_key, profile.name) : profile.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['value', 'defaults'],
|
||||
|
||||
data() {
|
||||
const ffz = window.FrankerFaceZ.get(),
|
||||
settings = ffz?.resolve('settings'),
|
||||
profiles = settings?.__profiles;
|
||||
|
||||
return {
|
||||
profiles: profiles ? profiles.map(prof => ({
|
||||
value: prof.uuid,
|
||||
name: prof.name,
|
||||
i18n_key: prof.i18n_key
|
||||
})) : []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -650,6 +650,104 @@ export const whisper = {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Toggle Profile
|
||||
// ============================================================================
|
||||
|
||||
export const toggle_profile = {
|
||||
presets: [{
|
||||
appearance: {
|
||||
type: 'dynamic'
|
||||
}
|
||||
}],
|
||||
|
||||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-profile.vue'),
|
||||
|
||||
required_context: [],
|
||||
supports_dynamic: true,
|
||||
can_self: true,
|
||||
|
||||
title: 'Toggle Settings Profile',
|
||||
description(data) {
|
||||
const ffz = window.FrankerFaceZ.get(),
|
||||
settings = ffz?.resolve('settings'),
|
||||
profile = settings?.profile(data.options?.uuid);
|
||||
|
||||
if ( ! profile )
|
||||
return null;
|
||||
|
||||
return profile.i18n_key ? this.t(profile.i18n_key, profile.name) : profile.name;
|
||||
},
|
||||
description_i18n: null,
|
||||
|
||||
dynamicAppearance(ap, data) {
|
||||
const profile = this.settings.profile(data.options?.uuid);
|
||||
if ( ! profile )
|
||||
return {
|
||||
type: 'icon',
|
||||
icon: 'ffz-i-attention',
|
||||
color: ap.color
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'icon',
|
||||
icon: profile.toggled ? 'ffz-i-ok' : 'ffz-i-cancel',
|
||||
color: ap.color
|
||||
}
|
||||
},
|
||||
|
||||
tooltip(data) {
|
||||
const profile = this.settings.profile(data.options?.uuid);
|
||||
let name;
|
||||
if ( ! profile )
|
||||
name = 'invalid';
|
||||
else
|
||||
name = profile.i18n_key ? this.i18n.t(profile.i18n_key, profile.name) : profile.name;
|
||||
|
||||
return this.i18n.t('chat.actions.toggle_profile.tooltip', 'Toggle Profile: {name}', {
|
||||
name
|
||||
});
|
||||
},
|
||||
|
||||
click(event, data) {
|
||||
const profile = this.settings.profile(data.options?.uuid);
|
||||
if ( ! profile )
|
||||
return;
|
||||
|
||||
// TODO: Make dynamic appearance things update automatically so
|
||||
// we don't need this hack.
|
||||
|
||||
// Find any setting using this.
|
||||
const found = new Set;
|
||||
for(const ctx of ['inline', 'hover', 'user-context', 'room']) {
|
||||
const key = `chat.actions.${ctx}`,
|
||||
stuff = this.parent.context.get(key);
|
||||
|
||||
if ( Array.isArray(stuff) )
|
||||
for(const item of stuff) {
|
||||
if ( item.action === 'toggle_profile' ) {
|
||||
if ( item.options?.uuid === profile.uuid ) {
|
||||
found.add(ctx);
|
||||
this.parent.context.once(`changed:${key}`, () => found.delete(ctx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the profile.
|
||||
profile.toggled = ! profile.toggled;
|
||||
|
||||
// Now wait, and update any setting that didn't update.
|
||||
requestAnimationFrame(() => {
|
||||
for(const ctx of found) {
|
||||
this.parent.context.update(`chat.actions.${ctx}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Gift Subscription
|
||||
// ============================================================================
|
||||
|
@ -675,4 +773,4 @@ export const gift_sub = {
|
|||
Woop woop.
|
||||
</div>);
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { DEBUG } from 'utilities/constants';
|
||||
import Module, { GenericModule, buildAddonProxy } from 'utilities/module';
|
||||
import {deep_equals, has, debounce, deep_copy} from 'utilities/object';
|
||||
import {deep_equals, has, debounce, deep_copy, generateUUID} from 'utilities/object';
|
||||
import {PathNode, parse as parse_path} from 'utilities/path-parser';
|
||||
|
||||
import SettingsProfile from './profile';
|
||||
|
@ -132,6 +132,7 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
/** @internal */
|
||||
__profiles: SettingsProfile[];
|
||||
private __profile_ids: Record<number, SettingsProfile | null>;
|
||||
private __profile_uuids: Record<string, SettingsProfile | null>;
|
||||
|
||||
/**
|
||||
* Whether or not profiles have been disabled for this session
|
||||
|
@ -175,6 +176,7 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
this.__contexts = [];
|
||||
this.__profiles = [];
|
||||
this.__profile_ids = {};
|
||||
this.__profile_uuids = {};
|
||||
|
||||
this.ui_structures = new Map;
|
||||
this.definitions = new Map;
|
||||
|
@ -858,10 +860,10 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
|
||||
/**
|
||||
* Get an existing {@link SettingsProfile} instance.
|
||||
* @param {number} id - The id of the profile.
|
||||
* @param id - The id or uuid of the profile.
|
||||
*/
|
||||
profile(id: number): SettingsProfile | null {
|
||||
return this.__profile_ids[id] ?? null;
|
||||
profile(id: number | string): SettingsProfile | null {
|
||||
return this.__profile_uuids[id] ?? this.__profile_ids[id as number] ?? null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -871,10 +873,12 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
*/
|
||||
loadProfiles(suppress_events: boolean = false) {
|
||||
const old_profile_ids = this.__profile_ids,
|
||||
old_profile_uuids = this.__profile_uuids,
|
||||
old_profiles = this.__profiles,
|
||||
|
||||
profile_ids: Record<number, SettingsProfile> = this.__profile_ids = {},
|
||||
profiles: SettingsProfile[] = this.__profiles = [],
|
||||
profile_uuids: Record<string, SettingsProfile> = this.__profile_uuids = {},
|
||||
|
||||
// Create a set of actual IDs with a map from the profiles
|
||||
// list rather than just getting the keys from the ID map
|
||||
|
@ -899,6 +903,25 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
];
|
||||
}
|
||||
|
||||
// Update: Add UUIDs to all profiles.
|
||||
let need_save = false;
|
||||
|
||||
for(const profile of raw_profiles) {
|
||||
if ( ! profile.uuid ) {
|
||||
need_save = true;
|
||||
|
||||
if ( profile.i18n_key === SettingsProfile.Default.i18n_key )
|
||||
profile.uuid = SettingsProfile.Default.uuid as string;
|
||||
else if ( profile.i18n_key === SettingsProfile.Moderation.i18n_key )
|
||||
profile.uuid = SettingsProfile.Moderation.uuid as string;
|
||||
else
|
||||
profile.uuid = generateUUID();
|
||||
}
|
||||
}
|
||||
|
||||
if ( need_save )
|
||||
this.provider?.set('profiles', raw_profiles);
|
||||
|
||||
let reordered = false,
|
||||
changed = false;
|
||||
|
||||
|
@ -918,23 +941,13 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
if ( old_slot_id !== slot_id )
|
||||
reordered = true;
|
||||
|
||||
// Monkey patch to the new profile format...
|
||||
// Update: Probably safe to remove this, at this point.
|
||||
/*
|
||||
if ( profile_data.context && ! Array.isArray(profile_data.context) ) {
|
||||
if ( profile_data.context.moderator )
|
||||
profile_data.context = SettingsProfile.Moderation.context;
|
||||
else
|
||||
profile_data.context = null;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( old_profile && deep_equals(old_profile.data, profile_data, true) ) {
|
||||
// Did the order change?
|
||||
if ( old_slot_id !== slot_id )
|
||||
changed = true;
|
||||
|
||||
profiles.push(profile_ids[id] = old_profile);
|
||||
profile_uuids[old_profile.uuid] = old_profile;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -947,6 +960,7 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
new_ids.add(id);
|
||||
|
||||
profiles.push(new_profile);
|
||||
profile_uuids[new_profile.uuid] = new_profile;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
@ -994,11 +1008,13 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
}
|
||||
|
||||
options.id = id;
|
||||
options.uuid = generateUUID();
|
||||
|
||||
if ( ! options.name )
|
||||
options.name = `Unnamed Profile ${this.__profiles.length + 1}`;
|
||||
|
||||
const profile = this.__profile_ids[id] = new SettingsProfile(this, options);
|
||||
this.__profile_uuids[options.uuid] = profile;
|
||||
this.__profiles.unshift(profile);
|
||||
|
||||
profile.on('toggled', this._onProfileToggled, this);
|
||||
|
@ -1040,6 +1056,7 @@ export default class SettingsManager extends Module<'settings', SettingsEvents>
|
|||
profile.off('toggled', this._onProfileToggled, this);
|
||||
profile.clear();
|
||||
this.__profile_ids[id] = null;
|
||||
this.__profile_uuids[profile.uuid] = null;
|
||||
|
||||
const idx = this.__profiles.indexOf(profile);
|
||||
if ( idx !== -1 )
|
||||
|
|
|
@ -34,6 +34,7 @@ export default class SettingsProfile extends EventEmitter<ProfileEvents> {
|
|||
|
||||
static Default: Partial<SettingsProfileMetadata> = {
|
||||
id: 0,
|
||||
uuid: 'ffz_profile_default',
|
||||
name: 'Default Profile',
|
||||
i18n_key: 'setting.profiles.default',
|
||||
|
||||
|
@ -43,6 +44,7 @@ export default class SettingsProfile extends EventEmitter<ProfileEvents> {
|
|||
|
||||
static Moderation: Partial<SettingsProfileMetadata> = {
|
||||
id: 1,
|
||||
uuid: 'ffz_profile_moderation',
|
||||
name: 'Moderation',
|
||||
i18n_key: 'setting.profiles.moderation',
|
||||
|
||||
|
@ -78,6 +80,11 @@ export default class SettingsProfile extends EventEmitter<ProfileEvents> {
|
|||
*/
|
||||
id: number = -1;
|
||||
|
||||
/**
|
||||
* The unique ID for this profile. UUIDs should always be unique.
|
||||
*/
|
||||
uuid: string = null as any;
|
||||
|
||||
// Metadata
|
||||
|
||||
/**
|
||||
|
@ -172,6 +179,7 @@ export default class SettingsProfile extends EventEmitter<ProfileEvents> {
|
|||
return {
|
||||
id: this.id,
|
||||
//parent: this.parent,
|
||||
uuid: this.uuid,
|
||||
|
||||
name: this.name,
|
||||
i18n_key: this.i18n_key,
|
||||
|
@ -260,6 +268,7 @@ export default class SettingsProfile extends EventEmitter<ProfileEvents> {
|
|||
// We don't want to override general settings.
|
||||
delete data.profile.ephemeral;
|
||||
delete data.profile.id;
|
||||
delete data.profile.uuid;
|
||||
delete data.profile.name;
|
||||
delete data.profile.i18n_key;
|
||||
delete data.profile.hotkey;
|
||||
|
|
|
@ -280,6 +280,7 @@ export type ExportedBlobMetadata = {
|
|||
|
||||
export type SettingsProfileMetadata = {
|
||||
id: number;
|
||||
uuid: string;
|
||||
|
||||
name: string;
|
||||
i18n_key?: string | null;
|
||||
|
|
|
@ -833,6 +833,7 @@ export default class Input extends Module {
|
|||
out.push({
|
||||
current: input,
|
||||
replacement: inst.determineReplacement(cmd),
|
||||
description: cmd.description,
|
||||
element: inst.renderCommandSuggestion(cmd, i),
|
||||
group: cmd.ffz_group ?
|
||||
(Array.isArray(cmd.ffz_group) ? t.i18n.t(...cmd.ffz_group) : cmd.ffz_group)
|
||||
|
|
|
@ -108,6 +108,7 @@ export default class ChatLine extends Module {
|
|||
|
||||
if ( data.tokenize ) {
|
||||
const tokens = data.ffz_tokens = data.ffz_tokens || this.chat.tokenizeMessage({
|
||||
badges: {},
|
||||
message: text,
|
||||
id: msg.id,
|
||||
user: msg.user,
|
||||
|
|
|
@ -64,7 +64,7 @@ export function isValidShortcut(key: string) {
|
|||
*/
|
||||
export const generateUUID = crypto.randomUUID
|
||||
? () => crypto.randomUUID()
|
||||
: function generateUUID(input?: any) {
|
||||
: function generateUUID(input?: any): string {
|
||||
return input // if the placeholder was passed, return
|
||||
? ( // a random number from 0 to 15
|
||||
input ^ // unless b is 8,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue