Refactor ChannelMember badges

This commit is contained in:
Samuel Elliott 2018-04-01 13:57:02 +01:00
parent c95d60ab0f
commit 7f5fa44fd3
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
4 changed files with 102 additions and 24 deletions

View File

@ -16,7 +16,7 @@ export { default as Vendor } from './vendor';
export * from './webpackmodules';
export * from './patcher';
export * from './reactcomponents';
export { ReactComponents, ReactAutoPatcher, Helpers as ReactComponentHelpers } from './reactcomponents';
export { default as EventListener } from './eventlistener';
export { default as SocketProxy } from './socketproxy';
export { default as EventHook } from './eventhook';

View File

@ -16,7 +16,7 @@ import { MonkeyPatch, Patcher } from './patcher';
import { WebpackModules, Filters } from './webpackmodules';
import DiscordApi from './discordapi';
class Helpers {
export class Helpers {
static get plannedActions() {
return this._plannedActions || (this._plannedActions = new Map());
}
@ -154,6 +154,10 @@ class Helpers {
return null;
}
static get React() {
return WebpackModules.getModuleByName('React');
}
static get ReactDOM() {
return WebpackModules.getModuleByName('ReactDOM');
}
@ -223,7 +227,7 @@ export class ReactComponents {
return;
}
if (!reflect.component.displayName) reflect.component.displayName = name;
Logger.info('ReactComponents', `Found important component ${name} with reflection`);
Logger.info('ReactComponents', [`Found important component ${name} with reflection`, reflect.component, reflect]);
this.push(reflect.component);
clearInterval(importantInterval);
}, 50);
@ -320,9 +324,10 @@ export class ReactAutoPatcher {
}
static async patchChannelMember() {
this.ChannelMember = await ReactComponents.getComponent('ChannelMember', { selector: '.member.member-status' });
this.ChannelMember = await ReactComponents.getComponent('ChannelMember', { selector: '.member-2FrNV0' });
this.unpatchChannelMemberRender = MonkeyPatch('BD:ReactComponents', this.ChannelMember.component.prototype).after('render', (component, args, retVal) => {
if (!retVal.props || !retVal.props.children || !retVal.props.children.length) return;
// Logger.log('ReactComponents', ['Rendering ChannelMember', component, args, retVal]);
if (!retVal.props || !retVal.props.children) return;
const user = Helpers.findProp(component, 'user');
if (!user) return;
retVal.props['data-user-id'] = user.id;
@ -360,7 +365,7 @@ export class ReactAutoPatcher {
}
static forceUpdate() {
for (const e of document.querySelectorAll('.message,.message-group,.guild,.containerDefault-7RImuF,.channel-members .member')) {
for (const e of document.querySelectorAll('.message, .message-group, .guild, .containerDefault-7RImuF, .channel-members .member-2FrNV0')) {
Reflection(e).forceUpdate();
}
}

View File

@ -125,7 +125,7 @@ export default class extends EventListener {
}
setUserIds() {
for (let user of document.querySelectorAll('.channel-members-wrap .member')) {
for (let user of document.querySelectorAll('.channel-members-wrap .member, .channel-members-wrap .member-2FrNV0')) {
this.setUserId(user);
}
}

View File

@ -8,7 +8,8 @@
* LICENSE file in the root directory of this source tree.
*/
import { EventListener } from 'modules';
import { EventListener, ReactComponents, ReactComponentHelpers as Helpers, MonkeyPatch } from 'modules';
import { ClientLogger as Logger } from 'common';
import DOM from './dom';
import { BdBadge, BdMessageBadge } from './components/bd';
import VueInjector from './vueinjector';
@ -16,11 +17,15 @@ import contributors from '../data/contributors';
export default class extends EventListener {
init() {
this.patchChannelMember();
this.patchNameTag();
}
bindings() {
this.uiEvent = this.uiEvent.bind(this);
this.messageBadge = this.messageBadge.bind(this);
this.badges = this.badges.bind(this);
this.userlistBadge = this.userlistBadge.bind(this);
}
get eventBindings() {
@ -30,7 +35,6 @@ export default class extends EventListener {
{ id: 'server-switch', callback: this.badges },
{ id: 'channel-switch', callback: this.badges },
{ id: 'ui:loadedmore', callback: this.badges },
{ id: 'ui:useridset', callback: this.userlistBadge },
{ id: 'ui-event', callback: this.uiEvent }
];
}
@ -71,20 +75,6 @@ export default class extends EventListener {
});
}
userlistBadge(e) {
const c = contributors.find(c => c.id === e.dataset.userId);
if (!c) return;
const memberUsername = e.querySelector('.member-username');
if (!memberUsername) return;
const root = document.createElement('span');
memberUsername.append(root);
VueInjector.inject(root, {
components: { BdMessageBadge },
data: { c },
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
});
}
inject(userid) {
const c = contributors.find(c => c.id === userid);
if (!c) return;
@ -110,4 +100,87 @@ export default class extends EventListener {
return contributors;
}
/**
* Patches ChannelMember to use the extended NameTag.
* This is because NameTag is also used in places we don't really want any badges.
*/
async patchChannelMember() {
const ChannelMember = await ReactComponents.getComponent('ChannelMember');
this.unpatchChannelMemberRender = MonkeyPatch('ProfileBadges', ChannelMember.component.prototype).after('render', (component, args, retVal) => {
if (!retVal.props || !retVal.props.children) return;
const user = Helpers.findProp(component, 'user');
if (!user) return;
const c = contributors.find(c => c.id === user.id);
if (!c) return;
const nameTag = retVal.props.children.props.children[1].props.children[0];
nameTag.type = this.PatchedNameTag || nameTag.type;
});
}
/**
* Creates an extended NameTag component that renders message badges.
*/
async patchNameTag() {
if (this.PatchedNameTag) return this.PatchedNameTag;
const ProfileBadges = this;
const NameTag = await ReactComponents.getComponent('NameTag', {selector: '.nameTag-26T3kW'});
return this.PatchedNameTag = class extends NameTag.component {
render() {
const retVal = NameTag.component.prototype.render.call(this, arguments);
try {
if (!retVal.props || !retVal.props.children) return;
const user = Helpers.findProp(this, 'user');
if (!user) return;
const c = contributors.find(c => c.id === user.id);
if (!c) return;
retVal.props.children.push(ReactHelpers.React.createElement('span', {
className: 'bd-badge-outer',
'data-userid': user.id
}));
} catch (err) {
Logger.err('ProfileBadges', ['Error thrown while rendering a NameTag', err]);
}
return retVal;
}
componentDidMount() {
const element = Helpers.ReactDOM.findDOMNode(this);
if (!element) return;
ProfileBadges.injectMessageBadges(element);
}
componentDidUpdate() {
const element = Helpers.ReactDOM.findDOMNode(this);
if (!element) return;
// ProfileBadges.injectMessageBadges(element);
}
};
}
injectMessageBadges(element) {
for (const beo of element.getElementsByClassName('bd-badge-outer')) this.injectNameTagBadge(beo);
}
injectMessageBadge(root) {
const { userid } = root.dataset;
if (!userid) return;
const c = contributors.find(c => c.id === userid);
if (!c) return;
VueInjector.inject(root, {
components: { BdMessageBadge },
data: { c },
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
}, DOM.createElement('span'));
root.classList.add('bd-has-badge');
}
}