mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 05:15:54 +00:00
* Added: Setting to hide the "Not Live" bar beneath videos and clips that appears when the channel is currently live. * Fixed: Handling of `https://www.twitch.tv/<channel>/clip/<slug>` urls for rich chat embeds and rich link tool-tips. * Fixed: Lower the priority of custom highlight terms so they will not break links. * Fixed: Holding multiple modifier keys to display in-line chat actions. * Fixed: Clean up out-dated avatar display setting for the directory. * API Added: Allow add-ons to access the Popper JS library via `FrankerFaceZ.utilities.popper`. * API Added: `<icon-picker />` Vue component for selecting an icon. * API Added: `<react-link />` Vue component for creating links that cause the React app to navigate without a page load. * API Added: `<t-list />` Vue component for translating text including Vue elements. * API Added: `maybeLoad(icon)` function for font awesome to only load the font if the icon is from font awesome. * API Added: `generateUUID()` function to `FrankerFaceZ.utilities.object` * API Added: The `vue-observe-visibility` module is now loaded with Vue and made available in all Vue contexts.
167 lines
No EOL
3.7 KiB
JavaScript
167 lines
No EOL
3.7 KiB
JavaScript
'use strict';
|
|
|
|
// ============================================================================
|
|
// Vue Library
|
|
// Loads Vue + Translation Shim
|
|
// ============================================================================
|
|
|
|
import Module from 'utilities/module';
|
|
import {has} from 'utilities/object';
|
|
import {DEBUG} from 'utilities/constants';
|
|
|
|
|
|
export class Vue extends Module {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this._components = {};
|
|
this.inject('i18n');
|
|
}
|
|
|
|
async onLoad() {
|
|
const Vue = window.ffzVue = this.Vue = (await import(/* webpackChunkName: "vue" */ 'vue')).default,
|
|
ObserveVisibility = await import(/* webpackChunkName: "vue" */ 'vue-observe-visibility'),
|
|
RavenVue = await import(/* webpackChunkName: "vue" */ 'raven-js/plugins/vue'),
|
|
components = this._components;
|
|
|
|
this.component((await import(/* webpackChunkName: "vue" */ 'src/std-components/index.js')).default);
|
|
|
|
Vue.use(ObserveVisibility);
|
|
|
|
if ( ! DEBUG && this.root.raven )
|
|
this.root.raven.addPlugin(RavenVue, Vue);
|
|
|
|
for(const key in components)
|
|
if ( has(components, key) )
|
|
Vue.component(key, components[key]);
|
|
|
|
this._components = null;
|
|
Vue.use(this);
|
|
}
|
|
|
|
component(name, component) {
|
|
if ( typeof name === 'function' ) {
|
|
for(const key of name.keys())
|
|
this.component(key.slice(2, key.length - 4), name(key).default);
|
|
|
|
} else if ( typeof name === 'object' ) {
|
|
for(const key in name)
|
|
if ( has(name, key) )
|
|
this.component(key, name[key]);
|
|
|
|
} else if ( this.Vue )
|
|
this.Vue.component(name, component);
|
|
|
|
else
|
|
this._components[name] = component;
|
|
}
|
|
|
|
install(vue) {
|
|
// This is a mess. I'm sure there's an easier way to tie the systems
|
|
// together. However, for now, this works.
|
|
|
|
const t = this;
|
|
if ( ! this._vue_i18n ) {
|
|
this._vue_i18n = new this.Vue({
|
|
data() {
|
|
return {
|
|
locale: t.i18n.locale,
|
|
phrases: {}
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
t_(key, phrase, options) {
|
|
this.locale && this.phrases[key];
|
|
return t.i18n.t(key, phrase, options);
|
|
},
|
|
|
|
tList_(key, phrase, options) {
|
|
this.locale && this.phrases[key];
|
|
return t.i18n.tList(key, phrase, options);
|
|
},
|
|
|
|
setLocale(locale) {
|
|
t.i18n.locale = locale;
|
|
}
|
|
}
|
|
});
|
|
|
|
this.on('i18n:transform', () => {
|
|
this._vue_i18n.locale = this.i18n.locale;
|
|
this._vue_i18n.phrases = {};
|
|
});
|
|
|
|
this.on('i18n:changed', () => {
|
|
this._vue_i18n.locale = this.i18n.locale;
|
|
this._vue_i18n.phrases = {};
|
|
});
|
|
|
|
this.on('i18n:loaded', keys => {
|
|
const i = this._vue_i18n,
|
|
p = i.phrases;
|
|
for(const key of keys)
|
|
i.$set(p, key, (p[key]||0) + 1);
|
|
});
|
|
|
|
vue.prototype.$i18n = this._vue_i18n;
|
|
}
|
|
|
|
vue.component('t-list', {
|
|
props: {
|
|
tag: {
|
|
required: false
|
|
},
|
|
phrase: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
default: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
data: {
|
|
type: Object,
|
|
required: false
|
|
}
|
|
},
|
|
|
|
render(createElement) {
|
|
return createElement(
|
|
this.tag || 'span',
|
|
this.$i18n.tList_(
|
|
this.phrase,
|
|
this.default,
|
|
Object.assign({}, this.data, this.$scopedSlots)
|
|
).map(out => {
|
|
if ( typeof out === 'function' )
|
|
return out();
|
|
return out;
|
|
})
|
|
);
|
|
}
|
|
})
|
|
|
|
vue.mixin({
|
|
methods: {
|
|
reactNavigate(url, event) {
|
|
const router = t.resolve('site.router');
|
|
if ( router && router.history ) {
|
|
if ( event ) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
router.history.push(url);
|
|
}
|
|
},
|
|
t(key, phrase, options) {
|
|
return this.$i18n.t_(key, phrase, options);
|
|
},
|
|
tList(key, phrase, options) {
|
|
return this.$i18n.tList_(key, phrase, options);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export default Vue; |