mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 23:37:41 +00:00
Fix bug tokenizing cheers in messages. Try to fix any bugs getting a user for a room. Strip apollo 404 messages from Sentry. Don't show the expansion arrows in the emote menu when using a filter. Fix emoji tab completion a bit. 🐝
should not match 🍺
. Start using crossorigin for script serving. Add a debug transformation for localization.
This commit is contained in:
parent
730e2129e9
commit
e581b50b08
13 changed files with 133 additions and 18 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
<div class="list-header">4.0.0-beta2.18<span>@c8636911fc387a9f5e0c</span> <time datetime="2018-04-15">(2018-04-15)</time></div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Debugging setting for localization to help test UI issues, identify strings, and find strings that aren't localized.</li>
|
||||||
|
<li>Fixed: Issue tokenizing cheers in messages.</li>
|
||||||
|
<li>Fixed: Clean up tab-completion of emoji a bit more.</li>
|
||||||
|
<li>Changed: Start using `crossorigin="anonymous"` when loading scripts from our CDN.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="list-header">4.0.0-beta2.17<span>@dce1b0c5268bdd3fe086</span> <time datetime="2018-04-13">(2018-04-13)</time></div>
|
<div class="list-header">4.0.0-beta2.17<span>@dce1b0c5268bdd3fe086</span> <time datetime="2018-04-13">(2018-04-13)</time></div>
|
||||||
<ul class="chat-menu-content menu-side-padding">
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
<li>Fixed: An issue in automatic error reporting potentially swallowing errors when error reporting is disabled.</li>
|
<li>Fixed: An issue in automatic error reporting potentially swallowing errors when error reporting is disabled.</li>
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
script = document.createElement('script');
|
script = document.createElement('script');
|
||||||
|
|
||||||
script.id = 'ffz-script';
|
script.id = 'ffz-script';
|
||||||
|
script.crossOrigin = 'anonymous';
|
||||||
script.src = `${SERVER}/script/${BABEL}${FLAVOR}.js?_=${Date.now()}`;
|
script.src = `${SERVER}/script/${BABEL}${FLAVOR}.js?_=${Date.now()}`;
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
})();
|
})();
|
81
src/i18n.js
81
src/i18n.js
|
@ -7,10 +7,59 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import {SERVER} from 'utilities/constants';
|
import {SERVER} from 'utilities/constants';
|
||||||
import {has} from 'utilities/object';
|
import {pick_random, has} from 'utilities/object';
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
|
|
||||||
|
|
||||||
|
const FACES = ['(・`ω´・)', ';;w;;', 'owo', 'UwU', '>w<', '^w^'],
|
||||||
|
|
||||||
|
format_text = (phrase, token_regex, formatter) => {
|
||||||
|
const out = [];
|
||||||
|
|
||||||
|
let i = 0, match;
|
||||||
|
token_regex.lastIndex = 0;
|
||||||
|
|
||||||
|
while((match = token_regex.exec(phrase))) {
|
||||||
|
if ( match.index !== i )
|
||||||
|
out.push(formatter(phrase.slice(i, match.index)))
|
||||||
|
|
||||||
|
out.push(match[0]);
|
||||||
|
i = match.index + match[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( i < phrase.length )
|
||||||
|
out.push(formatter(phrase.slice(i)));
|
||||||
|
|
||||||
|
return out.join('')
|
||||||
|
},
|
||||||
|
|
||||||
|
owo = text => text
|
||||||
|
.replace(/(?:r|l)/g, 'w')
|
||||||
|
.replace(/(?:R|L)/g, 'W')
|
||||||
|
.replace(/n([aeiou])/g, 'ny$1')
|
||||||
|
.replace(/N([aeiou])/g, 'Ny$1')
|
||||||
|
.replace(/N([AEIOU])/g, 'NY$1')
|
||||||
|
.replace(/ove/g, 'uv')
|
||||||
|
.replace(/!+/g, ` ${pick_random(FACES)} `),
|
||||||
|
|
||||||
|
|
||||||
|
TRANSFORMATIONS = {
|
||||||
|
double: (key, text) =>
|
||||||
|
`${text} ${text}`,
|
||||||
|
|
||||||
|
upper: (key, text, opts, locale, token_regex) =>
|
||||||
|
format_text(text, token_regex, t => t.toUpperCase()),
|
||||||
|
|
||||||
|
lower: (key, text, opts, locale, token_regex) =>
|
||||||
|
format_text(text, token_regex, t => t.toLowerCase()),
|
||||||
|
|
||||||
|
append_key: (key, text) => `${text} (${key})`,
|
||||||
|
|
||||||
|
owo: (key, text, opts, locale, token_regex) =>
|
||||||
|
format_text(text, token_regex, t => owo(t))
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TranslationManager
|
// TranslationManager
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -28,6 +77,31 @@ export class TranslationManager extends Module {
|
||||||
//ja: { name: '日本語' }
|
//ja: { name: '日本語' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.settings.add('i18n.debug.transform', {
|
||||||
|
default: null,
|
||||||
|
ui: {
|
||||||
|
path: 'Debugging > Localization >> General',
|
||||||
|
title: 'Transformation',
|
||||||
|
description: 'Transform all localized strings to test string coverage as well as length.',
|
||||||
|
component: 'setting-select-box',
|
||||||
|
data: [
|
||||||
|
{value: null, title: 'Disabled'},
|
||||||
|
{value: 'upper', title: 'Upper Case'},
|
||||||
|
{value: 'lower', title: 'Lower Case'},
|
||||||
|
{value: 'append_key', title: 'Append Key'},
|
||||||
|
{value: 'double', title: 'Double'},
|
||||||
|
{value: 'owo', title: "owo what's this"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
changed: val => {
|
||||||
|
this._.transformation = TRANSFORMATIONS[val];
|
||||||
|
this.emit(':update')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.settings.add('i18n.locale', {
|
this.settings.add('i18n.locale', {
|
||||||
default: -1,
|
default: -1,
|
||||||
process: (ctx, val) => {
|
process: (ctx, val) => {
|
||||||
|
@ -65,6 +139,7 @@ export class TranslationManager extends Module {
|
||||||
awarn: (...args) => this.log.info(...args)
|
awarn: (...args) => this.log.info(...args)
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
|
this._.transformation = TRANSFORMATIONS[this.settings.get('i18n.debug.transform')];
|
||||||
this.locale = this.settings.get('i18n.locale');
|
this.locale = this.settings.get('i18n.locale');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +386,7 @@ export default class TranslationCore {
|
||||||
this.extend(options.phrases);
|
this.extend(options.phrases);
|
||||||
this.locale = options.locale || 'en';
|
this.locale = options.locale || 'en';
|
||||||
this.defaultLocale = options.defaultLocale || this.locale;
|
this.defaultLocale = options.defaultLocale || this.locale;
|
||||||
|
this.transformation = null;
|
||||||
|
|
||||||
const allowMissing = options.allowMissing ? transformPhrase : null;
|
const allowMissing = options.allowMissing ? transformPhrase : null;
|
||||||
this.onMissingKey = typeof options.onMissingKey === 'function' ? options.onMissingKey : allowMissing;
|
this.onMissingKey = typeof options.onMissingKey === 'function' ? options.onMissingKey : allowMissing;
|
||||||
|
@ -413,6 +489,9 @@ export default class TranslationCore {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.transformation )
|
||||||
|
p = this.transformation(key, p, opts, locale, this.tokenRegex);
|
||||||
|
|
||||||
return this.transformPhrase(p, opts, locale, this.tokenRegex, this.formatters);
|
return this.transformPhrase(p, opts, locale, this.tokenRegex, this.formatters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ class FrankerFaceZ extends Module {
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-beta2.17',
|
major: 4, minor: 0, revision: 0, extra: '-beta2.18',
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
||||||
|
|
|
@ -116,7 +116,7 @@ export default class Room {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getUser(id, login, no_create, no_login) {
|
getUser(id, login, no_create, no_login, error = false) {
|
||||||
if ( this.destroyed )
|
if ( this.destroyed )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -130,18 +130,29 @@ export default class Room {
|
||||||
else if ( this.users[login] && ! no_login )
|
else if ( this.users[login] && ! no_login )
|
||||||
user = this.users[login];
|
user = this.users[login];
|
||||||
|
|
||||||
else if ( no_create )
|
if ( user && user.destroyed )
|
||||||
|
user = null;
|
||||||
|
|
||||||
|
if ( ! user ) {
|
||||||
|
if ( no_create )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
else
|
|
||||||
user = new User(this.manager, this, id, login);
|
user = new User(this.manager, this, id, login);
|
||||||
|
}
|
||||||
|
|
||||||
if ( id && id !== user.id ) {
|
if ( id && id !== user.id ) {
|
||||||
// If the ID isn't what we expected, something is very wrong here.
|
// If the ID isn't what we expected, something is very wrong here.
|
||||||
// Blame name changes.
|
// Blame name changes.
|
||||||
if ( user.id )
|
if ( user.id ) {
|
||||||
|
this.manager.log.warn(`Data mismatch for user #${id} -- Stored ID: ${user.id} -- Login: ${login} -- Stored Login: ${user.login}`);
|
||||||
|
if ( error )
|
||||||
throw new Error('id mismatch');
|
throw new Error('id mismatch');
|
||||||
|
|
||||||
|
// Remove the old reference if we're going with this.
|
||||||
|
if ( this.user_ids[user.id] === user )
|
||||||
|
this.user_ids[user.id] = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise, we're just here to set the ID.
|
// Otherwise, we're just here to set the ID.
|
||||||
user._id = id;
|
user._id = id;
|
||||||
this.user_ids[id] = user;
|
this.user_ids[id] = user;
|
||||||
|
|
|
@ -389,6 +389,8 @@ export const CheerEmotes = {
|
||||||
token.hidden = true;
|
token.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text.push('');
|
||||||
|
|
||||||
} else
|
} else
|
||||||
text.push(segment);
|
text.push(segment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ export default class RavenLogger extends Module {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.raven = Raven;
|
this.raven = Raven;
|
||||||
|
|
|
@ -351,7 +351,7 @@ export default class EmoteMenu extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
clickHeading() {
|
clickHeading() {
|
||||||
if ( this.props.filter )
|
if ( this.props.filtered )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const collapsed = storage.get('emote-menu.collapsed') || [],
|
const collapsed = storage.get('emote-menu.collapsed') || [],
|
||||||
|
@ -440,7 +440,7 @@ export default class EmoteMenu extends Module {
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-flex-grow-1" />
|
<div class="tw-flex-grow-1" />
|
||||||
{data.source || 'FrankerFaceZ'}
|
{data.source || 'FrankerFaceZ'}
|
||||||
<figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />
|
{filtered ? '' : <figure class={`tw-pd-l-05 ffz-i-${collapsed ? 'left' : 'down'}-dir`} />}
|
||||||
</heading>) : null}
|
</heading>) : null}
|
||||||
{collapsed || this.renderBody(show_heading)}
|
{collapsed || this.renderBody(show_heading)}
|
||||||
</section>)
|
</section>)
|
||||||
|
|
|
@ -147,13 +147,14 @@ export default class TabCompletion extends Module {
|
||||||
getEmojiSuggestions(input, inst) {
|
getEmojiSuggestions(input, inst) {
|
||||||
let search = input.slice(1).toLowerCase();
|
let search = input.slice(1).toLowerCase();
|
||||||
const style = this.chat.context.get('chat.emoji.style'),
|
const style = this.chat.context.get('chat.emoji.style'),
|
||||||
results = [];
|
results = [],
|
||||||
|
has_colon = search.endsWith(':');
|
||||||
|
|
||||||
if ( search.endsWith(':') )
|
if ( has_colon )
|
||||||
search = search.slice(0,-1);
|
search = search.slice(0,-1);
|
||||||
|
|
||||||
for(const name in this.emoji.names)
|
for(const name in this.emoji.names)
|
||||||
if ( name.startsWith(search) ) {
|
if ( has_colon ? name === search : name.startsWith(search) ) {
|
||||||
const emoji = this.emoji.emoji[this.emoji.names[name]];
|
const emoji = this.emoji.emoji[this.emoji.names[name]];
|
||||||
if ( emoji && (style === 0 || emoji.has[style]) )
|
if ( emoji && (style === 0 || emoji.has[style]) )
|
||||||
results.push({
|
results.push({
|
||||||
|
|
|
@ -14,6 +14,7 @@ const BAD_ERRORS = [
|
||||||
'unable to load',
|
'unable to load',
|
||||||
'error internal',
|
'error internal',
|
||||||
'context deadline exceeded',
|
'context deadline exceeded',
|
||||||
|
'404',
|
||||||
'500',
|
'500',
|
||||||
'501',
|
'501',
|
||||||
'502',
|
'502',
|
||||||
|
|
|
@ -235,6 +235,17 @@ export function split_chars(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function pick_random(obj) {
|
||||||
|
if ( ! obj )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if ( ! Array.isArray(obj) )
|
||||||
|
return obj[pick_random(Object.keys(obj))]
|
||||||
|
|
||||||
|
return obj[Math.floor(Math.random() * obj.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class SourcedSet {
|
export class SourcedSet {
|
||||||
constructor() {
|
constructor() {
|
||||||
this._cache = [];
|
this._cache = [];
|
||||||
|
|
|
@ -73,7 +73,7 @@ export class Tooltip {
|
||||||
} else if ( this.live ) {
|
} else if ( this.live ) {
|
||||||
this._onMouseOver = e => {
|
this._onMouseOver = e => {
|
||||||
const target = e.target;
|
const target = e.target;
|
||||||
if ( target && target.classList.contains(this.cls) )
|
if ( target && target.classList && target.classList.contains(this.cls) )
|
||||||
this._enter(target);
|
this._enter(target);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -296,8 +296,8 @@ export class Tooltip {
|
||||||
|
|
||||||
|
|
||||||
// Add everything to the DOM and create the Popper instance.
|
// Add everything to the DOM and create the Popper instance.
|
||||||
this.parent.appendChild(el);
|
|
||||||
tip.popper = new Popper(target, el, pop_opts);
|
tip.popper = new Popper(target, el, pop_opts);
|
||||||
|
this.parent.appendChild(el);
|
||||||
tip.visible = true;
|
tip.visible = true;
|
||||||
|
|
||||||
if ( opts.onShow )
|
if ( opts.onShow )
|
||||||
|
|
|
@ -27,7 +27,8 @@ module.exports = {
|
||||||
output: {
|
output: {
|
||||||
chunkFilename: '[name].[chunkhash].js',
|
chunkFilename: '[name].[chunkhash].js',
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
jsonpFunction: 'ffzWebpackJsonp'
|
jsonpFunction: 'ffzWebpackJsonp',
|
||||||
|
crossOriginLoading: 'anonymous'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.ExtendedAPIPlugin()
|
new webpack.ExtendedAPIPlugin()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue