mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-01 08:38:32 +00:00
4.0.0-rc4.5
* Added: Whisper Support * Fixed: UI missing hover state for a few elements added by FrankerFaceZ. * Fixed: Handle missing badge definition when rendering FFZ badges. * Fixed: Update static chat message type mappings. * Fixed: Error in metadata when unable to get the proper player. * Fixed: Incorrectly applying dark theme to products page. A bit more work on getting enhanced viewer cards ready.
This commit is contained in:
parent
4a326823b9
commit
17fb41f083
26 changed files with 396 additions and 80 deletions
|
@ -1,4 +1,14 @@
|
|||
<div class="list-header">4.0.0-rc4.4<span>@9e5af5443a7601d78faf</span> <time datetime="2018-07-09">(2018-07-09)</time></div>
|
||||
<div class="list-header">4.0.0-rc4.5<span>@f47412afa7703e2d3b18</span> <time datetime="2018-07-13">(2018-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Whisper Support</li>
|
||||
<li>Fixed: UI missing hover state for a few elements added by FrankerFaceZ.</li>
|
||||
<li>Fixed: Handle missing badge definition when rendering FFZ badges.</li>
|
||||
<li>Fixed: Update static chat message type mappings.</li>
|
||||
<li>Fixed: Error in metadata when unable to get the proper player.</li>
|
||||
<li>Fixed: Incorrectly applying dark theme to products page.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">4.0.0-rc4.4<span>@46f98c4cd4559eaa9828</span> <time datetime="2018-07-05">(2018-07-05)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Changed: Make usernames clickable in resub notifications in chat, to match with native subscription messages.</li>
|
||||
<li>Fixed: Make the code to automatically leave raids more robust.</li>
|
||||
|
|
11
src/i18n.js
11
src/i18n.js
|
@ -135,9 +135,11 @@ export class TranslationManager extends Module {
|
|||
}
|
||||
|
||||
onEnable() {
|
||||
this._ = new TranslationCore; /*({
|
||||
awarn: (...args) => this.log.info(...args)
|
||||
});*/
|
||||
this._ = new TranslationCore({
|
||||
formatters: {
|
||||
'humanTime': n => this.toHumanTime(n)
|
||||
}
|
||||
});
|
||||
|
||||
this._.transformation = TRANSFORMATIONS[this.settings.get('i18n.debug.transform')];
|
||||
this.locale = this.settings.get('i18n.locale');
|
||||
|
@ -162,6 +164,9 @@ export class TranslationManager extends Module {
|
|||
toHumanTime(duration, factor = 1) {
|
||||
// TODO: Make this better. Make all time handling better in fact.
|
||||
|
||||
if ( duration instanceof Date )
|
||||
duration = (Date.now() - duration.getTime()) / 1000;
|
||||
|
||||
duration = Math.floor(duration);
|
||||
|
||||
const years = Math.floor((duration * factor) / 31536000) / factor;
|
||||
|
|
|
@ -100,7 +100,7 @@ class FrankerFaceZ extends Module {
|
|||
FrankerFaceZ.Logger = Logger;
|
||||
|
||||
const VER = FrankerFaceZ.version_info = {
|
||||
major: 4, minor: 0, revision: 0, extra: '-rc4.4',
|
||||
major: 4, minor: 0, revision: 0, extra: '-rc4.5',
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
:key="i[0]"
|
||||
:aria-checked="value.icon === i[0]"
|
||||
:class="{'tw-interactable--selected': value.icon === i[0]}"
|
||||
class="ffz-icon tw-interactable"
|
||||
class="ffz-icon tw-interactable tw-interactable--inverted"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
@keydown.space.stop.prevent=""
|
||||
|
|
|
@ -57,6 +57,42 @@ export default class Actions extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.actions.viewer-card', {
|
||||
// Filter out actions
|
||||
process: (ctx, val) =>
|
||||
val.filter(x => x.appearance &&
|
||||
this.renderers[x.appearance.type] &&
|
||||
(! this.renderers[x.appearance.type].load || this.renderers[x.appearance.type].load(x.appearance)) &&
|
||||
(! x.action || this.actions[x.action])
|
||||
),
|
||||
|
||||
default: [
|
||||
{v: {action: 'friend'}},
|
||||
{v: {action: 'whisper', appearance: {type: 'text', text: 'Whisper', button: true}}},
|
||||
{v: {type: 'space'}},
|
||||
{v: {action: 'card_menu'}},
|
||||
{v: {type: 'new-line'}},
|
||||
{v: {action: 'ban', appearance: {type: 'icon', icon: 'ffz-i-block'}, display: {mod: true}}},
|
||||
{v: {action: 'timeout', appearance: {type: 'icon', icon: 'ffz-i-clock'}, display: {mod: true}}}
|
||||
],
|
||||
|
||||
type: 'array_merge',
|
||||
_ui: {
|
||||
path: 'Chat > Viewer Cards >> tabs ~> Actions @{"description": "Here, you define what actions are available on viewer cards."}',
|
||||
component: 'chat-actions',
|
||||
|
||||
data: () => {
|
||||
const chat = this.resolve('site.chat');
|
||||
|
||||
return {
|
||||
color: val => chat && chat.colors ? chat.colors.process(val) : val,
|
||||
actions: deep_copy(this.actions),
|
||||
renderers: deep_copy(this.renderers)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.handleContext = this.handleContext.bind(this);
|
||||
}
|
||||
|
@ -216,7 +252,7 @@ export default class Actions extends Module {
|
|||
});
|
||||
|
||||
return (<div
|
||||
class="ffz--inline-actions tw-inline tw-mg-r-05"
|
||||
class="ffz--inline-actions ffz-action-data tw-inline tw-mg-r-05"
|
||||
data-msg-id={msg.id}
|
||||
data-user={user}
|
||||
data-room={room}
|
||||
|
@ -228,7 +264,7 @@ export default class Actions extends Module {
|
|||
|
||||
getData(element) {
|
||||
const ds = element.dataset,
|
||||
parent = element.parentElement,
|
||||
parent = element.closest('.ffz-action-data'),
|
||||
pds = parent && parent.dataset,
|
||||
action = ds && ds.action,
|
||||
definition = this.actions[action];
|
||||
|
|
|
@ -387,7 +387,7 @@ export default class Badges extends Module {
|
|||
if ( hidden_badges[badge.id] )
|
||||
continue;
|
||||
|
||||
const full_badge = this.badges[badge.id],
|
||||
const full_badge = this.badges[badge.id] || {},
|
||||
slot = has(badge, 'slot') ? badge.slot : full_badge.slot,
|
||||
old_badge = slotted[slot],
|
||||
urls = badge.urls || (badge.image ? {1: badge.image} : null),
|
||||
|
|
|
@ -566,6 +566,43 @@ export default class Chat extends Module {
|
|||
}
|
||||
|
||||
|
||||
standardizeWhisper(msg) { // eslint-disable-line class-methods-use-this
|
||||
if ( ! msg )
|
||||
return msg;
|
||||
|
||||
if ( msg._ffz_message )
|
||||
return msg._ffz_message;
|
||||
|
||||
const emotes = {},
|
||||
is_action = msg.content.startsWith('/me '),
|
||||
offset = is_action ? 4 : 0,
|
||||
|
||||
out = msg._ffz_message = {
|
||||
user: msg.from,
|
||||
message: msg.content.slice(offset),
|
||||
is_action,
|
||||
emotes,
|
||||
timestamp: msg.sentAt && msg.sentAt.getTime(),
|
||||
deleted: false
|
||||
};
|
||||
|
||||
out.user.color = out.user.chatColor;
|
||||
|
||||
if ( Array.isArray(msg.emotes) && msg.emotes.length )
|
||||
for(const emote of msg.emotes) {
|
||||
const id = emote.emoteID,
|
||||
em = emotes[id] = emotes[id] || [];
|
||||
|
||||
em.push({
|
||||
startIndex: emote.from - offset,
|
||||
endIndex: emote.to - offset
|
||||
});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
standardizeMessage(msg) { // eslint-disable-line class-methods-use-this
|
||||
if ( ! msg )
|
||||
return msg;
|
||||
|
@ -574,6 +611,9 @@ export default class Chat extends Module {
|
|||
if ( msg.sender && ! msg.user )
|
||||
msg.user = msg.sender;
|
||||
|
||||
if ( msg.from && ! msg.user )
|
||||
msg.user = msg.from;
|
||||
|
||||
let user = msg.user;
|
||||
if ( ! user )
|
||||
user = msg.user = {};
|
||||
|
|
|
@ -20,6 +20,7 @@ export default class Room {
|
|||
this.style = new ManagedStyle(`room--${login}`);
|
||||
|
||||
this.emote_sets = new SourcedSet;
|
||||
this.badges = null;
|
||||
this.users = {};
|
||||
this.user_ids = {};
|
||||
|
||||
|
|
|
@ -928,7 +928,8 @@ export const TwitchEmotes = {
|
|||
emotes = [];
|
||||
|
||||
for(const emote_id in data)
|
||||
if ( has(data, emote_id) ) {
|
||||
// Disable fix for now so we can see what Twitch is sending for emote data.
|
||||
if ( has(data, emote_id) ) { // && Array.isArray(data[emote_id]) ) {
|
||||
for(const match of data[emote_id])
|
||||
emotes.push([emote_id, match.startIndex, match.endIndex + 1]);
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ export default {
|
|||
},
|
||||
|
||||
canEdit() {
|
||||
return this.action.v != null;
|
||||
return this.action.v != null && ! this.action.v.type;
|
||||
},
|
||||
|
||||
canPreview() {
|
||||
|
@ -268,6 +268,17 @@ export default {
|
|||
else if ( ! this.display )
|
||||
return this.t('setting.unknown', 'Unknown Value');
|
||||
|
||||
const type = this.display.type;
|
||||
|
||||
if ( type === 'new-line' )
|
||||
return this.t('setting.new-line', 'New Line');
|
||||
|
||||
else if ( type === 'space-small' )
|
||||
return this.t('setting.space-small', 'Space (Small)');
|
||||
|
||||
else if ( type === 'space' )
|
||||
return this.t('setting.space', 'Space');
|
||||
|
||||
const def = this.data.actions[this.display.action];
|
||||
if ( ! def )
|
||||
return this.t('setting.actions.unknown', 'Unknown Action Type: %{action}', this.display);
|
||||
|
@ -289,6 +300,17 @@ export default {
|
|||
if ( this.action.t === 'inherit' )
|
||||
return this.t('setting.inheritance.desc', 'Inherit values from lower priority profiles at this position.');
|
||||
|
||||
const type = this.display && this.display.type;
|
||||
|
||||
if ( type === 'new-line' )
|
||||
return this.t('setting.new-line.desc', 'Place all items following this on a new line.');
|
||||
|
||||
else if ( type === 'space-small' )
|
||||
return this.t('setting.space-small.desc', 'Place a small space between the previous item and the next item.');
|
||||
|
||||
else if ( type === 'space' )
|
||||
return this.t('setting.space.desc', 'Place as large a space as possible between the previous item and the next item.');
|
||||
|
||||
const def = this.display && this.data.actions[this.display.action];
|
||||
if ( ! def || ! def.description )
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tw-pd-x-1">
|
||||
<div v-if="item.inline" class="tw-pd-x-1">
|
||||
<input
|
||||
id="is_deleted"
|
||||
ref="is_deleted"
|
||||
|
@ -42,7 +42,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tw-pd-x-1">
|
||||
<div v-if="item.inline" class="tw-pd-x-1">
|
||||
<input
|
||||
id="with_mod_icons"
|
||||
ref="with_mod_icons"
|
||||
|
@ -76,18 +76,23 @@
|
|||
<div
|
||||
:data-user="JSON.stringify(sample_user)"
|
||||
:data-room="JSON.stringify(sample_room)"
|
||||
class="tw-flex tw-align-items-center tw-justify-content-center tw-pd-t-1"
|
||||
class="ffz-action-data tw-pd-t-1"
|
||||
data-msg-id="1234-5678"
|
||||
>
|
||||
<div
|
||||
v-if="! display.length"
|
||||
class="tw-c-text-alt-2 tw-pd-05 tw-font-size-4"
|
||||
class="tw-align-center tw-c-text-alt-2 tw-pd-05 tw-font-size-4"
|
||||
>
|
||||
{{ t('setting.actions.no-visible', 'no visible actions') }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(actions, idx) in display"
|
||||
:key="idx"
|
||||
class="tw-flex tw-align-items-center tw-justify-content-center"
|
||||
>
|
||||
<action-preview
|
||||
v-for="act in display"
|
||||
v-for="act in actions"
|
||||
:key="act.id"
|
||||
:act="act.v"
|
||||
:color="color(act.v.appearance.color)"
|
||||
|
@ -97,6 +102,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-flex tw-align-items-center tw-pd-b-05">
|
||||
<div class="tw-flex-grow-1">
|
||||
|
@ -135,7 +141,7 @@
|
|||
v-else
|
||||
:key="idx"
|
||||
:disabled="preset.disabled"
|
||||
class="tw-interactable tw-full-width"
|
||||
class="tw-interactable tw-interactable--inverted tw-full-width"
|
||||
@click="add(preset.value)"
|
||||
>
|
||||
<div class="tw-flex tw-align-items-center tw-pd-y-05 tw-pd-x-1">
|
||||
|
@ -299,13 +305,25 @@ export default {
|
|||
|
||||
display() {
|
||||
const out = [];
|
||||
let current = [];
|
||||
|
||||
if ( this.val )
|
||||
for(const val of this.val) {
|
||||
if ( val.v && this.displayAction(val.v) )
|
||||
out.push(val);
|
||||
if ( ! val.v )
|
||||
continue;
|
||||
|
||||
const type = val.v.type;
|
||||
if ( type === 'new-line' ) {
|
||||
out.push(current);
|
||||
current = [];
|
||||
|
||||
} else if ( this.displayAction(val.v) )
|
||||
current.push(val);
|
||||
}
|
||||
|
||||
if ( current.length )
|
||||
out.push(current);
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
|
@ -386,8 +404,8 @@ export default {
|
|||
this.show_all = this.$refs.show_all.checked;
|
||||
this.is_moderator = this.$refs.as_mod.checked;
|
||||
this.is_staff = false; //this.$refs.as_staff.checked;
|
||||
this.with_mod_icons = this.$refs.with_mod_icons.checked;
|
||||
this.is_deleted = this.$refs.is_deleted.checked;
|
||||
this.with_mod_icons = this.item.inline && this.$refs.with_mod_icons.checked;
|
||||
this.is_deleted = this.item.inline && this.$refs.is_deleted.checked;
|
||||
},
|
||||
|
||||
displayAction(action) {
|
||||
|
|
|
@ -220,6 +220,7 @@ export default class MainMenu extends Module {
|
|||
return;
|
||||
|
||||
const tree = this._settings_tree,
|
||||
expanded = this.settings.provider.get('settings-expanded', {}),
|
||||
tokens = def.ui.path_tokens,
|
||||
len = tokens.length;
|
||||
|
||||
|
@ -244,6 +245,10 @@ export default class MainMenu extends Module {
|
|||
};
|
||||
|
||||
Object.assign(token, raw_token);
|
||||
|
||||
if ( has(expanded, key) )
|
||||
token.expanded = expanded[key];
|
||||
|
||||
prefix = key;
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ export default class Metadata extends Module {
|
|||
const Player = this.resolve('site.player'),
|
||||
socket = this.resolve('socket'),
|
||||
player = Player.current,
|
||||
stats = player && player.getVideoInfo();
|
||||
stats = player && maybe_call(player.getVideoInfo, player);
|
||||
|
||||
if ( ! stats )
|
||||
return {stats};
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
{{ login }}
|
||||
</a>
|
||||
</h5>
|
||||
<div v-if="loaded">
|
||||
<div v-if="loaded" class="tw-pd-t-05">
|
||||
<span
|
||||
:data-title="t('viewer-card.views', 'Views')"
|
||||
class="ffz-tooltip tw-mg-r-05 ffz-i-views"
|
||||
|
@ -47,10 +47,11 @@
|
|||
{{ t(null, '%{followers|number}', {followers: user.followers.totalCount}) }}
|
||||
</span>
|
||||
<span
|
||||
data-title="rawUserAge"
|
||||
v-if="userAge"
|
||||
:data-title="t('viewer-card.age-tip', 'Member Since: %{age}', {age: userAge.toLocaleString()})"
|
||||
class="ffz-tooltip ffz-i-clock"
|
||||
>
|
||||
{ userAge }
|
||||
{{ t('viewer-card.age', '%{age|humanTime}', {age: userAge}) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -154,6 +155,13 @@ export default {
|
|||
return this.raw_user.displayName || this.raw_user.login;
|
||||
},
|
||||
|
||||
userAge() {
|
||||
if ( this.loaded )
|
||||
return new Date(this.user.createdAt);
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
current_tab() {
|
||||
return this.tabs[this.active_tab];
|
||||
}
|
||||
|
|
|
@ -54,10 +54,12 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import TabMixin from '../tab-mixin';
|
||||
|
||||
export default {
|
||||
mixins: [TabMixin],
|
||||
props: ['tab', 'user', 'room', 'currentUser']
|
||||
}
|
||||
|
||||
</script>
|
|
@ -4,8 +4,17 @@
|
|||
<loading-tab v-else-if="loading" />
|
||||
<template v-else>
|
||||
<ul>
|
||||
<li v-for="entry in data" :key="entry[1]">
|
||||
<span>{{ entry[0] }}</span>
|
||||
<li
|
||||
v-for="(entry, idx) in data"
|
||||
:key="entry[1]"
|
||||
:class="idx === 0 ? '' : 'tw-border-t'"
|
||||
class="tw-pd-x-1 tw-pd-y-05"
|
||||
>
|
||||
<span
|
||||
:data-title="fullTime(entry[0])"
|
||||
data-tooltip-type="text"
|
||||
class="ffz-tooltip tw-pd-r-1"
|
||||
>{{ formatTime(entry[0]) }}: </span>
|
||||
{{ entry[1] }}
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -44,12 +53,36 @@ export default {
|
|||
|
||||
socket.call('get_name_history', this.user.login).then(data => {
|
||||
this.loading = false;
|
||||
|
||||
if ( Array.isArray(data) )
|
||||
data = data.reverse();
|
||||
|
||||
this.data = data;
|
||||
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.getFFZ().log.error('Error loading name history.', err);
|
||||
this.errored = true;
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
fullTime(time) {
|
||||
try {
|
||||
const date = new Date(time);
|
||||
return date.toLocaleString();
|
||||
} catch(err) {
|
||||
return 'Unknown'
|
||||
}
|
||||
},
|
||||
|
||||
formatTime(time) {
|
||||
try {
|
||||
const date = new Date(time);
|
||||
return date.toLocaleDateString();
|
||||
} catch(err) {
|
||||
return 'Unknown'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,10 +55,18 @@ export default class Twilight extends BaseSite {
|
|||
this.router.on(':route', (route, match) => {
|
||||
this.log.info('Navigation', route && route.name, match && match[0]);
|
||||
this.fine.route(route && route.name);
|
||||
this.settings.updateContext({
|
||||
route,
|
||||
route_data: match
|
||||
});
|
||||
});
|
||||
|
||||
const current = this.router.current;
|
||||
this.fine.route(current && current.name);
|
||||
this.settings.updateContext({
|
||||
route: current,
|
||||
route_data: this.router.match
|
||||
});
|
||||
|
||||
document.head.appendChild(createElement('link', {
|
||||
href: MAIN_URL,
|
||||
|
@ -171,5 +179,6 @@ Twilight.ROUTES = {
|
|||
'user-events': '/:userName/events',
|
||||
'user-followers': '/:userName/followers',
|
||||
'user-following': '/:userName/following',
|
||||
'product': '/products/:productName',
|
||||
'user': '/:userName'
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import {ColorAdjuster} from 'utilities/color';
|
||||
import {setChildren} from 'utilities/dom';
|
||||
import {has, split_chars} from 'utilities/object';
|
||||
import {has, split_chars, shallow_object_equals} from 'utilities/object';
|
||||
import {FFZEvent} from 'utilities/events';
|
||||
|
||||
import Module from 'utilities/module';
|
||||
|
@ -73,31 +73,33 @@ const CHAT_TYPES = ((e = {}) => {
|
|||
e[e.ModerationAction = 2] = 'ModerationAction';
|
||||
e[e.TargetedModerationAction = 3] = 'TargetedModerationAction';
|
||||
e[e.AutoMod = 4] = 'AutoMod';
|
||||
e[e.Connected = 5] = 'Connected';
|
||||
e[e.Disconnected = 6] = 'Disconnected';
|
||||
e[e.Reconnect = 7] = 'Reconnect';
|
||||
e[e.Hosting = 8] = 'Hosting';
|
||||
e[e.Unhost = 9] = 'Unhost';
|
||||
e[e.Hosted = 10] = 'Hosted';
|
||||
e[e.Subscription = 11] = 'Subscription';
|
||||
e[e.Resubscription = 12] = 'Resubscription';
|
||||
e[e.SubGift = 13] = 'SubGift';
|
||||
e[e.Clear = 14] = 'Clear';
|
||||
e[e.SubscriberOnlyMode = 15] = 'SubscriberOnlyMode';
|
||||
e[e.FollowerOnlyMode = 16] = 'FollowerOnlyMode';
|
||||
e[e.SlowMode = 17] = 'SlowMode';
|
||||
e[e.EmoteOnlyMode = 18] = 'EmoteOnlyMode';
|
||||
e[e.RoomMods = 19] = 'RoomMods';
|
||||
e[e.RoomState = 20] = 'RoomState';
|
||||
e[e.Raid = 21] = 'Raid';
|
||||
e[e.Unraid = 22] = 'Unraid';
|
||||
e[e.Ritual = 23] = 'Ritual';
|
||||
e[e.Notice = 24] = 'Notice';
|
||||
e[e.Info = 25] = 'Info';
|
||||
e[e.BadgesUpdated = 26] = 'BadgesUpdated';
|
||||
e[e.Purchase = 27] = 'Purchase';
|
||||
e[e.BitsCharity = 28] = 'BitsCharity';
|
||||
e[e.CrateGift = 29] = 'CrateGift'
|
||||
e[e.SubscriberOnlyMode = 5] = 'SubscriberOnlyMode';
|
||||
e[e.FollowerOnlyMode = 6] = 'FollowerOnlyMode';
|
||||
e[e.SlowMode = 7] = 'SlowMode';
|
||||
e[e.EmoteOnlyMode = 8] = 'EmoteOnlyMode';
|
||||
e[e.R9KMode = 9] = 'R9KMode';
|
||||
e[e.Connected = 10] = 'Connected';
|
||||
e[e.Disconnected = 11] = 'Disconnected';
|
||||
e[e.Reconnect = 12] = 'Reconnect';
|
||||
e[e.Hosting = 13] = 'Hosting';
|
||||
e[e.Unhost = 14] = 'Unhost';
|
||||
e[e.Hosted = 15] = 'Hosted';
|
||||
e[e.Subscription = 16] = 'Subscription';
|
||||
e[e.Resubscription = 17] = 'Resubscription';
|
||||
e[e.SubGift = 18] = 'SubGift';
|
||||
e[e.Clear = 19] = 'Clear';
|
||||
e[e.RoomMods = 20] = 'RoomMods';
|
||||
e[e.RoomState = 21] = 'RoomState';
|
||||
e[e.Raid = 22] = 'Raid';
|
||||
e[e.Unraid = 23] = 'Unraid';
|
||||
e[e.Ritual = 24] = 'Ritual';
|
||||
e[e.Notice = 25] = 'Notice';
|
||||
e[e.Info = 26] = 'Info';
|
||||
e[e.BadgesUpdated = 27] = 'BadgesUpdated';
|
||||
e[e.Purchase = 28] = 'Purchase';
|
||||
e[e.BitsCharity = 29] = 'BitsCharity';
|
||||
e[e.CrateGift = 30] = 'CrateGift';
|
||||
e[e.RewardGift = 31] = 'RewardGift';
|
||||
return e;
|
||||
})();
|
||||
|
||||
|
@ -354,12 +356,34 @@ export default class ChatHook extends Module {
|
|||
|
||||
|
||||
async grabTypes() {
|
||||
const ct = await this.web_munch.findModule('chat-types');
|
||||
const ct = await this.web_munch.findModule('chat-types'),
|
||||
changes = [];
|
||||
|
||||
this.automod_types = ct && ct.a || AUTOMOD_TYPES;
|
||||
this.chat_types = ct && ct.b || CHAT_TYPES;
|
||||
this.message_types = ct && ct.c || MESSAGE_TYPES;
|
||||
this.mod_types = ct && ct.e || MOD_TYPES;
|
||||
if ( ! ct )
|
||||
return;
|
||||
|
||||
if ( ct.a && ! shallow_object_equals(ct.a, AUTOMOD_TYPES) ) {
|
||||
changes.push('AUTOMOD_TYPES');
|
||||
this.automod_types = ct.a;
|
||||
}
|
||||
|
||||
if ( ct.b && ! shallow_object_equals(ct.b, CHAT_TYPES) ) {
|
||||
changes.push('CHAT_TYPES');
|
||||
this.chat_types = ct.b;
|
||||
}
|
||||
|
||||
if ( ct.c && ! shallow_object_equals(ct.c, MESSAGE_TYPES) ) {
|
||||
changes.push('MESSAGE_TYPES');
|
||||
this.message_types = ct.c;
|
||||
}
|
||||
|
||||
if ( ct.e && ! shallow_object_equals(ct.e, MOD_TYPES) ) {
|
||||
changes.push('MOD_TYPES');
|
||||
this.mod_types = ct.e;
|
||||
}
|
||||
|
||||
if ( changes.length )
|
||||
this.log.info('Chat Types have changed from static mappings for categories:', changes.join(' '));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,6 +42,11 @@ export default class ChatLine extends Module {
|
|||
n => n.renderMessageBody && n.props && n.props.roomID,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.WhisperLine = this.fine.define(
|
||||
'whisper-line',
|
||||
n => n.props && n.props.message && n.props.reportOutgoingWhisperRendered
|
||||
)
|
||||
}
|
||||
|
||||
async onEnable() {
|
||||
|
@ -157,7 +162,50 @@ export default class ChatLine extends Module {
|
|||
});*/
|
||||
|
||||
|
||||
this.WhisperLine.ready(cls => {
|
||||
const old_render = cls.prototype.render;
|
||||
|
||||
cls.prototype.render = function() {
|
||||
if ( ! this.props.message || ! this.props.message.content )
|
||||
return old_render.call(this);
|
||||
|
||||
const msg = t.chat.standardizeWhisper(this.props.message),
|
||||
|
||||
is_action = msg.is_action,
|
||||
user = msg.user,
|
||||
color = t.parent.colors.process(user.color),
|
||||
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, null, null),
|
||||
contents = t.chat.renderTokens(tokens, e);
|
||||
|
||||
return e('div', {className: 'thread-message__message'},
|
||||
e('div', {className: 'tw-pd-x-1 tw-pd-y-05'}, [
|
||||
e('span', {
|
||||
className: 'thread-message__message--user-name notranslate',
|
||||
style: {
|
||||
color
|
||||
}
|
||||
}, user.displayName),
|
||||
e('span', null, is_action ? ' ' : ': '),
|
||||
e('span', {
|
||||
className: 'message',
|
||||
style: {
|
||||
color: is_action && color
|
||||
}
|
||||
}, contents)
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// Do this after a short delay to hopefully reduce the chance of React
|
||||
// freaking out on us.
|
||||
setTimeout(() => this.WhisperLine.forceUpdate());
|
||||
});
|
||||
|
||||
|
||||
this.ChatLine.ready(cls => {
|
||||
const old_render = cls.prototype.render;
|
||||
|
||||
cls.prototype.shouldComponentUpdate = function(props, state) {
|
||||
const show = state.alwaysShowMessage || ! props.message.deleted,
|
||||
old_show = this._ffz_show;
|
||||
|
@ -176,6 +224,8 @@ export default class ChatLine extends Module {
|
|||
}
|
||||
|
||||
cls.prototype.render = function() {
|
||||
try {
|
||||
|
||||
const types = t.parent.message_types || {},
|
||||
|
||||
msg = t.chat.standardizeMessage(this.props.message),
|
||||
|
@ -233,7 +283,7 @@ export default class ChatLine extends Module {
|
|||
bg_css = msg.mentioned && msg.mention_color ? t.parent.inverse_colors.process(msg.mention_color) : null;
|
||||
|
||||
if ( ! this.ffz_user_click_handler )
|
||||
this.ffz_user_click_handler = this.usernameClickHandler; // event => event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event);
|
||||
this.ffz_user_click_handler = this.usernameClickHandler; // event => ! event.ctrlKey ? this.usernameClickHandler(event) : t.viewer_cards.openCard(r, user, event);
|
||||
|
||||
let cls = `chat-line__message${show_class ? ' ffz--deleted-message' : ''}`,
|
||||
out = (tokens.length || ! msg.ffz_type) ? [
|
||||
|
@ -363,6 +413,16 @@ export default class ChatLine extends Module {
|
|||
'data-user-id': user.userID,
|
||||
'data-user': user.userLogin && user.userLogin.toLowerCase(),
|
||||
}, out);
|
||||
|
||||
} catch(err) {
|
||||
t.log.capture(err, {
|
||||
extra: {
|
||||
props: this.props
|
||||
}
|
||||
});
|
||||
|
||||
return old_render.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Do this after a short delay to hopefully reduce the chance of React
|
||||
|
@ -389,7 +449,14 @@ export default class ChatLine extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
for(const inst of this.WhisperLine.instances) {
|
||||
const msg = inst.props.message;
|
||||
if ( msg && msg._ffz_message )
|
||||
msg._ffz_message = null;
|
||||
}
|
||||
|
||||
this.ChatLine.forceUpdate();
|
||||
this.ChatRoomLine.forceUpdate();
|
||||
this.WhisperLine.forceUpdate();
|
||||
}
|
||||
}
|
|
@ -260,8 +260,11 @@ export default class Scroller extends Module {
|
|||
let step = this.ffz_smooth_scroll,
|
||||
old_time = Date.now();
|
||||
|
||||
const scroll_content = this.scroll.scrollContent,
|
||||
target_top = scroll_content.scrollHeight - scroll_content.clientHeight,
|
||||
const scroll_content = this.scroll.scrollContent;
|
||||
if ( ! scroll_content )
|
||||
return;
|
||||
|
||||
const target_top = scroll_content.scrollHeight - scroll_content.clientHeight,
|
||||
difference = target_top - scroll_content.scrollTop;
|
||||
|
||||
// If we are falling behind speed us up
|
||||
|
|
|
@ -61,7 +61,7 @@ export default class Following extends SiteModule {
|
|||
]
|
||||
},
|
||||
|
||||
changed: () => this.ChannelCard.forceUpdate()
|
||||
changed: () => this.parent.DirectoryCard.forceUpdate()
|
||||
});
|
||||
|
||||
this.apollo.registerModifier('FollowedChannels_RENAME2', FOLLOWED_CHANNELS);
|
||||
|
@ -219,7 +219,7 @@ export default class Following extends SiteModule {
|
|||
|
||||
// Hosted Channel Content
|
||||
simplebarContentChildren.push(<a
|
||||
class="tw-interactable"
|
||||
class="tw-interactable tw-interactable--inverted"
|
||||
href={`/${hostData.channel}`}
|
||||
onClick={e => this.parent.hijackUserClick(e, hostData.channel, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
|
||||
>
|
||||
|
@ -242,7 +242,7 @@ export default class Following extends SiteModule {
|
|||
for (let i = 0; i < hostData.nodes.length; i++) {
|
||||
const node = hostData.nodes[i];
|
||||
simplebarContentChildren.push(<a
|
||||
class="tw-interactable"
|
||||
class="tw-interactable tw-interactable--inverted"
|
||||
href={`/${node.login}`}
|
||||
onClick={e => this.parent.hijackUserClick(e, node.login, this.destroyHostMenu.bind(this))} // eslint-disable-line react/jsx-no-bind
|
||||
>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
:data-id="host._id"
|
||||
class="tw-border-t ffz--host-user"
|
||||
>
|
||||
<div class="tw-interactable">
|
||||
<div class="tw-interactable tw-interactable--inverted">
|
||||
<div class="tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1">
|
||||
<figure class="ffz-i-ellipsis-vert handle" />
|
||||
<div class="ffz-channel-avatar">
|
||||
|
|
|
@ -9,6 +9,8 @@ import {createElement} from 'utilities/dom';
|
|||
|
||||
import THEME_CSS_URL from 'site/styles/theme.scss';
|
||||
|
||||
const BAD_ROUTES = ['product'];
|
||||
|
||||
|
||||
export default class ThemeEngine extends Module {
|
||||
constructor(...args) {
|
||||
|
@ -17,14 +19,15 @@ export default class ThemeEngine extends Module {
|
|||
|
||||
this.inject('site');
|
||||
this.inject('site.css_tweaks');
|
||||
this.inject('site.router');
|
||||
|
||||
this.should_enable = true;
|
||||
|
||||
this.settings.add('theme.dark', {
|
||||
requires: ['context.ui.theme'],
|
||||
requires: ['theme.is-dark'],
|
||||
default: false,
|
||||
process(ctx, val) {
|
||||
return ctx.get('context.ui.theme') === 1 ? val : false
|
||||
return ctx.get('theme.is-dark') ? val : false
|
||||
},
|
||||
|
||||
ui: {
|
||||
|
@ -37,10 +40,17 @@ export default class ThemeEngine extends Module {
|
|||
changed: val => this.updateSetting(val)
|
||||
});
|
||||
|
||||
this.settings.add('theme.is-dark', {
|
||||
requires: ['context.ui.theme'],
|
||||
this.settings.add('theme.can-dark', {
|
||||
requires: ['context.route.name'],
|
||||
process(ctx) {
|
||||
return ctx.get('context.ui.theme') === 1;
|
||||
return ! BAD_ROUTES.includes(ctx.get('context.route.name'))
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('theme.is-dark', {
|
||||
requires: ['theme.can-dark', 'context.ui.theme'],
|
||||
process(ctx) {
|
||||
return ctx.get('theme.can-dark') && ctx.get('context.ui.theme') === 1;
|
||||
},
|
||||
changed: () => this.updateCSS()
|
||||
});
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
z-index: 9001;
|
||||
|
||||
> header {
|
||||
cursor: move;
|
||||
|
||||
background-size: cover;
|
||||
background-position: top;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,18 @@ export function array_equals(a, b) {
|
|||
}
|
||||
|
||||
|
||||
export function shallow_object_equals(a, b) {
|
||||
if ( typeof a !== 'object' || typeof b !== 'object' || ! array_equals(Object.keys(a), Object.keys(b)) )
|
||||
return false;
|
||||
|
||||
for(const key in a)
|
||||
if ( a[key] !== b[key] )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
export function set_equals(a,b) {
|
||||
if ( !(a instanceof Set) || !(b instanceof Set) || a.size !== b.size )
|
||||
return false;
|
||||
|
|
|
@ -71,6 +71,11 @@ export class Vue extends Module {
|
|||
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;
|
||||
}
|
||||
|
@ -96,6 +101,9 @@ export class Vue extends Module {
|
|||
methods: {
|
||||
t(key, phrase, options) {
|
||||
return this.$i18n.t_(key, phrase, options);
|
||||
},
|
||||
tList(key, phrase, options) {
|
||||
return this.$i18n.tList_(key, phrase, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue