mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-10-13 22:41:57 +00:00
4.3.1
* Fixed: Unable to see in-line chat action context menus in theater mode. * Changed: Add a new socket server to the list to take pressure off the others. * API Added: `debounce` method in `FrankerFaceZ.utilities.object`. * API Added: `<autocomplete>` Vue component for implementing text fields with auto-completion. * API Changed: Update localized Vue strings immediately when the i18n debug transformation changes. * API Changed: `<icon-picker />` now has a closed and open state. It doesn't always show the drawer of icons. * API Changed: Include the `vue-clickaway` mixin in everything.
This commit is contained in:
parent
aa25bff498
commit
21ee6fcfb7
23 changed files with 667 additions and 100 deletions
|
@ -73,7 +73,8 @@ export const WS_CLUSTERS = {
|
|||
['wss://andknuckles.frankerfacez.com/', 0.8],
|
||||
['wss://tuturu.frankerfacez.com/', 1],
|
||||
['wss://lilz.frankerfacez.com/', 1],
|
||||
['wss://yoohoo.frankerfacez.com/', 1]
|
||||
['wss://yoohoo.frankerfacez.com/', 1],
|
||||
['wss://pog.frankerfacez.com/', 1]
|
||||
],
|
||||
|
||||
Development: [
|
||||
|
|
|
@ -73,6 +73,52 @@ export function timeout(promise, delay) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a wrapper for a function that will only execute the function
|
||||
* a period of time after it has stopped being called.
|
||||
* @param {Function} fn The function to wrap.
|
||||
* @param {Integer} delay The time to wait, in milliseconds
|
||||
* @param {Boolean} immediate If immediate is true, trigger the function immediately rather than eventually.
|
||||
* @returns {Function} wrapped function
|
||||
*/
|
||||
export function debounce(fn, delay, immediate) {
|
||||
let timer;
|
||||
if ( immediate ) {
|
||||
const later = () => timer = null;
|
||||
if ( immediate === 2 )
|
||||
// Special Mode! Run immediately OR later.
|
||||
return function(...args) {
|
||||
if ( timer ) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
fn.apply(this, args); // eslint-disable-line no-invalid-this
|
||||
}, delay);
|
||||
} else {
|
||||
fn.apply(this, args); // eslint-disable-line no-invalid-this
|
||||
timer = setTimeout(later, delay);
|
||||
}
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
if ( ! timer )
|
||||
fn.apply(this, args); // eslint-disable-line no-invalid-this
|
||||
else
|
||||
clearTimeout(timer);
|
||||
|
||||
timer = setTimeout(later, delay);
|
||||
}
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
if ( timer )
|
||||
clearTimeout(timer);
|
||||
|
||||
timer = setTimeout(fn.bind(this, ...args), delay); // eslint-disable-line no-invalid-this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that a given asynchronous function is only called once
|
||||
* at a time.
|
||||
|
|
|
@ -72,7 +72,7 @@ export const DEFAULT_TYPES = {
|
|||
},
|
||||
|
||||
humantime(val, node) {
|
||||
return this.formatHumanTime(val, node.f);
|
||||
return this.formatHumanTime(val, 1, node.f);
|
||||
},
|
||||
|
||||
en_plural: v => v !== 1 ? 's' : ''
|
||||
|
@ -226,26 +226,28 @@ export default class TranslationCore {
|
|||
return thing;
|
||||
}
|
||||
|
||||
formatHumanTime(value, factor) {
|
||||
formatHumanTime(value, factor, round = false) {
|
||||
if ( value instanceof Date )
|
||||
value = (Date.now() - value.getTime()) / 1000;
|
||||
|
||||
value = Math.floor(value);
|
||||
factor = Number(factor) || 1;
|
||||
|
||||
const years = Math.floor((value * factor) / 31536000) / factor;
|
||||
const fn = round ? Math.round : Math.floor;
|
||||
|
||||
const years = fn((value * factor) / 31536000) / factor;
|
||||
if ( years >= 1 )
|
||||
return this.t('human-time.years', '{count,number} year{count,en_plural}', years);
|
||||
|
||||
const days = Math.floor((value %= 31536000) / 86400);
|
||||
const days = fn((value %= 31536000) / 86400);
|
||||
if ( days >= 1 )
|
||||
return this.t('human-time.days', '{count,number} day{count,en_plural}', days);
|
||||
|
||||
const hours = Math.floor((value %= 86400) / 3600);
|
||||
const hours = fn((value %= 86400) / 3600);
|
||||
if ( hours >= 1 )
|
||||
return this.t('human-time.hours', '{count,number} hour{count,en_plural}', hours);
|
||||
|
||||
const minutes = Math.floor((value %= 3600) / 60);
|
||||
const minutes = fn((value %= 3600) / 60);
|
||||
if ( minutes >= 1 )
|
||||
return this.t('human-time.minutes', '{count,number} minute{count,en_plural}', minutes);
|
||||
|
||||
|
@ -436,29 +438,33 @@ export default class TranslationCore {
|
|||
return this._processAST(...this._preTransform(key, phrase, options, use_default));
|
||||
}
|
||||
|
||||
formatNode(node, data, locale = null, out = null, ast = null) {
|
||||
if ( ! node || typeof node !== 'object' )
|
||||
return node;
|
||||
|
||||
if ( locale == null )
|
||||
locale = this.locale;
|
||||
|
||||
let val = get(node.v, data);
|
||||
if ( val == null )
|
||||
return null;
|
||||
|
||||
if ( node.t ) {
|
||||
if ( this.types[node.t] )
|
||||
return this.types[node.t].call(this, val, node, locale, out, ast, data);
|
||||
else if ( this.warn )
|
||||
this.warn(`Encountered unknown type "${node.t}" when formatting node.`);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
_processAST(ast, data, locale) {
|
||||
const out = [];
|
||||
|
||||
for(const node of ast) {
|
||||
if ( typeof node === 'string' ) {
|
||||
out.push(node);
|
||||
continue;
|
||||
|
||||
} else if ( ! node || typeof node !== 'object' )
|
||||
continue;
|
||||
|
||||
let val = get(node.v, data);
|
||||
if ( val == null )
|
||||
continue;
|
||||
|
||||
if ( node.t ) {
|
||||
if ( this.types[node.t] )
|
||||
val = this.types[node.t].call(this, val, node, locale, out, ast, data);
|
||||
else if ( this.warn )
|
||||
this.warn(`Encountered unknown type "${node.t}" when processing AST.`);
|
||||
}
|
||||
|
||||
if ( val )
|
||||
const val = this.formatNode(node, data, locale, out, ast);
|
||||
if( val != null )
|
||||
out.push(val);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,13 +19,25 @@ export class Vue extends Module {
|
|||
|
||||
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);
|
||||
const [
|
||||
ObserveVisibility,
|
||||
Clickaway,
|
||||
RavenVue,
|
||||
Components
|
||||
|
||||
] = await Promise.all([
|
||||
import(/* webpackChunkName: "vue" */ 'vue-observe-visibility'),
|
||||
import(/* webpackChunkName: "vue" */ 'vue-clickaway'),
|
||||
import(/* webpackChunkName: "vue" */ 'raven-js/plugins/vue'),
|
||||
import(/* webpackChunkName: "vue" */ 'src/std-components/index.js')
|
||||
]);
|
||||
|
||||
this.component(Components.default);
|
||||
|
||||
Vue.use(ObserveVisibility);
|
||||
Vue.mixin(Clickaway.mixin);
|
||||
|
||||
if ( ! DEBUG && this.root.raven )
|
||||
this.root.raven.addPlugin(RavenVue, Vue);
|
||||
|
@ -80,6 +92,11 @@ export class Vue extends Module {
|
|||
return t.i18n.tList(key, phrase, options);
|
||||
},
|
||||
|
||||
tNode_(node, data) {
|
||||
this.locale;
|
||||
return t.i18n.formatNode(node, data);
|
||||
},
|
||||
|
||||
setLocale(locale) {
|
||||
t.i18n.locale = locale;
|
||||
}
|
||||
|
@ -158,6 +175,9 @@ export class Vue extends Module {
|
|||
},
|
||||
tList(key, phrase, options) {
|
||||
return this.$i18n.tList_(key, phrase, options);
|
||||
},
|
||||
tNode(node, data) {
|
||||
return this.$i18n.tNode_(node, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue