mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-03 00:18:31 +00:00
4.1.2
* Fixed: Mentions not appearing in bold font. * Fixed: Highlighting messages in chat when opening a viewer card by clicking a mention that wasn't all lower-case. * Fixed: Detection of External add-ons when there is a name collision. * Changed: Do not report name collision errors. * Changed: Allow for multi-line add-on descriptions.
This commit is contained in:
parent
cdedf29d04
commit
535192d931
11 changed files with 382 additions and 243 deletions
|
@ -113,6 +113,7 @@ module.exports = {
|
|||
"warn",
|
||||
"tab"
|
||||
],
|
||||
"vue/valid-template-root": "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/require-prop-types": "off",
|
||||
"vue/require-default-prop": "off",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// ============================================================================
|
||||
|
||||
import Module from 'utilities/module';
|
||||
import { DEBUG, SERVER } from 'utilities/constants';
|
||||
import { SERVER } from 'utilities/constants';
|
||||
import { createElement } from 'utilities/dom';
|
||||
import { timeout, has } from 'utilities/object';
|
||||
|
||||
|
@ -38,7 +38,7 @@ export default class AddonManager extends Module {
|
|||
|
||||
this.settings.addUI('add-ons', {
|
||||
path: 'Add-Ons @{"description": "Add-Ons are additional modules, often written by other people, that can be loaded automatically by FrankerFaceZ to add new capabilities and behaviors to the extension and Twitch."}',
|
||||
component: 'add-ons',
|
||||
component: 'addon-list',
|
||||
title: 'Add-Ons',
|
||||
no_filter: true,
|
||||
|
||||
|
@ -129,7 +129,6 @@ export default class AddonManager extends Module {
|
|||
|
||||
addon.name_i18n = addon.name_i18n || `addon.${addon.id}.name`;
|
||||
addon.short_name_i18n = addon.short_name_i18n || `addon.${addon.id}.short_name`;
|
||||
addon.description_i18n = addon.description_i18n || `addon.${addon.id}.description`;
|
||||
addon.author_i18n = addon.author_i18n || `addon.${addon.id}.author`;
|
||||
|
||||
addon.dev = is_dev;
|
||||
|
@ -173,6 +172,9 @@ export default class AddonManager extends Module {
|
|||
}
|
||||
|
||||
isAddonEnabled(id) {
|
||||
if ( this.isAddonExternal(id) )
|
||||
return true;
|
||||
|
||||
return this.enabled_addons.includes(id);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
|||
FrankerFaceZ.Logger = Logger;
|
||||
|
||||
const VER = FrankerFaceZ.version_info = {
|
||||
major: 4, minor: 1, revision: 1,
|
||||
major: 4, minor: 1, revision: 2,
|
||||
commit: __git_commit__,
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
|
|
|
@ -209,13 +209,13 @@ export const Mentions = {
|
|||
},
|
||||
|
||||
render(token, createElement) {
|
||||
return (<span
|
||||
return (<strong
|
||||
class={`chat-line__message-mention${token.me ? ' ffz--mention-me' : ''}`}
|
||||
data-login={token.recipient}
|
||||
onClick={this.handleMentionClick}
|
||||
>
|
||||
{token.text}
|
||||
</span>)
|
||||
</strong>)
|
||||
},
|
||||
|
||||
process(tokens, msg, user) {
|
||||
|
@ -274,7 +274,7 @@ export const Mentions = {
|
|||
type: 'mention',
|
||||
text: `${at}${recipient}`,
|
||||
me: mentioned,
|
||||
recipient
|
||||
recipient: recipient ? recipient.toLowerCase() : ''
|
||||
});
|
||||
|
||||
if ( mentioned )
|
||||
|
|
|
@ -1,231 +0,0 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--addons tw-border-t tw-pd-y-1">
|
||||
<div v-if="reload" class="tw-mg-y-1 tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
||||
<h4 class="ffz-i-attention">
|
||||
{{ t('addon.refresh-needed', 'You must refresh your Twitch pages for some changes to take effect.') }}
|
||||
</h4>
|
||||
|
||||
<button
|
||||
class="tw-button tw-button--hollow tw-mg-t-05"
|
||||
@click="item.refresh()"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-arrows-cw" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.refresh', 'Refresh') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="! ready" class="tw-align-center tw-pd-1">
|
||||
<h1 class="tw-mg-5 ffz-i-zreknarf loading" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div
|
||||
v-for="addon in sorted_addons"
|
||||
v-if="shouldShow(addon)"
|
||||
:key="addon.id"
|
||||
class="ffz--addon-info tw-elevation-1 tw-c-background-base tw-border tw-pd-1 tw-mg-b-1 tw-flex tw-flex-nowrap"
|
||||
>
|
||||
<div class="tw-flex tw-flex-column tw-align-center tw-flex-shrink-0 tw-mg-r-1">
|
||||
<div class="tw-card-img--size-6 tw-overflow-hidden tw-mg-b-1">
|
||||
<img :src="addon.icon" class="tw-image">
|
||||
</div>
|
||||
|
||||
<div v-if="enabled[addon.id]" class="tw-mg-b-05 tw-pill ffz--pill-enabled">
|
||||
{{ t('addon.enabled', 'Enabled') }}
|
||||
</div>
|
||||
|
||||
<div v-if="addon.dev" class="tw-mg-b-05 tw-pill">
|
||||
{{ t('addon.dev', 'Developer') }}
|
||||
</div>
|
||||
|
||||
<div v-if="item.isAddonExternal(addon.id)" class="tw-mg-b-05 tw-pill">
|
||||
{{ t('addon.external', 'External') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-flex-grow-1">
|
||||
<div class="tw-border-b tw-mg-b-05">
|
||||
<h4>{{ t(addon.name_i18n, addon.name) }} <span class="tw-c-text-alt-2 tw-font-size-6">({{ addon.id }})</span></h4>
|
||||
<span class="tw-c-text-alt tw-mg-r-1">
|
||||
{{ t('addon.author', 'By: {author}', {
|
||||
author: t(addon.author_i18n, addon.author)
|
||||
}) }}
|
||||
</span>
|
||||
<span v-if="item.getVersion(addon.id)" class="tw-c-text-alt">
|
||||
{{ t('addon.version', 'Version {version}', {version: item.getVersion(addon.id)}) }}
|
||||
</span>
|
||||
</div>
|
||||
<markdown :source="t(addon.description_i18n, addon.description)" />
|
||||
|
||||
<div class="tw-mg-t-1 tw-pd-t-1 tw-border-t">
|
||||
<template v-if="enabled[addon.id]">
|
||||
<button
|
||||
v-if="item.isAddonExternal(addon.id)"
|
||||
disabled
|
||||
class="tw-button tw-button--hollow tw-button--disabled tw-tooltip-wrapper tw-mg-r-1"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-trash" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.disable', 'Disable') }}
|
||||
</span>
|
||||
<div class="tw-tooltip tw-tooltip--up tw-tooltip--align-left">
|
||||
{{ t('addon.external.description', 'This add-on has been loaded by an external script and cannot be disabled here.') }}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="tw-button tw-button--hollow ffz--button-disable tw-mg-r-1"
|
||||
@click="item.disableAddon(addon.id)"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-trash" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.disable', 'Disable') }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="addon.settings"
|
||||
class="tw-button tw-button--hollow tw-mg-r-1"
|
||||
@click="openSettings(addon)"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-cog" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.settings', 'Settings') }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button
|
||||
class="tw-button tw-button--hollow ffz--button-enable tw-mg-r-1"
|
||||
@click="item.enableAddon(addon.id)"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-download" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.enable', 'Enable') }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<a
|
||||
v-if="addon.website"
|
||||
:href="addon.website"
|
||||
:title="addon.website"
|
||||
class="tw-button tw-button--hollow tw-mg-r-1"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-link-ext" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.website', 'Website') }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['item', 'context', 'filter'],
|
||||
|
||||
data() {
|
||||
const enabled = {};
|
||||
|
||||
for(const addon of this.item.getAddons())
|
||||
enabled[addon.id] = this.item.isAddonEnabled(addon.id);
|
||||
|
||||
return {
|
||||
ready: this.item.isReady(),
|
||||
reload: this.item.isReloadRequired(),
|
||||
enabled
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
sorted_addons() {
|
||||
const addons = this.item.getAddons();
|
||||
|
||||
addons.sort((a, b) => {
|
||||
if ( a.sort < b.sort ) return -1;
|
||||
if ( b.sort < a.sort ) return 1;
|
||||
|
||||
const a_n = a.name.toLowerCase(),
|
||||
b_n = b.name.toLowerCase();
|
||||
|
||||
if ( a_n < b_n ) return -1;
|
||||
if ( b_n < a_n ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return addons;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.item.on(':ready', this.onReady, this);
|
||||
this.item.on(':addon-enabled', this.onEnabled, this);
|
||||
this.item.on(':addon-disabled', this.onDisabled, this);
|
||||
this.item.on(':reload-required', this.onReload, this);
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.item.off(':ready', this.onReady, this);
|
||||
this.item.off(':addon-enabled', this.onEnabled, this);
|
||||
this.item.off(':addon-disabled', this.onDisabled, this);
|
||||
this.item.off(':reload-required', this.onReload, this);
|
||||
},
|
||||
|
||||
methods: {
|
||||
shouldShow(addon) {
|
||||
if ( ! this.filter || ! this.filter.length )
|
||||
return true;
|
||||
|
||||
return addon.search_terms.includes(this.filter)
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.ready = true;
|
||||
|
||||
// Refresh the enabled cache.
|
||||
for(const addon of this.item.getAddons())
|
||||
this.enabled[addon.id] = this.item.isAddonEnabled(addon.id);
|
||||
},
|
||||
|
||||
onEnabled(id) {
|
||||
this.enabled[id] = true;
|
||||
},
|
||||
|
||||
onDisabled(id) {
|
||||
this.enabled[id] = false;
|
||||
},
|
||||
|
||||
onReload() {
|
||||
this.reload = true;
|
||||
},
|
||||
|
||||
openSettings(addon) {
|
||||
let key;
|
||||
if ( typeof addon.settings === 'string' )
|
||||
key = addon.settings;
|
||||
else
|
||||
key = `add_ons.${addon.id}`;
|
||||
|
||||
this.$emit('navigate', key);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
107
src/modules/main_menu/components/addon-list.vue
Normal file
107
src/modules/main_menu/components/addon-list.vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--addons tw-border-t tw-pd-y-1">
|
||||
<div v-if="reload" class="tw-mg-y-1 tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
||||
<h4 class="ffz-i-attention">
|
||||
{{ t('addon.refresh-needed', 'You must refresh your Twitch pages for some changes to take effect.') }}
|
||||
</h4>
|
||||
|
||||
<button
|
||||
class="tw-button tw-button--hollow tw-mg-t-05"
|
||||
@click="item.refresh()"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-arrows-cw" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.refresh', 'Refresh') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="! ready" class="tw-align-center tw-pd-1">
|
||||
<h1 class="tw-mg-5 ffz-i-zreknarf loading" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<addon
|
||||
v-for="addon in sorted_addons"
|
||||
v-if="shouldShow(addon)"
|
||||
:key="addon.id"
|
||||
:id="addon.id"
|
||||
:addon="addon"
|
||||
:item="item"
|
||||
@navigate="navigate"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['item', 'context', 'filter'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
ready: this.item.isReady(),
|
||||
reload: this.item.isReloadRequired()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
sorted_addons() {
|
||||
const addons = this.item.getAddons();
|
||||
|
||||
addons.sort((a, b) => {
|
||||
if ( a.sort < b.sort ) return -1;
|
||||
if ( b.sort < a.sort ) return 1;
|
||||
|
||||
const a_n = a.name.toLowerCase(),
|
||||
b_n = b.name.toLowerCase();
|
||||
|
||||
if ( a_n < b_n ) return -1;
|
||||
if ( b_n < a_n ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
return addons;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.item.on(':ready', this.onReady, this);
|
||||
this.item.on(':added', this.onAdded, this);
|
||||
this.item.on(':reload-required', this.onReload, this);
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.item.off(':ready', this.onReady, this);
|
||||
this.item.off(':added', this.onAdded, this);
|
||||
this.item.off(':reload-required', this.onReload, this);
|
||||
},
|
||||
|
||||
methods: {
|
||||
shouldShow(addon) {
|
||||
if ( ! this.filter || ! this.filter.length )
|
||||
return true;
|
||||
|
||||
return addon.search_terms.includes(this.filter)
|
||||
},
|
||||
|
||||
onAdded() {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.ready = true;
|
||||
},
|
||||
|
||||
onReload() {
|
||||
this.reload = true;
|
||||
},
|
||||
|
||||
navigate(...args) {
|
||||
this.$emit('navigate', ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
224
src/modules/main_menu/components/addon.vue
Normal file
224
src/modules/main_menu/components/addon.vue
Normal file
|
@ -0,0 +1,224 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--addon-info tw-elevation-1 tw-c-background-base tw-border tw-pd-1 tw-mg-b-1 tw-flex tw-flex-nowrap">
|
||||
<div class="tw-flex tw-flex-column tw-align-center tw-flex-shrink-0 tw-mg-r-1">
|
||||
<div class="tw-card-img--size-6 tw-overflow-hidden tw-mg-b-1">
|
||||
<img :src="icon" class="tw-image">
|
||||
</div>
|
||||
|
||||
<div v-if="external" class="tw-mg-b-05 tw-pill">
|
||||
{{ t('addon.external', 'External') }}
|
||||
</div>
|
||||
|
||||
<div v-else-if="enabled" class="tw-mg-b-05 tw-pill ffz--pill-enabled">
|
||||
{{ t('addon.enabled', 'Enabled') }}
|
||||
</div>
|
||||
|
||||
<div v-if="addon.dev" class="tw-mg-b-05 tw-pill">
|
||||
{{ t('addon.dev', 'Developer') }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tw-flex-grow-1">
|
||||
<div class="tw-border-b tw-mg-b-05">
|
||||
<h4>{{ t(addon.name_i18n, addon.name) }} <span class="tw-c-text-alt-2 tw-font-size-6">({{ addon.id }})</span></h4>
|
||||
<span class="tw-c-text-alt tw-mg-r-1">
|
||||
{{ t('addon.author', 'By: {author}', {
|
||||
author: t(addon.author_i18n, addon.author)
|
||||
}) }}
|
||||
</span>
|
||||
<span v-if="version" class="tw-c-text-alt">
|
||||
{{ t('addon.version', 'Version {version}', {version}) }}
|
||||
</span>
|
||||
</div>
|
||||
<markdown :source="show_description" />
|
||||
<a
|
||||
v-if="multi_line"
|
||||
href="#"
|
||||
class="tw-c-text-alt-2"
|
||||
@click.prevent="toggle"
|
||||
>
|
||||
<template v-if="expanded">{{ t('addon.show-less', '(Show Less)') }}</template>
|
||||
<template v-else>{{ t('addon.show-more', '(Show More)') }}</template>
|
||||
</a>
|
||||
|
||||
<div class="tw-mg-t-1 tw-pd-t-1 tw-border-t">
|
||||
<template v-if="enabled">
|
||||
<button
|
||||
v-if="external"
|
||||
disabled
|
||||
class="tw-button tw-button--hollow tw-button--disabled tw-tooltip-wrapper tw-mg-r-1"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-trash" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.disable', 'Disable') }}
|
||||
</span>
|
||||
<div class="tw-tooltip tw-tooltip--up tw-tooltip--align-left">
|
||||
{{ t('addon.external.description', 'This add-on has been loaded by an external script and cannot be disabled here.') }}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="tw-button tw-button--hollow ffz--button-disable tw-mg-r-1"
|
||||
@click="item.disableAddon(id)"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-trash" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.disable', 'Disable') }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="addon.settings"
|
||||
class="tw-button tw-button--hollow tw-mg-r-1"
|
||||
@click="openSettings()"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-cog" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.settings', 'Settings') }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button
|
||||
class="tw-button tw-button--hollow ffz--button-enable tw-mg-r-1"
|
||||
@click="item.enableAddon(id)"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-download" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.enable', 'Enable') }}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<a
|
||||
v-if="addon.website"
|
||||
:href="addon.website"
|
||||
:title="addon.website"
|
||||
class="tw-button tw-button--hollow tw-mg-r-1"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-link-ext" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.website', 'Website') }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['id', 'addon', 'item'],
|
||||
|
||||
data() {
|
||||
let description;
|
||||
if ( this.addon.description_i18n )
|
||||
description = this.t(this.addon.description_i18n, this.addon.description);
|
||||
else
|
||||
description = this.addon.description;
|
||||
|
||||
const lines = description.split(/\n/);
|
||||
|
||||
return {
|
||||
description,
|
||||
multi_line: lines.length > 1,
|
||||
first_line: lines[0],
|
||||
enabled: this.item.isAddonEnabled(this.id),
|
||||
external: this.item.isAddonExternal(this.id),
|
||||
version: this.item.getVersion(this.id),
|
||||
expanded: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
icon() {
|
||||
return this.addon.icon || 'https://cdn.frankerfacez.com/badge/2/4/solid'
|
||||
},
|
||||
|
||||
show_description() {
|
||||
if ( this.expanded )
|
||||
return this.description;
|
||||
return this.first_line;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.item.on('i18n:update', this.updateDescription, this);
|
||||
this.item.on(':added', this.refreshExternal, this);
|
||||
this.item.on(':ready', this.refreshExternal, this);
|
||||
this.item.on(':addon-enabled', this.onEnabled, this);
|
||||
this.item.on(':addon-disabled', this.onDisabled, this);
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.item.off('i18n:update', this.updateDescription, this);
|
||||
this.item.off(':added', this.refreshExternal, this);
|
||||
this.item.off(':ready', this.refreshExternal, this);
|
||||
this.item.off(':addon-enabled', this.onEnabled, this);
|
||||
this.item.off(':addon-disabled', this.onDisabled, this);
|
||||
},
|
||||
|
||||
methods: {
|
||||
refreshExternal() {
|
||||
this.external = this.item.isAddonExternal(this.id);
|
||||
this.version = this.item.getVersion(this.id);
|
||||
},
|
||||
|
||||
updateDescription() {
|
||||
if ( this.addon.description_i18n )
|
||||
this.description = this.t(this.addon.description_i18n, this.addon.description);
|
||||
else
|
||||
this.description = this.addon.description;
|
||||
|
||||
const lines = this.description.split(/\n/);
|
||||
|
||||
this.multi_line = lines.length > 1;
|
||||
this.first_line = lines[0];
|
||||
},
|
||||
|
||||
onEnabled(id) {
|
||||
if ( id === this.id )
|
||||
this.enabled = true;
|
||||
|
||||
this.version = this.item.getVersion(this.id);
|
||||
},
|
||||
|
||||
onDisabled(id) {
|
||||
if ( id === this.id )
|
||||
this.enabled = false;
|
||||
},
|
||||
|
||||
toggle() {
|
||||
this.expanded = ! this.expanded
|
||||
},
|
||||
|
||||
openSettings() {
|
||||
if ( typeof this.addon.settings === 'string' ) {
|
||||
this.$emit('navigate', this.addon.settings);
|
||||
return;
|
||||
}
|
||||
|
||||
const list = [`add_ons.${this.id}`];
|
||||
if ( this.addon.short_name )
|
||||
list.push(`add_ons.${this.addon.short_name.toSnakeCase()}`);
|
||||
if ( this.addon.name )
|
||||
list.push(`add_ons.${this.addon.name.toSnakeCase()}`);
|
||||
|
||||
this.$emit('navigate', ...list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -227,8 +227,14 @@ export default {
|
|||
this.displace.reinit();
|
||||
},
|
||||
|
||||
navigate(key) {
|
||||
let item = this.nav_keys[key];
|
||||
navigate(...keys) {
|
||||
let item;
|
||||
for(const key of keys) {
|
||||
item = this.nav_keys[key];
|
||||
if ( item )
|
||||
break;
|
||||
}
|
||||
|
||||
while(item && item.page)
|
||||
item = item.parent;
|
||||
|
||||
|
|
|
@ -141,7 +141,8 @@ export default class RavenLogger extends Module {
|
|||
'Zugriff verweigert',
|
||||
'freed script',
|
||||
'ffzenhancing',
|
||||
'dead object'
|
||||
'dead object',
|
||||
'Name Collision for Module'
|
||||
],
|
||||
sanitizeKeys: [
|
||||
/Token$/
|
||||
|
|
|
@ -64,6 +64,10 @@ export default class ViewerCards extends Module {
|
|||
}
|
||||
|
||||
updateStyle(login) {
|
||||
// Make sure we're dealing with lower-case logins.
|
||||
if ( typeof login === 'string' )
|
||||
login = login.toLowerCase();
|
||||
|
||||
this.last_login = login;
|
||||
if ( login && this.chat.context.get('chat.viewer-cards.highlight-chat') ) {
|
||||
let color = this.chat.context.get('chat.viewer-cards.color');
|
||||
|
|
|
@ -9,12 +9,37 @@ export class Addon extends Module {
|
|||
}
|
||||
|
||||
static register(id, info) {
|
||||
const ffz = FrankerFaceZ.get();
|
||||
ffz.register(`addon.${id}`, this);
|
||||
if ( typeof id === 'object' ) {
|
||||
info = id;
|
||||
id = info.id || undefined;
|
||||
}
|
||||
|
||||
if ( ! id ) {
|
||||
if ( this.name )
|
||||
id = this.name.toSnakeCase();
|
||||
else
|
||||
throw new Error(`Unable to register module without ID.`);
|
||||
}
|
||||
|
||||
if ( ! info && this.info )
|
||||
info = this.info;
|
||||
|
||||
const ffz = FrankerFaceZ.get();
|
||||
if ( info ) {
|
||||
info.id = id;
|
||||
ffz.addons.addAddon(info);
|
||||
}
|
||||
|
||||
try {
|
||||
ffz.register(`addon.${id}`, this);
|
||||
} catch(err) {
|
||||
if ( err.message && err.message.includes('Name Collision for Module') ) {
|
||||
const module = ffz.resolve(`addon.${id}`);
|
||||
if ( module )
|
||||
module.external = true;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue