mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 12:55:55 +00:00
4.77.2
* Fixed: Channel points reward messages not appearing in chat correctly for some users. (Closes #1650) * Fixed: Clicking on a stream's up-time not being able to find the ID of the current broadcast. * Maintenance: Update the `twitch_data` module to have better typing and better use of promises. * API: Change the `createElement` method to create SVG elements with the proper namespace.
This commit is contained in:
parent
43c80713e9
commit
ddfcf9e17c
10 changed files with 624 additions and 364 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.77.1",
|
||||
"version": "4.77.2",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -378,11 +378,8 @@ export default class Channel extends Module {
|
|||
if ( event.ctrlKey || event.shiftKey || event.altKey )
|
||||
return;
|
||||
|
||||
const history = this.router.history;
|
||||
if ( history ) {
|
||||
event.preventDefault();
|
||||
history.push(link);
|
||||
}
|
||||
event.preventDefault();
|
||||
this.router.push(link);
|
||||
});
|
||||
|
||||
return a;
|
||||
|
|
|
@ -273,6 +273,12 @@ export default class ChatHook extends Module {
|
|||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.ChatRewardEventHandler = this.fine.define(
|
||||
'chat-reward-event-handler',
|
||||
n => n.unsubscribe && n.handleMessage && n.props?.messageHandlerAPI && n.props?.rewardMap,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.joined_raids = new Set;
|
||||
|
||||
this.RaidController = this.fine.define(
|
||||
|
@ -1614,6 +1620,85 @@ export default class ChatHook extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.ChatRewardEventHandler.ready((cls, instances) => {
|
||||
const t = this,
|
||||
old_subscribe = cls.prototype.subscribe;
|
||||
|
||||
cls.prototype.ffzInstall = function() {
|
||||
if (this._ffz_installed)
|
||||
return;
|
||||
|
||||
this._ffz_installed = true;
|
||||
const inst = this;
|
||||
const old_handle = this.handleMessage;
|
||||
this.handleMessage = function(msg) {
|
||||
//t.log.info('reward-message', msg, inst);
|
||||
try {
|
||||
if ( ! inst.props?.channelID || ! msg )
|
||||
return;
|
||||
|
||||
// TODO: Refactor this code and the PubSub code to remove code dupe.
|
||||
const type = msg.type,
|
||||
data = msg.data?.redemption,
|
||||
isAutomaticReward = type === 'automatic-reward-redeemed',
|
||||
isRedeemed = 'reward-redeemed';
|
||||
|
||||
if (!data?.reward || (!isAutomaticReward && !isRedeemed))
|
||||
return;
|
||||
|
||||
if ((isRedeemed && data.user_input) || (isAutomaticReward && data.reward.reward_type !== 'celebration'))
|
||||
return;
|
||||
|
||||
let rewardID;
|
||||
if (isAutomaticReward)
|
||||
rewardID = `${inst.props.channelID}:${data.reward.reward_type}`;
|
||||
else
|
||||
rewardID = data.reward.id;
|
||||
|
||||
const reward = inst.props.rewardMap[rewardID];
|
||||
if ( ! reward )
|
||||
return;
|
||||
|
||||
if ( t.chat.context.get('chat.filtering.blocked-types').has('ChannelPointsReward') )
|
||||
return;
|
||||
|
||||
inst.addMessage({
|
||||
id: data.id,
|
||||
type: t.chat_types.Message,
|
||||
ffz_type: 'points',
|
||||
ffz_reward: reward,
|
||||
ffz_reward_highlight: isHighlightedReward(reward),
|
||||
messageParts: [],
|
||||
user: {
|
||||
id: data.user.id,
|
||||
login: data.user.login,
|
||||
displayName: data.user.display_name
|
||||
},
|
||||
timestamp: new Date(msg.data.timestamp || data.redeemed_at).getTime()
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
} catch(err) {
|
||||
t.log.error('Error handling reward event:', err);
|
||||
return old_handle.call(this, msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cls.prototype.subscribe = function(...args) {
|
||||
try {
|
||||
this.ffzInstall();
|
||||
} catch(err) {
|
||||
t.log.error('Error in subscribe for RewardEventHandler:', err);
|
||||
}
|
||||
return old_subscribe.call(this, ...args);
|
||||
}
|
||||
|
||||
for(const inst of instances)
|
||||
inst.subscribe();
|
||||
});
|
||||
|
||||
this.ChatBufferConnector.on('mount', this.connectorMounted, this);
|
||||
this.ChatBufferConnector.on('update', this.connectorUpdated, this);
|
||||
this.ChatBufferConnector.on('unmount', this.connectorUnmounted, this);
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
query FFZ_BroadcastID($id: ID, $login: String) {
|
||||
user(id: $id, login: $login) {
|
||||
id
|
||||
stream {
|
||||
id
|
||||
archiveVideo {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
query FFZ_LastBroadcast($id: ID, $login: String) {
|
||||
user(id: $id, login: $login) {
|
||||
id
|
||||
lastBroadcast {
|
||||
id
|
||||
startedAt
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
25
src/utilities/data/recent-broadcasts.gql
Normal file
25
src/utilities/data/recent-broadcasts.gql
Normal file
|
@ -0,0 +1,25 @@
|
|||
query FFZ_RecentBroadcasts($id: ID, $login: String, $limit: Int, $cursor: Cursor, $type: BroadcastType, $sort: VideoSort, $options: VideoConnectionOptionsInput) {
|
||||
user(id: $id, login: $login) {
|
||||
id
|
||||
videos(
|
||||
first: $limit
|
||||
after: $cursor
|
||||
type: $type
|
||||
sort: $sort
|
||||
options: $options
|
||||
) {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
title
|
||||
createdAt
|
||||
publishedAt
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,9 @@ query FFZ_FetchUser($id: ID, $login: String) {
|
|||
title
|
||||
game {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
boxArtURL(width: 40, height: 56)
|
||||
}
|
||||
}
|
||||
stream {
|
||||
|
@ -28,4 +30,4 @@ query FFZ_FetchUser($id: ID, $login: String) {
|
|||
isStaff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ query FFZ_UserGame($id: ID, $login: String) {
|
|||
id
|
||||
game {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
boxArtURL(width: 40, height: 56)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ const SVG_TAGS = [
|
|||
'font-face-name', 'font-face-src', 'font-face-uri', 'font-face', 'font', 'foreignObject',
|
||||
'g', 'glyph', 'glyphRef', 'hkern', 'image', 'line', 'linearGradient', 'marker', 'mask',
|
||||
'metadata', 'missing-glyph', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient',
|
||||
'rect', 'set', 'stop', 'svg', 'switch', 'symbol', 'text', 'textPath', 'tref',
|
||||
'rect', 'set', 'stop', 'switch', 'symbol', 'text', 'textPath', 'tref',
|
||||
'tspan', 'use', 'view', 'vkern'
|
||||
];
|
||||
|
||||
|
@ -199,7 +199,11 @@ export function findReactFragment<TNode extends SimpleNodeLike>(
|
|||
export function createElement<K extends keyof HTMLElementTagNameMap>(tag: K, props?: any, ...children: DomFragment[]): HTMLElementTagNameMap[K];
|
||||
export function createElement<K extends keyof HTMLElementDeprecatedTagNameMap>(tag: K, props?: any, ...children: DomFragment[]): HTMLElementDeprecatedTagNameMap[K];
|
||||
export function createElement(tag: string, props?: any, ...children: DomFragment[]): HTMLElement {
|
||||
const el = document.createElement(tag);
|
||||
const isSvg = SVG_TAGS.includes(tag);
|
||||
const el = isSvg
|
||||
// This is technically wrong. I do not really care.
|
||||
? document.createElementNS('http://www.w3.org/2000/svg', tag) as unknown as HTMLElement
|
||||
: document.createElement(tag);
|
||||
|
||||
if ( children.length === 0)
|
||||
children = null as any;
|
||||
|
@ -207,14 +211,17 @@ export function createElement(tag: string, props?: any, ...children: DomFragment
|
|||
children = children[0] as any;
|
||||
|
||||
if ( typeof props === 'string' )
|
||||
el.className = props;
|
||||
el.setAttribute('class', props);
|
||||
else if ( props )
|
||||
for(const key in props)
|
||||
if ( has(props, key) ) {
|
||||
const lk = key.toLowerCase(),
|
||||
prop = props[key];
|
||||
|
||||
if ( lk === 'style' ) {
|
||||
if ( key === 'className' ) {
|
||||
el.setAttribute('class', prop);
|
||||
|
||||
} else if ( lk === 'style' ) {
|
||||
if ( typeof prop === 'string' )
|
||||
el.style.cssText = prop;
|
||||
else if ( prop && typeof prop === 'object' )
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue