mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 15:27:43 +00:00
The Report Your Errors Update
* Add automatic error reporting with Sentry.io * Filter a bunch of bad errors from showing up on Sentry * Add module.hasModule method. * Fix deep_copy * Fix disallow mouse interaction with extensions * Add some new icons to the icon font for mod cards * Allow Ctrl-Shift-Clicking emotes. * Rarity sorting for experiments and unset display for unused experiments.
This commit is contained in:
parent
e3a7e3b64d
commit
d7a07a5612
32 changed files with 575 additions and 83 deletions
|
@ -1,3 +1,18 @@
|
||||||
|
<div class="list-header">4.0.0-beta2.6<span>@b85fa005ec1f3929cdd8</span> <time datetime="2018-04-11">(2018-04-11)</time></div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Changed: Filter a bunch of errors out from Sentry logging.</li>
|
||||||
|
<li>API Added: <code>module.hasModule(name)</code> method to test if a module is already installed.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="list-header">4.0.0-beta2.5<span>@b3fb24504616675ad2b9</span> <time datetime="2018-04-11">(2018-04-11)</time></div>
|
||||||
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
|
<li>Added: Automatic error reporting using Sentry.</li>
|
||||||
|
<li>Added: Rarity sorting for the Experiments debugging information.</li>
|
||||||
|
<li>Changed: Allow Ctrl-Shift-Clicking emotes to open their information pages.</li>
|
||||||
|
<li>Fixed: <code>deep_copy</code> erroneously thinking some objects were recursive.</li>
|
||||||
|
<li>Fixed: The option to disallow mouse mouse interaction with extensions was not functioning.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div class="list-header">4.0.0-beta2.4<span>@b3fb24504616675ad2b9</span> <time datetime="2018-04-10">(2018-04-10)</time></div>
|
<div class="list-header">4.0.0-beta2.4<span>@b3fb24504616675ad2b9</span> <time datetime="2018-04-10">(2018-04-10)</time></div>
|
||||||
<ul class="chat-menu-content menu-side-padding">
|
<ul class="chat-menu-content menu-side-padding">
|
||||||
<li>Added: Debugging > Experiments for viewing active experiment information.</li>
|
<li>Added: Debugging > Experiments for viewing active experiment information.</li>
|
||||||
|
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -7294,6 +7294,11 @@
|
||||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
|
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"raven-js": {
|
||||||
|
"version": "3.24.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.1.tgz",
|
||||||
|
"integrity": "sha512-p+e+yoQbq4YgXDonYIRZNL/Kov6+t5L0UNEHZeYNzjOkNNCXcwQ1Vi3pulgGBaOjqXNipkFsbpmnH7YI+GPSjw=="
|
||||||
|
},
|
||||||
"raw-body": {
|
"raw-body": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
"path-to-regexp": "^2.2.0",
|
"path-to-regexp": "^2.2.0",
|
||||||
"popper.js": "^1.14.2",
|
"popper.js": "^1.14.2",
|
||||||
|
"raven-js": "^3.24.1",
|
||||||
"sortablejs": "^1.7.0",
|
"sortablejs": "^1.7.0",
|
||||||
"vue": "^2.5.16",
|
"vue": "^2.5.16",
|
||||||
"vue-clickaway": "^2.1.0",
|
"vue-clickaway": "^2.1.0",
|
||||||
|
|
Binary file not shown.
|
@ -76,6 +76,14 @@
|
||||||
|
|
||||||
<glyph glyph-name="arrows-cw" unicode="" d="M843 261q0-3 0-4-36-150-150-243t-267-93q-81 0-157 31t-136 88l-72-72q-11-11-25-11t-25 11-11 25v250q0 14 11 25t25 11h250q14 0 25-11t10-25-10-25l-77-77q40-36 90-57t105-20q74 0 139 37t104 99q6 10 30 66 4 13 16 13h107q8 0 13-6t5-12z m14 446v-250q0-14-10-25t-26-11h-250q-14 0-25 11t-10 25 10 25l77 77q-82 77-194 77-75 0-140-37t-104-99q-6-10-29-66-5-13-17-13h-111q-7 0-13 6t-5 12v4q36 150 151 243t268 93q81 0 158-31t137-88l72 72q11 11 25 11t26-11 10-25z" horiz-adv-x="857.1" />
|
<glyph glyph-name="arrows-cw" unicode="" d="M843 261q0-3 0-4-36-150-150-243t-267-93q-81 0-157 31t-136 88l-72-72q-11-11-25-11t-25 11-11 25v250q0 14 11 25t25 11h250q14 0 25-11t10-25-10-25l-77-77q40-36 90-57t105-20q74 0 139 37t104 99q6 10 30 66 4 13 16 13h107q8 0 13-6t5-12z m14 446v-250q0-14-10-25t-26-11h-250q-14 0-25 11t-10 25 10 25l77 77q-82 77-194 77-75 0-140-37t-104-99q-6-10-29-66-5-13-17-13h-111q-7 0-13 6t-5 12v4q36 150 151 243t268 93q81 0 158-31t137-88l72 72q11 11 25 11t26-11 10-25z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="ignore" unicode="" d="M813 141v-291l-233 194c-26-4-53-6-80-6-242 0-437 153-437 343 0 190 195 344 437 344s438-154 438-344c0-93-48-178-125-240z m-125 272h-375v-63h375v63z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="block" unicode="" d="M732 352q0 90-48 164l-421-420q76-50 166-50 62 0 118 25t96 65 65 97 24 119z m-557-167l421 421q-75 50-167 50-83 0-153-40t-110-111-41-153q0-91 50-167z m682 167q0-88-34-168t-91-137-137-92-166-34-167 34-137 92-91 137-34 168 34 167 91 137 137 91 167 34 166-34 137-91 91-137 34-167z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="pin" unicode="" d="M573 37q0-23-15-38t-37-15q-21 0-37 16l-169 169-315-236 236 315-168 169q-24 23-12 56 14 32 48 32 157 0 270 57 90 45 151 171 9 24 36 32t50-13l208-209q21-23 14-50t-32-36q-127-63-172-152-56-110-56-268z" horiz-adv-x="834" />
|
||||||
|
|
||||||
|
<glyph glyph-name="pin-outline" unicode="" d="M856 554q30-30 30-73t-30-75q-16-16-36-24-106-51-144-125-51-102-51-246 0-45-29-74t-75-30q-47 0-75 30l-167 169-279-199 199 279-169 168q-20 18-27 49t5 64q27 65 96 65 146 0 246 50l11 7q69 37 118 141 7 20 21 34 30 30 74 29t74-31z m-273-250q53 107 196 175l-205 211q-69-146-176-200l-13-6q-120-57-281-57l416-416q0 168 63 293z" horiz-adv-x="886" />
|
||||||
|
|
||||||
<glyph glyph-name="twitter" unicode="" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
|
<glyph glyph-name="twitter" unicode="" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
<glyph glyph-name="gauge" unicode="" d="M214 207q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
<glyph glyph-name="gauge" unicode="" d="M214 207q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
||||||
|
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -18,6 +18,12 @@ const OVERRIDE_COOKIE = 'experiment_overrides',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// We want to import this so that the file is included in the output.
|
||||||
|
// We don't load using this because we might want a newer file from the
|
||||||
|
// server.
|
||||||
|
import EXPERIMENTS from 'file-loader?name=[name].[hash].[ext]!./experiments.json'; // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Experiment Manager
|
// Experiment Manager
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -31,6 +37,9 @@ export default class ExperimentManager extends Module {
|
||||||
this.settings.addUI('experiments', {
|
this.settings.addUI('experiments', {
|
||||||
path: 'Debugging > Experiments',
|
path: 'Debugging > Experiments',
|
||||||
component: 'experiments',
|
component: 'experiments',
|
||||||
|
|
||||||
|
unique_id: () => this.unique_id,
|
||||||
|
|
||||||
ffz_data: () => deep_copy(this.experiments),
|
ffz_data: () => deep_copy(this.experiments),
|
||||||
twitch_data: () => deep_copy(this.getTwitchExperiments()),
|
twitch_data: () => deep_copy(this.getTwitchExperiments()),
|
||||||
|
|
||||||
|
@ -66,7 +75,7 @@ export default class ExperimentManager extends Module {
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = await fetch(`${SERVER}/static/experiments.json?_=${Date.now()}`).then(r =>
|
data = await fetch(`${SERVER}/script/experiments.json?_=${Date.now()}`).then(r =>
|
||||||
r.ok ? r.json() : null);
|
r.ok ? r.json() : null);
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -179,12 +188,11 @@ export default class ExperimentManager extends Module {
|
||||||
|
|
||||||
_rebuildTwitchKey(key, is_set, new_val) {
|
_rebuildTwitchKey(key, is_set, new_val) {
|
||||||
const core = this.resolve('site').getCore(),
|
const core = this.resolve('site').getCore(),
|
||||||
exps = core.experiments;
|
exps = core.experiments,
|
||||||
|
|
||||||
if ( ! has(exps.assignments, key) )
|
old_val = has(exps.assignments, key) ?
|
||||||
return;
|
exps.assignments[key] :
|
||||||
|
undefined;
|
||||||
const old_val = exps.assignments[key];
|
|
||||||
|
|
||||||
if ( old_val !== new_val ) {
|
if ( old_val !== new_val ) {
|
||||||
const value = is_set ? new_val : old_val;
|
const value = is_set ? new_val : old_val;
|
||||||
|
|
19
src/main.js
19
src/main.js
|
@ -1,5 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import RavenLogger from './raven';
|
||||||
|
|
||||||
import Logger from 'utilities/logging';
|
import Logger from 'utilities/logging';
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
|
|
||||||
|
@ -24,7 +26,14 @@ class FrankerFaceZ extends Module {
|
||||||
this.__state = 0;
|
this.__state = 0;
|
||||||
this.__modules.core = this;
|
this.__modules.core = this;
|
||||||
|
|
||||||
this.log = new Logger(this);
|
// ========================================================================
|
||||||
|
// Error Reporting and Logging
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
//if ( ! DEBUG )
|
||||||
|
this.inject('raven', RavenLogger);
|
||||||
|
|
||||||
|
this.log = new Logger(null, null, null, this.raven);
|
||||||
this.core_log = this.log.get('core');
|
this.core_log = this.log.get('core');
|
||||||
|
|
||||||
this.log.info(`FrankerFaceZ v${VER} (build ${VER.build})`);
|
this.log.info(`FrankerFaceZ v${VER} (build ${VER.build})`);
|
||||||
|
@ -86,18 +95,12 @@ class FrankerFaceZ extends Module {
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* eslint class-methods-use-this: off */
|
|
||||||
api(...args) {
|
|
||||||
return this._api.create(...args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-beta2.4',
|
major: 4, minor: 0, revision: 0, extra: '-beta2.7',
|
||||||
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' : ''}`
|
||||||
|
|
|
@ -195,6 +195,35 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
const provider = ds.provider;
|
const provider = ds.provider;
|
||||||
|
|
||||||
|
if ( event.shiftKey && this.parent.context.get('chat.click-emotes') ) {
|
||||||
|
let url;
|
||||||
|
|
||||||
|
if ( provider === 'twitch' )
|
||||||
|
url = `https://twitchemotes.com/emotes/${ds.id}`;
|
||||||
|
|
||||||
|
else if ( provider === 'ffz' ) {
|
||||||
|
const emote_set = this.emote_sets[ds.set],
|
||||||
|
emote = emote_set && emote_set.emotes[ds.id];
|
||||||
|
|
||||||
|
if ( ! emote )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( emote.click_url )
|
||||||
|
url = emote.click_url;
|
||||||
|
|
||||||
|
else if ( ! emote_set.source )
|
||||||
|
url = `https://www.frankerfacez.com/emoticons/${emote.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( url ) {
|
||||||
|
const win = window.open();
|
||||||
|
win.opener = null;
|
||||||
|
win.location = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ( event[MOD_KEY] ) {
|
if ( event[MOD_KEY] ) {
|
||||||
// Favoriting Emotes
|
// Favoriting Emotes
|
||||||
let source, id;
|
let source, id;
|
||||||
|
@ -225,35 +254,6 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( event.shiftKey && this.parent.context.get('chat.click-emotes') ) {
|
|
||||||
let url;
|
|
||||||
|
|
||||||
if ( provider === 'twitch' )
|
|
||||||
url = `https://twitchemotes.com/emotes/${ds.id}`;
|
|
||||||
|
|
||||||
else if ( provider === 'ffz' ) {
|
|
||||||
const emote_set = this.emote_sets[ds.set],
|
|
||||||
emote = emote_set && emote_set.emotes[ds.id];
|
|
||||||
|
|
||||||
if ( ! emote )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( emote.click_url )
|
|
||||||
url = emote.click_url;
|
|
||||||
|
|
||||||
else if ( ! emote_set.source )
|
|
||||||
url = `https://www.frankerfacez.com/emoticons/${emote.id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( url ) {
|
|
||||||
const win = window.open();
|
|
||||||
win.opener = null;
|
|
||||||
win.location = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -303,11 +303,15 @@ export default class Chat extends Module {
|
||||||
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 )
|
||||||
return null;
|
user = null;
|
||||||
|
|
||||||
|
if ( ! user ) {
|
||||||
|
if ( no_create )
|
||||||
|
return null;
|
||||||
else
|
else
|
||||||
user = new User(this, null, id, login);
|
user = new User(this, null, 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.
|
||||||
|
@ -358,11 +362,15 @@ export default class Chat extends Module {
|
||||||
else if ( this.rooms[login] && ! no_login )
|
else if ( this.rooms[login] && ! no_login )
|
||||||
room = this.rooms[login];
|
room = this.rooms[login];
|
||||||
|
|
||||||
else if ( no_create )
|
if ( room && room.destroyed )
|
||||||
return null;
|
room = null;
|
||||||
|
|
||||||
|
if ( ! room ) {
|
||||||
|
if ( no_create )
|
||||||
|
return null;
|
||||||
else
|
else
|
||||||
room = new Room(this, id, login);
|
room = new Room(this, id, login);
|
||||||
|
}
|
||||||
|
|
||||||
if ( id && id !== room.id ) {
|
if ( id && id !== room.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.
|
||||||
|
|
|
@ -70,8 +70,8 @@ export default class Room {
|
||||||
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this.manager.room_ids[this.id] === this )
|
if ( this.manager.room_ids[this._id] === this )
|
||||||
this.manager.room_ids[this.id] = null;
|
this.manager.room_ids[this._id] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,14 @@ export default class User {
|
||||||
this.manager.emotes.unrefSet(set_id);
|
this.manager.emotes.unrefSet(set_id);
|
||||||
|
|
||||||
this.emote_sets = null;
|
this.emote_sets = null;
|
||||||
|
|
||||||
|
const parent = this.room || this.manager;
|
||||||
|
|
||||||
|
if ( this._login && parent.users[this._login] === this )
|
||||||
|
parent.users[this._login] = null;
|
||||||
|
|
||||||
|
if ( parent.user_ids[this._id] === this )
|
||||||
|
parent.user_ids[this._id] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
<template lang="html">
|
<template lang="html">
|
||||||
<div class="ffz--experiments tw-pd-t-05">
|
<div class="ffz--experiments tw-pd-t-05">
|
||||||
<div class="tw-pd-b-1 tw-mg-b-2 tw-border-b">
|
<div class="tw-pd-b-1 tw-mg-b-1 tw-border-b">
|
||||||
{{ t('settings.experiments.about', 'This feature allows you to override experiment values. Please note that, for most experiments, you may have to refresh the page for your changes to take effect.') }}
|
{{ t('settings.experiments.about', 'This feature allows you to override experiment values. Please note that, for most experiments, you may have to refresh the page for your changes to take effect.') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-mg-b-2 tw-flex tw-align-items-center">
|
||||||
|
<div class="tw-flex-grow-1">
|
||||||
|
{{ t('settings.experiments.unique-id', 'Unique ID: %{id}', {id: unique_id}) }}
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
ref="sort_select"
|
||||||
|
class="tw-mg-x-05 tw-select tw-display-line tw-width-auto"
|
||||||
|
@change="onSort"
|
||||||
|
>
|
||||||
|
<option :selected="sort_by === 0">{{ t('settings.experiments.sort-name', 'Sort By: Name') }}</option>
|
||||||
|
<option :selected="sort_by === 1">{{ t('settings.experiments.sort-rarity', 'Sort By: Rarity') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 class="tw-mg-b-1">
|
<h3 class="tw-mg-b-1">
|
||||||
{{ t('settings.experiments.ffz', 'FrankerFaceZ Experiments') }}
|
{{ t('settings.experiments.ffz', 'FrankerFaceZ Experiments') }}
|
||||||
</h3>
|
</h3>
|
||||||
|
@ -34,7 +48,7 @@
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:selected="i.value === exp.value"
|
:selected="i.value === exp.value"
|
||||||
>
|
>
|
||||||
{{ t('settings.exepriments.entry', '%{value} (weight: %{weight})', i) }}
|
{{ t('settings.experiments.entry', '%{value} (weight: %{weight})', i) }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@ -97,6 +111,12 @@
|
||||||
class="tw-mg-05 tw-select tw-display-line tw-width-auto"
|
class="tw-mg-05 tw-select tw-display-line tw-width-auto"
|
||||||
@change="onTwitchChange($event)"
|
@change="onTwitchChange($event)"
|
||||||
>
|
>
|
||||||
|
<option
|
||||||
|
v-if="exp.in_use === false"
|
||||||
|
:selected="exp.default"
|
||||||
|
>
|
||||||
|
{{ t('settings.experiments.unset', 'unset') }}
|
||||||
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="(i, idx) in exp.groups"
|
v-for="(i, idx) in exp.groups"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
|
@ -136,6 +156,8 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
sort_by: 1,
|
||||||
|
unique_id: this.item.unique_id(),
|
||||||
ffz_data: this.item.ffz_data(),
|
ffz_data: this.item.ffz_data(),
|
||||||
twitch_data: this.item.twitch_data()
|
twitch_data: this.item.twitch_data()
|
||||||
}
|
}
|
||||||
|
@ -147,6 +169,10 @@ export default {
|
||||||
const exp = this.ffz_data[key];
|
const exp = this.ffz_data[key];
|
||||||
this.$set(exp, 'value', this.item.getAssignment(key));
|
this.$set(exp, 'value', this.item.getAssignment(key));
|
||||||
this.$set(exp, 'default', ! this.item.hasOverride(key));
|
this.$set(exp, 'default', ! this.item.hasOverride(key));
|
||||||
|
|
||||||
|
exp.total = exp.groups.reduce((a,b) => a + b.weight, 0);
|
||||||
|
this.calculateRarity(exp);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const key in this.twitch_data)
|
for(const key in this.twitch_data)
|
||||||
|
@ -157,6 +183,8 @@ export default {
|
||||||
|
|
||||||
exp.in_use = this.item.usingTwitchExperiment(key);
|
exp.in_use = this.item.usingTwitchExperiment(key);
|
||||||
exp.remainder = `v: ${exp.v}, t: ${exp.t}`;
|
exp.remainder = `v: ${exp.v}, t: ${exp.t}`;
|
||||||
|
exp.total = exp.groups.reduce((a,b) => a + b.weight, 0);
|
||||||
|
this.calculateRarity(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.item.on(':changed', this.valueChanged, this);
|
this.item.on(':changed', this.valueChanged, this);
|
||||||
|
@ -169,6 +197,17 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
calculateRarity(exp) {
|
||||||
|
let rarity;
|
||||||
|
for(const group of exp.groups)
|
||||||
|
if ( group.value === exp.value ) {
|
||||||
|
rarity = group.weight / exp.total;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$set(exp, 'rarity', rarity);
|
||||||
|
},
|
||||||
|
|
||||||
sorted(data) {
|
sorted(data) {
|
||||||
const out = Object.entries(data).map(x => ({key: x[0], exp: x[1]}));
|
const out = Object.entries(data).map(x => ({key: x[0], exp: x[1]}));
|
||||||
|
|
||||||
|
@ -179,11 +218,20 @@ export default {
|
||||||
if ( a_use && ! b_use ) return -1;
|
if ( a_use && ! b_use ) return -1;
|
||||||
if ( ! a_use && b_use ) return 1;
|
if ( ! a_use && b_use ) return 1;
|
||||||
|
|
||||||
|
if ( this.sort_by === 1 ) {
|
||||||
|
const a_r = a.exp.rarity,
|
||||||
|
b_r = b.exp.rarity;
|
||||||
|
|
||||||
|
if ( a_r < b_r ) return -1;
|
||||||
|
if ( a_r > b_r ) return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const a_n = a.exp.name.toLowerCase(),
|
const a_n = a.exp.name.toLowerCase(),
|
||||||
b_n = b.exp.name.toLowerCase();
|
b_n = b.exp.name.toLowerCase();
|
||||||
|
|
||||||
if ( a_n < b_n ) return -1;
|
if ( a_n < b_n ) return -1;
|
||||||
if ( a_n > b_n ) return 1;
|
if ( a_n > b_n ) return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -204,6 +252,10 @@ export default {
|
||||||
exp.default = ! this.item.hasTwitchOverride(key);
|
exp.default = ! this.item.hasTwitchOverride(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSort() {
|
||||||
|
this.sort_by = this.$refs.sort_select.selectedIndex;
|
||||||
|
},
|
||||||
|
|
||||||
onChange(event) {
|
onChange(event) {
|
||||||
const el = event.target,
|
const el = event.target,
|
||||||
idx = el.selectedIndex,
|
idx = el.selectedIndex,
|
||||||
|
@ -223,8 +275,9 @@ export default {
|
||||||
key = el.dataset.key;
|
key = el.dataset.key;
|
||||||
|
|
||||||
const exp = this.twitch_data[key],
|
const exp = this.twitch_data[key],
|
||||||
|
offset = exp.in_use ? 0 : 1,
|
||||||
groups = exp && exp.groups,
|
groups = exp && exp.groups,
|
||||||
entry = groups && groups[idx];
|
entry = groups && groups[idx - offset];
|
||||||
|
|
||||||
if ( entry )
|
if ( entry )
|
||||||
this.item.setTwitchOverride(key, entry.value);
|
this.item.setTwitchOverride(key, entry.value);
|
||||||
|
@ -235,6 +288,7 @@ export default {
|
||||||
if ( exp ) {
|
if ( exp ) {
|
||||||
exp.value = value;
|
exp.value = value;
|
||||||
exp.default = ! this.item.hasOverride(key);
|
exp.default = ! this.item.hasOverride(key);
|
||||||
|
this.calculateRarity(exp);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -243,6 +297,7 @@ export default {
|
||||||
if ( exp ) {
|
if ( exp ) {
|
||||||
exp.value = value;
|
exp.value = value;
|
||||||
exp.default = ! this.item.hasTwitchOverride(key);
|
exp.default = ! this.item.hasTwitchOverride(key);
|
||||||
|
this.calculateRarity(exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -439,6 +439,11 @@ export default class Metadata extends Module {
|
||||||
el.disabled = maybe_call(def.disabled, this, data);
|
el.disabled = maybe_call(def.disabled, this, data);
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
this.log.capture(err, {
|
||||||
|
tags: {
|
||||||
|
metadata: key
|
||||||
|
}
|
||||||
|
});
|
||||||
this.log.error(`Error rendering metadata for ${key}`, err);
|
this.log.error(`Error rendering metadata for ${key}`, err);
|
||||||
return destroy();
|
return destroy();
|
||||||
}
|
}
|
||||||
|
|
174
src/raven.js
Normal file
174
src/raven.js
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* global FrankerFaceZ: false */
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Raven Logging
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
import {DEBUG, SENTRY_ID} from 'utilities/constants';
|
||||||
|
import {has} from 'utilities/object';
|
||||||
|
import Module from 'utilities/module';
|
||||||
|
|
||||||
|
import Raven from 'raven-js';
|
||||||
|
|
||||||
|
const BAD_URLS = [
|
||||||
|
'hls.ttvnw.net',
|
||||||
|
'trowel.twitch.tv',
|
||||||
|
'client-event-reporter.twitch.tv',
|
||||||
|
'.twitch.tv/gql',
|
||||||
|
'spade.twitch.tv'
|
||||||
|
];
|
||||||
|
|
||||||
|
const BAD_QUERIES = [
|
||||||
|
'ChannelPage_SetSessionStatus'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Raven Logger
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export default class RavenLogger extends Module {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
|
||||||
|
this.inject('settings');
|
||||||
|
this.inject('site');
|
||||||
|
this.inject('experiments');
|
||||||
|
|
||||||
|
this.raven = Raven;
|
||||||
|
|
||||||
|
Raven.config(SENTRY_ID, {
|
||||||
|
autoBreadcrumbs: {
|
||||||
|
console: false
|
||||||
|
},
|
||||||
|
release: FrankerFaceZ.version_info.toString(),
|
||||||
|
environment: DEBUG ? 'development' : 'production',
|
||||||
|
captureUnhandledRejections: false,
|
||||||
|
ignoreErrors: [
|
||||||
|
'InvalidAccessError',
|
||||||
|
'out of memory'
|
||||||
|
],
|
||||||
|
whitelistUrls: [
|
||||||
|
/cdn\.frankerfacez\.com/
|
||||||
|
],
|
||||||
|
sanitizeKeys: [
|
||||||
|
/Token$/
|
||||||
|
],
|
||||||
|
breadcrumbCallback(crumb) {
|
||||||
|
if ( crumb.category === 'gql' ) {
|
||||||
|
for(const matcher of BAD_QUERIES)
|
||||||
|
if ( crumb.message.includes(matcher) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( crumb.type === 'http' ) {
|
||||||
|
const url = crumb.data.url;
|
||||||
|
for(const matcher of BAD_URLS)
|
||||||
|
if ( url.includes(matcher) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
shouldSendCallback(data) {
|
||||||
|
if ( data.message && data.messages.includes('raven-js/') )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).install();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnable() {
|
||||||
|
const user = this.site.getUser();
|
||||||
|
if ( user )
|
||||||
|
this.raven.setUserContext({
|
||||||
|
id: user.id,
|
||||||
|
username: user.login
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buildExtra() {
|
||||||
|
const context = this.settings.main_context,
|
||||||
|
chat_context = this.resolve('chat').context;
|
||||||
|
|
||||||
|
|
||||||
|
const settings = {},
|
||||||
|
chat_settings = {},
|
||||||
|
modules = {},
|
||||||
|
experiments = {},
|
||||||
|
twitch_experiments = {},
|
||||||
|
out = {
|
||||||
|
experiments,
|
||||||
|
twitch_experiments,
|
||||||
|
modules,
|
||||||
|
settings,
|
||||||
|
settings_context: context._context,
|
||||||
|
chat_settings,
|
||||||
|
chat_context: chat_context._context
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const key in this.__modules)
|
||||||
|
if ( has(this.__modules, key) ) {
|
||||||
|
const mod = this.__modules[key];
|
||||||
|
modules[key] = [
|
||||||
|
mod.loaded ? 'loaded' : mod.loading ? 'loading' : 'unloaded',
|
||||||
|
mod.enabled ? 'enabled' : mod.enabling ? 'enabling' : 'disabled'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const [key, value] of context.__cache.entries())
|
||||||
|
settings[key] = value;
|
||||||
|
|
||||||
|
for(const [key, value] of chat_context.__cache.entries())
|
||||||
|
chat_settings[key] = value;
|
||||||
|
|
||||||
|
for(const [key, value] of Object.entries(this.experiments.getTwitchExperiments()))
|
||||||
|
if ( this.experiments.usingTwitchExperiment(key) )
|
||||||
|
twitch_experiments[value.name] = this.experiments.getTwitchAssignment(key);
|
||||||
|
|
||||||
|
for(const key of Object.keys(this.experiments.experiments))
|
||||||
|
experiments[key] = this.experiments.getAssignment(key);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
buildTags() {
|
||||||
|
const core = this.site.getCore(),
|
||||||
|
out = {};
|
||||||
|
|
||||||
|
out.build = __webpack_hash__;
|
||||||
|
|
||||||
|
if ( core )
|
||||||
|
out.twitch_build = core.config.buildID;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addPlugin(...args) { return this.raven.addPlugin(...args) }
|
||||||
|
setUserContext(...args) { return this.raven.setUserContext(...args) }
|
||||||
|
|
||||||
|
captureException(exc, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts.extra = Object.assign(this.buildExtra(), opts.extra);
|
||||||
|
opts.tags = Object.assign(this.buildTags(), opts.tags);
|
||||||
|
|
||||||
|
return this.raven.captureException(exc, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
captureMessage(msg, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts.extra = Object.assign(this.buildExtra(), opts.extra);
|
||||||
|
opts.tags = Object.assign(this.buildTags(), opts.tags);
|
||||||
|
|
||||||
|
return this.raven.captureMessage(msg, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
captureBreadcrumb(...args) { return this.raven.captureBreadcrumb(...args) }
|
||||||
|
}
|
|
@ -516,6 +516,7 @@ export default class ChatHook extends Module {
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
t.log.capture(err, {extra: e});
|
||||||
return old_resub.call(i, e);
|
return old_resub.call(i, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,6 +534,7 @@ export default class ChatHook extends Module {
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
t.log.capture(err, {extra: e});
|
||||||
return old_ritual.call(i, e);
|
return old_ritual.call(i, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,9 @@ export default class RichContent extends Module {
|
||||||
}, data));
|
}, data));
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
if ( err.message !== 'timeout' )
|
||||||
|
t.log.capture(err);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
loaded: true,
|
loaded: true,
|
||||||
error: true,
|
error: true,
|
||||||
|
|
|
@ -79,6 +79,8 @@ export default class Scroller extends Module {
|
||||||
ffz_errors: errs + 1,
|
ffz_errors: errs + 1,
|
||||||
ffz_total_errors: (this.state.ffz_total_errors||0) + 1
|
ffz_total_errors: (this.state.ffz_total_errors||0) + 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.log.capture(err, {extra: info});
|
||||||
t.log.info('Error within Chat', err, info, errs);
|
t.log.info('Error within Chat', err, info, errs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
.player .extension-overlay__iframe,
|
||||||
.player .extension-overlay {
|
.player .extension-overlay {
|
||||||
pointer-events: none !important;
|
pointer-events: none !important;
|
||||||
}
|
}
|
|
@ -8,6 +8,28 @@
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {has, get} from 'utilities/object';
|
import {has, get} from 'utilities/object';
|
||||||
|
|
||||||
|
|
||||||
|
const BAD_ERRORS = [
|
||||||
|
'timeout',
|
||||||
|
'unable to load',
|
||||||
|
'error internal',
|
||||||
|
'Internal Server Error'
|
||||||
|
];
|
||||||
|
|
||||||
|
function skip_error(err) {
|
||||||
|
for(const m of BAD_ERRORS)
|
||||||
|
if ( err.message.includes(m) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class GQLError extends Error {
|
||||||
|
constructor(err) {
|
||||||
|
super(`${err.message}; Location: ${err.locations}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class Apollo extends Module {
|
export default class Apollo extends Module {
|
||||||
constructor(...args) {
|
constructor(...args) {
|
||||||
super(...args);
|
super(...args);
|
||||||
|
@ -17,6 +39,7 @@ export default class Apollo extends Module {
|
||||||
|
|
||||||
this.inject('..web_munch');
|
this.inject('..web_munch');
|
||||||
this.inject('..fine');
|
this.inject('..fine');
|
||||||
|
//this.inject('core');
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -62,6 +85,10 @@ export default class Apollo extends Module {
|
||||||
if ( ! this.enabled )
|
if ( ! this.enabled )
|
||||||
return forward(operation);
|
return forward(operation);
|
||||||
|
|
||||||
|
let vars = operation.variables;
|
||||||
|
if ( ! Object.keys(vars).length )
|
||||||
|
vars = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ONLY do this if we've hooked query init, thus letting us ignore certain issues
|
// ONLY do this if we've hooked query init, thus letting us ignore certain issues
|
||||||
// that would cause Twitch to show lovely "Error loading data" messages everywhere.
|
// that would cause Twitch to show lovely "Error loading data" messages everywhere.
|
||||||
|
@ -69,6 +96,14 @@ export default class Apollo extends Module {
|
||||||
this.apolloPreFlight(operation);
|
this.apolloPreFlight(operation);
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
this.log.capture(err, {
|
||||||
|
tags: {
|
||||||
|
operation: operation.operationName
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
variables: vars
|
||||||
|
}
|
||||||
|
});
|
||||||
this.log.error('Error running Pre-Flight', err, operation);
|
this.log.error('Error running Pre-Flight', err, operation);
|
||||||
return forward(operation);
|
return forward(operation);
|
||||||
}
|
}
|
||||||
|
@ -80,9 +115,45 @@ export default class Apollo extends Module {
|
||||||
try {
|
try {
|
||||||
out.subscribe({
|
out.subscribe({
|
||||||
next: result => {
|
next: result => {
|
||||||
|
if ( result.errors ) {
|
||||||
|
const name = operation.operationName;
|
||||||
|
if ( name.includes('FFZ') || has(this.modifiers, name) || has(this.post_modifiers, name) ) {
|
||||||
|
for(const err of result.errors) {
|
||||||
|
if ( skip_error(err) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.log.capture(new GQLError(err), {
|
||||||
|
tags: {
|
||||||
|
operation: operation.operationName
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
variables: vars
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log.crumb({
|
||||||
|
level: 'info',
|
||||||
|
category: 'gql',
|
||||||
|
message: `${operation.operationName} [${result.extensions && result.extensions.durationMilliseconds || '??'}ms]`,
|
||||||
|
data: {
|
||||||
|
variables: vars,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.apolloPostFlight(result);
|
this.apolloPostFlight(result);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
this.log.capture(err, {
|
||||||
|
tags: {
|
||||||
|
operation: operation.operationName
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
variables: vars
|
||||||
|
}
|
||||||
|
});
|
||||||
this.log.error('Error running Post-Flight', err, result);
|
this.log.error('Error running Post-Flight', err, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +168,14 @@ export default class Apollo extends Module {
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
this.log.capture(err, {
|
||||||
|
tags: {
|
||||||
|
operation: operation.operationName
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
variables: vars
|
||||||
|
}
|
||||||
|
});
|
||||||
this.log.error('Link Error', err);
|
this.log.error('Link Error', err);
|
||||||
observer.error(err);
|
observer.error(err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,14 +61,14 @@ export default class FineRouter extends Module {
|
||||||
this.current = route;
|
this.current = route;
|
||||||
this.current_name = route.name;
|
this.current_name = route.name;
|
||||||
this.match = match;
|
this.match = match;
|
||||||
this.emitSafe(':route', route, match);
|
this.emit(':route', route, match);
|
||||||
this.emitSafe(`:route:${route.name}`, ...match);
|
this.emit(`:route:${route.name}`, ...match);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.current = this.current_name = this.match = null;
|
this.current = this.current_name = this.match = null;
|
||||||
this.emitSafe(':route', null, null);
|
this.emit(':route', null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
route(name, path) {
|
route(name, path) {
|
||||||
|
|
|
@ -573,7 +573,13 @@ export class FineWrapper extends EventEmitter {
|
||||||
try {
|
try {
|
||||||
inst.forceUpdate();
|
inst.forceUpdate();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.fine.log.error(`An error occured when calling forceUpdate on an instance of ${this.name}`, err);
|
this.fine.log.capture(err, {
|
||||||
|
tags: {
|
||||||
|
fine_wrapper: this.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.finelog.error(`An error occured when calling forceUpdate on an instance of ${this.name}`, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ export const SERVER = DEBUG ? '//localhost:8000' : 'https://cdn.frankerfacez.com
|
||||||
export const CLIENT_ID = 'a3bc9znoz6vi8ozsoca0inlcr4fcvkl';
|
export const CLIENT_ID = 'a3bc9znoz6vi8ozsoca0inlcr4fcvkl';
|
||||||
export const API_SERVER = '//api.frankerfacez.com';
|
export const API_SERVER = '//api.frankerfacez.com';
|
||||||
|
|
||||||
|
export const SENTRY_ID = 'https://18f42c65339d4164b3fdebfc8c8bc99b@sentry.io/1186960';
|
||||||
|
|
||||||
export const TWITCH_EMOTE_BASE = '//static-cdn.jtvnw.net/emoticons/v1/';
|
export const TWITCH_EMOTE_BASE = '//static-cdn.jtvnw.net/emoticons/v1/';
|
||||||
|
|
||||||
export const KNOWN_CODES = {
|
export const KNOWN_CODES = {
|
||||||
|
|
|
@ -178,7 +178,7 @@ export class ClickOutside {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e) {
|
handleClick(e) {
|
||||||
if ( ! this.el.contains(e.target) )
|
if ( this.el && ! this.el.contains(e.target) )
|
||||||
this.cb(e);
|
this.cb(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -119,7 +119,7 @@ export class EventEmitter {
|
||||||
return list ? Array.from(list) : [];
|
return list ? Array.from(list) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(event, ...args) {
|
emitUnsafe(event, ...args) {
|
||||||
const list = this.__listeners[event];
|
const list = this.__listeners[event];
|
||||||
if ( ! list )
|
if ( ! list )
|
||||||
return;
|
return;
|
||||||
|
@ -160,28 +160,25 @@ export class EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSafe(event, ...args) {
|
emit(event, ...args) {
|
||||||
try {
|
|
||||||
return [this.emit(event, ...args), undefined];
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
return [null, err];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitAsync(event, ...args) {
|
|
||||||
const list = this.__listeners[event];
|
const list = this.__listeners[event];
|
||||||
if ( ! list )
|
if ( ! list )
|
||||||
return Promise.resolve([]);
|
return;
|
||||||
|
|
||||||
// Track removals separately to make iteration over the event list
|
// Track removals separately to make iteration over the event list
|
||||||
// much, much simpler.
|
// much, much simpler.
|
||||||
const removed = new Set,
|
const removed = new Set;
|
||||||
promises = [];
|
|
||||||
|
|
||||||
for(const item of list) {
|
for(const item of list) {
|
||||||
const [fn, ctx, ttl] = item;
|
const [fn, ctx, ttl] = item;
|
||||||
const ret = fn.apply(ctx, args);
|
let ret;
|
||||||
|
try {
|
||||||
|
ret = fn.apply(ctx, args);
|
||||||
|
} catch(err) {
|
||||||
|
if ( this.log )
|
||||||
|
this.log.capture(err, {tags: {event}, extra:{args}});
|
||||||
|
}
|
||||||
|
|
||||||
if ( ret === Detach )
|
if ( ret === Detach )
|
||||||
removed.add(item);
|
removed.add(item);
|
||||||
else if ( ttl !== false ) {
|
else if ( ttl !== false ) {
|
||||||
|
@ -190,9 +187,6 @@ export class EventEmitter {
|
||||||
else
|
else
|
||||||
item[2] = ttl - 1;
|
item[2] = ttl - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ret !== Detach )
|
|
||||||
promises.push(ret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( removed.size ) {
|
if ( removed.size ) {
|
||||||
|
@ -211,8 +205,72 @@ export class EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
async emitAsync(event, ...args) {
|
||||||
|
const list = this.__listeners[event];
|
||||||
|
if ( ! list )
|
||||||
|
return [];
|
||||||
|
|
||||||
|
// Track removals separately to make iteration over the event list
|
||||||
|
// much, much simpler.
|
||||||
|
const removed = new Set,
|
||||||
|
promises = [];
|
||||||
|
|
||||||
|
for(const item of list) {
|
||||||
|
const [fn, ctx] = item;
|
||||||
|
let ret;
|
||||||
|
try {
|
||||||
|
ret = fn.apply(ctx, args);
|
||||||
|
} catch(err) {
|
||||||
|
if ( this.log )
|
||||||
|
this.log.capture(err, {tags: {event}, extra: {args}});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !(ret instanceof Promise) )
|
||||||
|
ret = Promise.resolve(ret);
|
||||||
|
|
||||||
|
promises.push(ret.then(r => {
|
||||||
|
const new_ttl = item[2];
|
||||||
|
if ( r === Detach )
|
||||||
|
removed.add(item);
|
||||||
|
else if ( new_ttl !== false ) {
|
||||||
|
if ( new_ttl <= 1 )
|
||||||
|
removed.add(item);
|
||||||
|
else
|
||||||
|
item[2] = new_ttl - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ret !== Detach )
|
||||||
|
return ret;
|
||||||
|
}).catch(err => {
|
||||||
|
if ( this.log )
|
||||||
|
this.log.capture(err, {event, args});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = await Promise.all(promises);
|
||||||
|
|
||||||
|
if ( removed.size ) {
|
||||||
|
// Re-grab the list to make sure it wasn't removed mid-iteration.
|
||||||
|
const new_list = this.__listeners[event];
|
||||||
|
if ( new_list ) {
|
||||||
|
for(const item of removed) {
|
||||||
|
const idx = new_list.indexOf(item);
|
||||||
|
if ( idx !== -1 )
|
||||||
|
new_list.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! list.length ) {
|
||||||
|
this.__listeners[event] = null;
|
||||||
|
this.__dead_events++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const RAVEN_LEVELS = {
|
||||||
|
1: 'debug',
|
||||||
|
2: 'info',
|
||||||
|
4: 'warn',
|
||||||
|
8: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export default class Logger {
|
export default class Logger {
|
||||||
constructor(parent, name, level) {
|
constructor(parent, name, level, raven) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
this.level = level || (parent && parent.level) || Logger.DEFAULT_LEVEL;
|
this.level = level || (parent && parent.level) || Logger.DEFAULT_LEVEL;
|
||||||
|
this.raven = raven || (parent && parent.raven);
|
||||||
|
|
||||||
this.children = {};
|
this.children = {};
|
||||||
}
|
}
|
||||||
|
@ -34,6 +43,24 @@ export default class Logger {
|
||||||
return this.invoke(Logger.ERROR, args);
|
return this.invoke(Logger.ERROR, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crumb(...args) {
|
||||||
|
if ( this.raven )
|
||||||
|
return this.raven.captureBreadcrumb(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
capture(exc, opts, ...args) {
|
||||||
|
if ( this.raven ) {
|
||||||
|
opts = opts || {};
|
||||||
|
if ( ! opts.logger )
|
||||||
|
opts.logger = this.name;
|
||||||
|
|
||||||
|
this.raven.captureException(exc, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( args.length )
|
||||||
|
return this.error(...args);
|
||||||
|
}
|
||||||
|
|
||||||
/* eslint no-console: "off" */
|
/* eslint no-console: "off" */
|
||||||
invoke(level, args) {
|
invoke(level, args) {
|
||||||
if ( ! this.enabled || level < this.level )
|
if ( ! this.enabled || level < this.level )
|
||||||
|
@ -41,6 +68,12 @@ export default class Logger {
|
||||||
|
|
||||||
const message = Array.prototype.slice.call(args);
|
const message = Array.prototype.slice.call(args);
|
||||||
|
|
||||||
|
this.crumb({
|
||||||
|
message: message.join(' '),
|
||||||
|
category: this.name,
|
||||||
|
level: RAVEN_LEVELS[level] || level
|
||||||
|
});
|
||||||
|
|
||||||
if ( this.name )
|
if ( this.name )
|
||||||
message.unshift(`%cFFZ [%c${this.name}%c]:%c`, 'color:#755000; font-weight:bold', '', 'color:#755000; font-weight:bold', '');
|
message.unshift(`%cFFZ [%c${this.name}%c]:%c`, 'color:#755000; font-weight:bold', '', 'color:#755000; font-weight:bold', '');
|
||||||
else
|
else
|
||||||
|
|
|
@ -380,6 +380,12 @@ export class Module extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hasModule(name) {
|
||||||
|
const module = this.__modules[this.abs_path(name)];
|
||||||
|
return module instanceof Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
__get_requires() {
|
__get_requires() {
|
||||||
if ( has(this, 'requires') )
|
if ( has(this, 'requires') )
|
||||||
return this.requires;
|
return this.requires;
|
||||||
|
@ -430,6 +436,8 @@ export class Module extends EventEmitter {
|
||||||
else
|
else
|
||||||
this.__modules[this.abs_path(full_name)] = [[], [], [[this.__path, name]]]
|
this.__modules[this.abs_path(full_name)] = [[], [], [[this.__path, name]]]
|
||||||
|
|
||||||
|
requires.push(this.abs_path(full_name));
|
||||||
|
|
||||||
return this[name] = null;
|
return this[name] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,14 +198,14 @@ export function deep_copy(object, seen) {
|
||||||
seen.add(object);
|
seen.add(object);
|
||||||
|
|
||||||
if ( Array.isArray(object) )
|
if ( Array.isArray(object) )
|
||||||
return object.map(x => deep_copy(x, seen));
|
return object.map(x => deep_copy(x, new Set(seen)));
|
||||||
|
|
||||||
const out = {};
|
const out = {};
|
||||||
for(const key in object)
|
for(const key in object)
|
||||||
if ( HOP.call(object, key) ) {
|
if ( HOP.call(object, key) ) {
|
||||||
const val = object[key];
|
const val = object[key];
|
||||||
if ( typeof val === 'object' )
|
if ( typeof val === 'object' )
|
||||||
out[key] = deep_copy(val, seen);
|
out[key] = deep_copy(val, new Set(seen));
|
||||||
else
|
else
|
||||||
out[key] = val;
|
out[key] = val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,14 @@ export class Vue extends Module {
|
||||||
|
|
||||||
async onLoad() {
|
async onLoad() {
|
||||||
const Vue = window.ffzVue = this.Vue = (await import(/* webpackChunkName: "vue" */ 'vue')).default,
|
const Vue = window.ffzVue = this.Vue = (await import(/* webpackChunkName: "vue" */ 'vue')).default,
|
||||||
|
RavenVue = await import(/* webpackChunkName: "vue" */ 'raven-js/plugins/vue'),
|
||||||
components = this._components;
|
components = this._components;
|
||||||
|
|
||||||
this.component((await import(/* webpackChunkName: "vue" */ 'src/std-components/index.js')).default);
|
this.component((await import(/* webpackChunkName: "vue" */ 'src/std-components/index.js')).default);
|
||||||
|
|
||||||
|
if ( this.root.raven )
|
||||||
|
this.root.raven.addPlugin(RavenVue, Vue);
|
||||||
|
|
||||||
for(const key in components)
|
for(const key in components)
|
||||||
if ( has(components, key) )
|
if ( has(components, key) )
|
||||||
Vue.component(key, components[key]);
|
Vue.component(key, components[key]);
|
||||||
|
|
|
@ -115,6 +115,10 @@
|
||||||
.ffz-i-lock:before { content: '\e820'; } /* '' */
|
.ffz-i-lock:before { content: '\e820'; } /* '' */
|
||||||
.ffz-i-lock-open:before { content: '\e821'; } /* '' */
|
.ffz-i-lock-open:before { content: '\e821'; } /* '' */
|
||||||
.ffz-i-arrows-cw:before { content: '\e822'; } /* '' */
|
.ffz-i-arrows-cw:before { content: '\e822'; } /* '' */
|
||||||
|
.ffz-i-ignore:before { content: '\e823'; } /* '' */
|
||||||
|
.ffz-i-block:before { content: '\e824'; } /* '' */
|
||||||
|
.ffz-i-pin:before { content: '\e825'; } /* '' */
|
||||||
|
.ffz-i-pin-outline:before { content: '\e826'; } /* '' */
|
||||||
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
.ffz-i-twitter:before { content: '\f099'; } /* '' */
|
||||||
.ffz-i-gauge:before { content: '\f0e4'; } /* '' */
|
.ffz-i-gauge:before { content: '\f0e4'; } /* '' */
|
||||||
.ffz-i-download-cloud:before { content: '\f0ed'; } /* '' */
|
.ffz-i-download-cloud:before { content: '\f0ed'; } /* '' */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue