mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
* Fixed: The button to expand chat not appearing when using theater mode along with Swap Sidebars. * Fixed: Emote / link cards not appearing correctly when using moderator view. * API Added: `searchParentNode(input: InputNode, criteria: (node: ReactNode) => boolean)` method to `site.fine` for finding React internal nodes, useful for locating stateless components for extracting props. * API Added: `getRenderers()`, `getActions()` methods for `chat.actions` to get bulk data. * API Added: `getBadge(id: string | number)` method for `chat.badges` to get a badge. * API Added: `getUser(...).getBadges()` method for `chat` to get a user's badges. * API Added: The `action-editor` component for `chat.actions` has an additional `extra_appearance_editor` field for adding additional inputs. * API Changed: The `removeAction(...keys: string[])` and `removeRenderer(...keys: string[])` methods for `chat.actions` now support multiple keys to allow for more efficient removal.
821 lines
20 KiB
Vue
821 lines
20 KiB
Vue
<template lang="html">
|
|
<div class="ffz--widget ffz--chat-actions tw-border-t tw-pd-y-1">
|
|
<div
|
|
v-if="source && source !== profile"
|
|
class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1"
|
|
>
|
|
<span class="ffz-i-info" />
|
|
{{ t('setting.warn-inheritence', 'These values are being overridden by another profile and may not take effect.') }}
|
|
</div>
|
|
|
|
<div
|
|
v-if="(item.warn_icons || (has_icons && item.warn_icons !== false)) && context.mod_icons === false"
|
|
class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1"
|
|
>
|
|
<span class="ffz-i-info" />
|
|
{{ t('setting.actions.warn-hidden', 'You currently have Mod Icons turned off in your Twitch chat settings, so some actions might be hidden as a result. Use the settings menu in chat to toggle them.') }}
|
|
</div>
|
|
|
|
<div class="tw-pd-b-1 tw-border-b tw-mg-b-1">
|
|
<div class="tw-flex tw-flex-wrap tw-align-items-center ffz--inline">
|
|
{{ t('setting.actions.preview', 'Preview:') }}
|
|
|
|
<div class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="as_mod"
|
|
ref="as_mod"
|
|
:checked="is_moderator"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="as_mod" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.mod', 'As Moderator') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_msg" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="is_deleted"
|
|
ref="is_deleted"
|
|
:checked="is_deleted"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="is_deleted" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.deleted', 'Deleted Message') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_icons" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_mod_icons"
|
|
ref="with_mod_icons"
|
|
:checked="with_mod_icons"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_mod_icons" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.mod_icons', 'With Mod Icons') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_mode" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_slow"
|
|
ref="with_slow"
|
|
:checked="with_slow"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_slow" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.slow', 'Slow Mode') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_mode" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_emote"
|
|
ref="with_emote"
|
|
:checked="with_emote"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_emote" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.emote-only', 'Emote-Only') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_mode" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_subs"
|
|
ref="with_subs"
|
|
:checked="with_subs"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_subs" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.subs', 'Subs Only') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_mode" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_followers"
|
|
ref="with_followers"
|
|
:checked="with_followers"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_followers" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.followers', 'Followers-Only') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div v-if="has_mode" class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="with_r9k"
|
|
ref="with_r9k"
|
|
:checked="with_r9k"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="with_r9k" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.r9k', 'R9k Mode') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="tw-pd-x-1 ffz-checkbox">
|
|
<input
|
|
id="show_all"
|
|
ref="show_all"
|
|
:checked="show_all"
|
|
type="checkbox"
|
|
class="ffz-checkbox__input"
|
|
@change="onPreview"
|
|
>
|
|
|
|
<label for="show_all" class="ffz-checkbox__label">
|
|
<span class="tw-mg-l-1">
|
|
{{ t('setting.actions.preview.all', 'Show All') }}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
:data-user="JSON.stringify(sample_user)"
|
|
:data-room="JSON.stringify(sample_room)"
|
|
:data-message="JSON.stringify(sample_message)"
|
|
class="ffz-action-data tw-pd-t-1"
|
|
data-msg-id="1234-5678"
|
|
>
|
|
<div
|
|
v-if="! display.length"
|
|
class="tw-align-center tw-c-text-alt-2 tw-pd-05 tw-font-size-4"
|
|
>
|
|
{{ t('setting.actions.no-visible', 'no visible actions') }}
|
|
</div>
|
|
|
|
<div
|
|
v-for="(actions, idx) in display"
|
|
:key="idx"
|
|
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
|
|
v-else
|
|
:key="act.id"
|
|
:act="maybeDynamic(act.v)"
|
|
:process-color="color"
|
|
:renderers="data.renderers"
|
|
tooltip="true"
|
|
pad="true"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tw-flex tw-align-items-center tw-pd-b-05">
|
|
<div class="tw-flex-grow-1">
|
|
{{ t('setting.actions.drag', 'Drag actions to re-order them.') }}
|
|
</div>
|
|
<div
|
|
v-if="! maybe_clear"
|
|
v-on-clickaway="closeAdd"
|
|
class="tw-relative"
|
|
>
|
|
<button
|
|
class="tw-mg-l-1 tw-button tw-button--text"
|
|
@click="toggleAdd"
|
|
>
|
|
<span class="tw-button__text ffz-i-plus">
|
|
{{ t('setting.actions.new', 'New...') }}
|
|
</span>
|
|
<span class="tw-button__icon tw-button__icon--right">
|
|
<figure class="ffz-i-down-dir" />
|
|
</span>
|
|
</button>
|
|
<balloon
|
|
v-if="add_open"
|
|
color="background-alt-2"
|
|
dir="down-right"
|
|
:size="add_pasting ? 'md' : 'sm'"
|
|
>
|
|
<simplebar classes="ffz-mh-30">
|
|
<div v-if="add_pasting" class="tw-pd-1">
|
|
<div class="tw-flex tw-align-items-center">
|
|
<input
|
|
ref="paste"
|
|
:placeholder="t('setting.paste-json.json', '[json]')"
|
|
class="tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 ffz-input"
|
|
@keydown.enter="addFromJSON"
|
|
>
|
|
<button
|
|
class="tw-mg-l-05 tw-button"
|
|
@click="addFromJSON"
|
|
>
|
|
<span class="tw-button__text ffz-i-plus">
|
|
{{ t('setting.add', 'Add') }}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-else class="tw-pd-y-1">
|
|
<button
|
|
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
|
@click="preparePaste"
|
|
>
|
|
<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">
|
|
{{ t('setting.paste-json', 'Paste JSON') }}
|
|
</div>
|
|
</div>
|
|
</button>
|
|
<template v-for="(preset, idx) in presets">
|
|
<div
|
|
v-if="preset.divider"
|
|
:key="idx"
|
|
class="tw-mg-1 tw-border-b"
|
|
/>
|
|
<button
|
|
v-else
|
|
:key="idx"
|
|
:disabled="preset.disabled"
|
|
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
|
@click="add(preset.value)"
|
|
>
|
|
<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">
|
|
{{ preset.title_i18n ? t(preset.title_i18n, preset.title, preset) : preset.title }}
|
|
</div>
|
|
<action-preview
|
|
v-if="preset.appearance"
|
|
:act="maybeDynamic(preset)"
|
|
:process-color="color"
|
|
:renderers="data.renderers"
|
|
/>
|
|
</div>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</simplebar>
|
|
</balloon>
|
|
</div>
|
|
<button
|
|
v-if="! maybe_clear && strip_skip_val.length"
|
|
class="tw-mg-l-1 tw-button tw-button--text ffz-il-tooltip__container"
|
|
@click="maybe_clear = true"
|
|
>
|
|
<span class="tw-button__text ffz-i-trash">
|
|
{{ t('setting.delete-all', 'Delete All') }}
|
|
</span>
|
|
<span class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-right">
|
|
{{ t('setting.actions.delete-all', "Delete all of this profile's actions.") }}
|
|
</span>
|
|
</button>
|
|
<button
|
|
v-if="maybe_clear"
|
|
class="tw-mg-l-1 tw-button tw-button--text ffz-il-tooltip__container"
|
|
@click="doClear"
|
|
>
|
|
<span class="tw-button__text ffz-i-trash">
|
|
{{ t('setting.delete-all', 'Delete All') }}
|
|
</span>
|
|
<span class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-right">
|
|
{{ t('setting.actions.delete-all', "Delete all of this profile's actions.") }}
|
|
</span>
|
|
</button>
|
|
<button
|
|
v-if="maybe_clear"
|
|
class="tw-mg-l-1 tw-button tw-button--text ffz-il-tooltip__container"
|
|
@click="maybe_clear = false"
|
|
>
|
|
<span class="tw-button__text ffz-i-cancel">
|
|
{{ t('setting.cancel', 'Cancel') }}
|
|
</span>
|
|
</button>
|
|
<button
|
|
v-if="! strip_skip_val.length && has_default"
|
|
class="tw-mg-l-1 tw-button tw-button--text ffz-il-tooltip__container"
|
|
@click="populate"
|
|
>
|
|
<span class="tw-button__text ffz-i-trash">
|
|
{{ t('setting.actions.add-default', 'Add Defaults') }}
|
|
</span>
|
|
<span class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-right">
|
|
{{ t('setting.actions.add-default-tip', 'Add all of the default actions to this profile.') }}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div ref="list" class="ffz--action-list">
|
|
<div v-if="! val.length" class="tw-c-text-alt-2 tw-font-size-4 tw-align-center tw-c-text-alt-2 tw-pd-1">
|
|
{{ t('setting.actions.no-actions', 'no actions are defined in this profile') }}
|
|
|
|
<div class="tw-mg-t-1">
|
|
<button
|
|
class="tw-button tw-button--text ffz-il-tooltip__container"
|
|
@click="addSkip"
|
|
>
|
|
<span class="tw-button__text ffz-i-block">
|
|
{{ t('setting.actions.do-not-inherit', 'Do Not Inherit') }}
|
|
</span>
|
|
<span class="ffz-il-tooltip ffz-il-tooltip--down ffz-il-tooltip--align-center">
|
|
{{ t('setting.actions.do-not-inherit.desc', 'Do not inherit values from lower priority profiles or the defaults.') }}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<section v-for="act in val" :key="act.id">
|
|
<action-editor
|
|
:action="act"
|
|
:data="data"
|
|
:inline="item.inline"
|
|
:mod_icons="has_icons"
|
|
:context="item.context"
|
|
:extra_appearance="item.extra_appearance_editor"
|
|
:vuectx="context"
|
|
:modifiers="item.modifiers"
|
|
:hover_modifier="item.hover_modifier"
|
|
@remove="remove(act)"
|
|
@save="save(act, $event)"
|
|
/>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import SettingMixin from '../setting-mixin';
|
|
import Sortable from 'sortablejs';
|
|
import {deep_copy} from 'utilities/object';
|
|
|
|
let last_id = 0;
|
|
|
|
|
|
export default {
|
|
mixins: [SettingMixin],
|
|
props: ['item', 'context'],
|
|
|
|
data() {
|
|
return {
|
|
is_moderator: true,
|
|
with_mod_icons: true,
|
|
|
|
with_emote: false,
|
|
with_subs: false,
|
|
with_slow: false,
|
|
with_followers: false,
|
|
with_r9k: false,
|
|
|
|
is_staff: false,
|
|
is_deleted: false,
|
|
show_all: false,
|
|
|
|
maybe_clear: false,
|
|
add_open: false,
|
|
add_pasting: false
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
hasInheritance() {
|
|
for(const val of this.val)
|
|
if ( val.t === 'inherit' )
|
|
return true;
|
|
|
|
return false;
|
|
},
|
|
|
|
hasSkip() {
|
|
for(const val of this.val)
|
|
if ( val.t === 'skip' )
|
|
return true;
|
|
|
|
return false;
|
|
},
|
|
|
|
sample_user() {
|
|
return this.has_user ? {
|
|
displayName: 'SirStendec',
|
|
login: 'sirstendec',
|
|
id: 49399878,
|
|
color: '#008000'
|
|
} : null
|
|
},
|
|
|
|
sample_room() {
|
|
return this.has_room ? {
|
|
displayName: 'FrankerFaceZ',
|
|
login: 'frankerfacez',
|
|
id: 46622312
|
|
} : null
|
|
},
|
|
|
|
sample_message() {
|
|
return this.has_msg ? {
|
|
id: '46a473ee-a3c4-4556-a5ca-c0f1eac93ec0',
|
|
text: 'sirstendec: Please do not do that.'
|
|
} : null
|
|
},
|
|
|
|
has_icons() {
|
|
return this.item.mod_icons || this.item.inline
|
|
},
|
|
|
|
has_default() {
|
|
return this.default_value && this.default_value.length
|
|
},
|
|
|
|
has_user() {
|
|
return this.item.context && this.item.context.includes('user')
|
|
},
|
|
|
|
has_room() {
|
|
return this.item.context && this.item.context.includes('room')
|
|
},
|
|
|
|
has_mode() {
|
|
return this.item.context && this.item.context.includes('room-mode')
|
|
},
|
|
|
|
has_msg() {
|
|
return this.item.context && this.item.context.includes('message')
|
|
},
|
|
|
|
presets() {
|
|
const out = [],
|
|
contexts = this.item.context || [];
|
|
|
|
out.push({
|
|
disabled: this.hasInheritance,
|
|
|
|
title: 'Inheritance Point',
|
|
title_i18n: 'setting.inheritance',
|
|
value: {t: 'inherit'}
|
|
});
|
|
|
|
if ( ! this.item.inline ) {
|
|
out.push({
|
|
title: 'New Line',
|
|
title_i18n: 'setting.new-line',
|
|
value: {
|
|
v: {type: 'new-line'}
|
|
}
|
|
});
|
|
|
|
out.push({
|
|
title: 'Space (Small)',
|
|
title_i18n: 'setting.space-small',
|
|
value: {
|
|
v: {type: 'space-small'}
|
|
}
|
|
});
|
|
|
|
out.push({
|
|
title: 'Space (Expanding)',
|
|
title_i18n: 'setting.space',
|
|
value: {
|
|
v: {type: 'space'}
|
|
}
|
|
});
|
|
}
|
|
|
|
out.push({divider: true});
|
|
|
|
for(const key in this.data.actions) { // eslint-disable-line guard-for-in
|
|
const act = this.data.actions[key];
|
|
if ( act && act.presets ) {
|
|
if ( act.required_context ) {
|
|
let okay = true;
|
|
for(const ctx of act.required_context)
|
|
if ( ! contexts.includes(ctx) ) {
|
|
okay = false;
|
|
break;
|
|
}
|
|
|
|
if ( ! okay )
|
|
continue;
|
|
}
|
|
|
|
for(const preset of act.presets) {
|
|
if ( typeof act.title !== 'string' && ! preset.title )
|
|
continue;
|
|
|
|
out.push(Object.assign({
|
|
action: key,
|
|
title: act.title,
|
|
title_i18n: act.title_i18n || `chat.actions.${key}`,
|
|
value: {
|
|
v: Object.assign({
|
|
action: key
|
|
}, preset)
|
|
}
|
|
}, preset));
|
|
}
|
|
}
|
|
}
|
|
|
|
return out;
|
|
},
|
|
|
|
display() {
|
|
const out = [];
|
|
let current = [];
|
|
|
|
if ( this.val )
|
|
for(const val of this.val) {
|
|
if ( ! val.v )
|
|
continue;
|
|
|
|
const type = val.v.type;
|
|
if ( type === 'new-line' ) {
|
|
out.push(current);
|
|
current = [];
|
|
|
|
} else if ( type === 'space' || type === 'small-space' ) {
|
|
current.push(val.v);
|
|
|
|
} else if ( this.displayAction(val.v) )
|
|
current.push(val);
|
|
}
|
|
|
|
if ( current.length )
|
|
out.push(current);
|
|
|
|
return out;
|
|
},
|
|
|
|
strip_skip_val() {
|
|
return this.val.filter(x => x.t !== 'skip');
|
|
},
|
|
|
|
val() {
|
|
if ( ! this.has_value )
|
|
return [];
|
|
|
|
return this.value.map(x => {
|
|
x.id = x.id || `${Date.now()}-${Math.random()}-${last_id++}`;
|
|
return x;
|
|
});
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this._sortable = Sortable.create(this.$refs.list, {
|
|
draggable: 'section',
|
|
filter: 'button',
|
|
|
|
onUpdate: event => {
|
|
if ( event.newIndex === event.oldIndex )
|
|
return;
|
|
|
|
const new_val = Array.from(this.val);
|
|
new_val.splice(event.newIndex, 0, ...new_val.splice(event.oldIndex, 1));
|
|
|
|
this.set(new_val);
|
|
}
|
|
});
|
|
},
|
|
|
|
beforeDestroy() {
|
|
if ( this._sortable )
|
|
this._sortable.destroy();
|
|
|
|
this._sortable = null;
|
|
},
|
|
|
|
methods: {
|
|
doClear() {
|
|
this.maybe_clear = false;
|
|
this.clear();
|
|
},
|
|
|
|
closeAdd() {
|
|
this.add_open = false;
|
|
this.add_pasting = false;
|
|
},
|
|
|
|
toggleAdd() {
|
|
this.add_open = ! this.add_open;
|
|
this.add_pasting = false;
|
|
},
|
|
|
|
preparePaste() {
|
|
this.add_open = true;
|
|
this.add_pasting = true;
|
|
requestAnimationFrame(() => {
|
|
this.$refs.paste.focus()
|
|
});
|
|
},
|
|
|
|
addFromJSON() {
|
|
let value = this.$refs.paste.value;
|
|
this.closeAdd();
|
|
|
|
if ( value ) {
|
|
try {
|
|
value = JSON.parse(value);
|
|
} catch(err) {
|
|
alert(err); // eslint-disable-line no-alert
|
|
return;
|
|
}
|
|
|
|
if ( ! value.appearance )
|
|
value.appearance = {};
|
|
|
|
if ( ! value.appearance.type )
|
|
value.appearance.type = 'text';
|
|
|
|
if ( value.appearance.type === 'text' && ! value.appearance.text )
|
|
value.appearance.text = 'C';
|
|
|
|
if ( value.appearance.type === 'icon' && ! value.appearance.icon )
|
|
value.appearance.icon = 'ffz-i-ok';
|
|
|
|
if ( ! value.display )
|
|
value.display = {};
|
|
|
|
if ( ! value.action )
|
|
value.action = 'chat';
|
|
|
|
this.add({v: value});
|
|
}
|
|
},
|
|
|
|
populate() {
|
|
this.set(deep_copy(this.default_value));
|
|
},
|
|
|
|
addSkip() {
|
|
const vals = Array.from(this.val);
|
|
if(vals.length > 0)
|
|
return;
|
|
|
|
vals.push({
|
|
t: 'skip'
|
|
});
|
|
|
|
this.set(deep_copy(vals));
|
|
},
|
|
|
|
add(val) {
|
|
const vals = Array.from(this.val);
|
|
|
|
// Remove any skip entry.
|
|
let i = vals.length;
|
|
while(i--) {
|
|
if (vals[i]?.t === 'skip')
|
|
vals.splice(i, 1);
|
|
}
|
|
|
|
vals.push(val);
|
|
this.set(deep_copy(vals));
|
|
this.add_open = false;
|
|
},
|
|
|
|
remove(val) {
|
|
const vals = Array.from(this.val),
|
|
idx = vals.indexOf(val);
|
|
|
|
if ( idx !== -1 ) {
|
|
vals.splice(idx, 1);
|
|
if ( vals.length )
|
|
this.set(vals);
|
|
else
|
|
this.clear();
|
|
}
|
|
},
|
|
|
|
save(val, new_val) {
|
|
val.v = new_val;
|
|
this.set(deep_copy(this.val));
|
|
},
|
|
|
|
onPreview() {
|
|
this.show_all = this.$refs.show_all.checked;
|
|
this.is_moderator = this.$refs.as_mod.checked;
|
|
this.is_staff = false; //this.$refs.as_staff.checked;
|
|
this.with_mod_icons = this.has_icons && this.$refs.with_mod_icons.checked;
|
|
this.is_deleted = this.has_msg && this.$refs.is_deleted.checked;
|
|
|
|
this.with_emote = this.has_mode && this.$refs.with_emote.checked;
|
|
this.with_subs = this.has_mode && this.$refs.with_subs.checked;
|
|
this.with_slow = this.has_mode && this.$refs.with_slow.checked;
|
|
this.with_followers = this.has_mode && this.$refs.with_followers.checked;
|
|
this.with_r9k = this.has_mode && this.$refs.with_r9k.checked;
|
|
},
|
|
|
|
displayAction(action) {
|
|
if ( ! action.appearance )
|
|
return false;
|
|
|
|
const disp = action.display;
|
|
if ( ! disp || this.show_all )
|
|
return true;
|
|
|
|
if ( disp.disable )
|
|
return false;
|
|
|
|
if ( disp.mod != null && disp.mod !== this.is_moderator )
|
|
return false;
|
|
|
|
if ( disp.mod_icons != null && disp.mod_icons !== this.with_mod_icons )
|
|
return false;
|
|
|
|
if ( disp.staff != null && disp.staff !== this.is_staff )
|
|
return false;
|
|
|
|
if ( disp.deleted != null && disp.deleted !== this.is_deleted )
|
|
return false;
|
|
|
|
if ( this.has_mode ) {
|
|
if ( disp.emoteOnly != null && disp.emoteOnly !== this.with_emote )
|
|
return false;
|
|
|
|
if ( disp.slowMode != null && disp.slowMode !== this.with_slow )
|
|
return false;
|
|
|
|
if ( disp.subsMode != null && disp.subsMode !== this.with_subs )
|
|
return false;
|
|
|
|
if ( disp.r9kMode != null && disp.r9kMode !== this.with_r9k )
|
|
return false;
|
|
|
|
if ( disp.followersOnly != null && disp.followersOnly !== this.with_followers )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
maybeDynamic(data) {
|
|
let ap = data.appearance;
|
|
if (ap?.type === 'dynamic') {
|
|
const act = this.data.actions[data.action],
|
|
ffz = this.context.getFFZ(),
|
|
actions = ffz && ffz.resolve('chat.actions');
|
|
|
|
const out = actions && act?.dynamicAppearance && act.dynamicAppearance
|
|
.call(actions, deep_copy(ap), data, this.sample_message, this.sample_room, this.sample_user, this.with_mod_icons);
|
|
if ( out )
|
|
return Object.assign({}, data, {appearance: out});
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
color(input) {
|
|
if ( ! input )
|
|
return input;
|
|
|
|
return this.data.color(input)
|
|
}
|
|
}
|
|
}
|
|
|
|
</script>
|