Add React wrapper for Vue
This commit is contained in:
parent
9eb8eaa906
commit
581c94f6b3
|
@ -92,17 +92,7 @@ export default new class EmoteModule {
|
|||
return this._searchCache || (this._searchCache = {});
|
||||
}
|
||||
|
||||
get React() {
|
||||
return WebpackModules.getModuleByName('React');
|
||||
}
|
||||
|
||||
get ReactDOM() {
|
||||
return WebpackModules.getModuleByName('ReactDOM');
|
||||
}
|
||||
|
||||
processMarkup(markup, timestamp) {
|
||||
if (!this.enabledSetting.value) return markup;
|
||||
|
||||
timestamp = timestamp.valueOf();
|
||||
const allowNoWrapper = timestamp < enforceWrapperFrom;
|
||||
|
||||
|
@ -126,12 +116,13 @@ export default new class EmoteModule {
|
|||
newMarkup.push(text);
|
||||
text = null;
|
||||
}
|
||||
newMarkup.push(this.React.createElement('span', {
|
||||
className: 'bd-emote-outer',
|
||||
'data-bdemote-name': emote.name,
|
||||
'data-bdemote-src': emote.src,
|
||||
'data-has-wrapper': /;[\w]+;/gmi.test(word)
|
||||
|
||||
newMarkup.push(VueInjector.createReactElement({
|
||||
components: { EmoteComponent },
|
||||
data: { emote, hasWrapper: /;[\w]+;/gmi.test(word) },
|
||||
template: '<EmoteComponent :src="emote.src" :name="emote.name" :hasWrapper="hasWrapper" />'
|
||||
}));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (text === null) {
|
||||
|
@ -151,16 +142,6 @@ export default new class EmoteModule {
|
|||
return !/;[\w]+;/gmi.test(word);
|
||||
}
|
||||
|
||||
injectAll() {
|
||||
if (!this.enabledSetting.value) return;
|
||||
|
||||
const all = document.getElementsByClassName('bd-emote-outer');
|
||||
for (const ec of all) {
|
||||
if (ec.children.length) continue;
|
||||
this.injectEmote(ec);
|
||||
}
|
||||
}
|
||||
|
||||
findByProp(obj, what, value) {
|
||||
if (obj.hasOwnProperty(what) && obj[what] === value) return obj;
|
||||
if (obj.props && !obj.children) return this.findByProp(obj.props, what, value);
|
||||
|
@ -179,7 +160,7 @@ export default new class EmoteModule {
|
|||
try {
|
||||
// First child has all the actual text content, second is the edited timestamp
|
||||
const markup = this.findByProp(retVal, 'className', 'markup');
|
||||
if (!markup) return;
|
||||
if (!markup || !this.enabledSetting.value) return;
|
||||
markup.children[0] = this.processMarkup(markup.children[0], component.props.message.editedTimestamp || component.props.message.timestamp);
|
||||
} catch (err) {
|
||||
Logger.err('EmoteModule', err);
|
||||
|
@ -188,39 +169,6 @@ export default new class EmoteModule {
|
|||
for (const message of document.querySelectorAll('.message')) {
|
||||
Reflection(message).forceUpdate();
|
||||
}
|
||||
this.injectAll();
|
||||
this.unpatchMount = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidMount', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
this.unpatchUpdate = MonkeyPatch('BD:EmoteModule', Message.component.prototype).after('componentDidUpdate', component => {
|
||||
const element = this.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectEmotes(element);
|
||||
});
|
||||
}
|
||||
|
||||
injectEmote(root) {
|
||||
if (!this.enabledSetting.value) return;
|
||||
|
||||
while (root.firstChild) {
|
||||
root.removeChild(root.firstChild);
|
||||
}
|
||||
const { bdemoteName, bdemoteSrc, hasWrapper } = root.dataset;
|
||||
if (!bdemoteName || !bdemoteSrc) return;
|
||||
VueInjector.inject(root, {
|
||||
components: { EmoteComponent },
|
||||
data: { src: bdemoteSrc, name: bdemoteName, hasWrapper },
|
||||
template: '<EmoteComponent :src="src" :name="name" :hasWrapper="hasWrapper" />'
|
||||
}, DOM.createElement('span'));
|
||||
root.classList.add('bd-is-emote');
|
||||
}
|
||||
|
||||
injectEmotes(element) {
|
||||
if (!this.enabledSetting.value || !element) return;
|
||||
|
||||
for (const beo of element.getElementsByClassName('bd-emote-outer')) this.injectEmote(beo);
|
||||
}
|
||||
|
||||
getEmote(word) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
}
|
||||
|
||||
.bd-emotewrapper {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
max-height: 32px;
|
||||
|
||||
img {
|
||||
|
|
|
@ -30,8 +30,7 @@ export default class extends Module {
|
|||
}
|
||||
|
||||
/**
|
||||
* Patches Message to use the extended NameTag.
|
||||
* This is because NameTag is also used in places we don't really want any badges.
|
||||
* Patches Message to render profile badges.
|
||||
*/
|
||||
async patchMessage() {
|
||||
const Message = await ReactComponents.getComponent('Message');
|
||||
|
@ -48,24 +47,14 @@ export default class extends Module {
|
|||
|
||||
const username = ReactHelpers.findByProp(retVal, 'type', 'h2');
|
||||
if (!username) return;
|
||||
username.props.children.splice(1, 0, ReactHelpers.React.createElement('span', {
|
||||
className: 'bd-badge-outer',
|
||||
'data-userid': user.id
|
||||
|
||||
username.props.children.splice(1, 0, VueInjector.createReactElement({
|
||||
components: { BdMessageBadge },
|
||||
data: { c },
|
||||
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
|
||||
}));
|
||||
});
|
||||
|
||||
this.unpatchMessageMount = MonkeyPatch('ProfileBadges', Message.component.prototype).after('componentDidMount', component => {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectMessageBadges(element);
|
||||
});
|
||||
|
||||
this.unpatchMessageUpdate = MonkeyPatch('ProfileBadges', Message.component.prototype).after('componentDidUpdate', component => {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectMessageBadges(element);
|
||||
});
|
||||
|
||||
// Rerender all messages
|
||||
for (const message of document.querySelectorAll('.message')) {
|
||||
Reflection(message).forceUpdate();
|
||||
|
@ -122,27 +111,16 @@ export default class extends Module {
|
|||
const c = contributors.find(c => c.id === user.id);
|
||||
if (!c) return;
|
||||
|
||||
retVal.props.children.splice(1, 0, ReactHelpers.React.createElement('span', {
|
||||
className: 'bd-badge-outer',
|
||||
'data-userid': user.id
|
||||
retVal.props.children.splice(1, 0, VueInjector.createReactElement({
|
||||
components: { BdMessageBadge },
|
||||
data: { c },
|
||||
template: '<BdMessageBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />'
|
||||
}));
|
||||
} catch (err) {
|
||||
Logger.err('ProfileBadges', ['Error thrown while rendering a NameTag', err]);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(this);
|
||||
if (!element) return;
|
||||
ProfileBadges.injectMessageBadges(element);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(this);
|
||||
if (!element) return;
|
||||
ProfileBadges.injectMessageBadges(element);
|
||||
}
|
||||
};
|
||||
|
||||
// Rerender all channel members
|
||||
|
@ -157,45 +135,22 @@ export default class extends Module {
|
|||
return this.PatchedNameTag;
|
||||
}
|
||||
|
||||
injectMessageBadges(element) {
|
||||
for (const beo of element.getElementsByClassName('bd-badge-outer'))
|
||||
this.injectMessageBadge(beo);
|
||||
}
|
||||
|
||||
injectMessageBadge(root) {
|
||||
while (root.firstChild) {
|
||||
root.removeChild(root.firstChild);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Patches UserProfileModal to render profile badges.
|
||||
*/
|
||||
async patchUserProfileModal() {
|
||||
this.UserProfileModal = await ReactComponents.getComponent('UserProfileModal');
|
||||
this.unpatchUserProfileModal = MonkeyPatch('ProfileBadges', this.UserProfileModal.component.prototype).after('renderBadges', (component, args, retVal, setRetVal) => {
|
||||
|
||||
this.unpatchUserProfileModal = MonkeyPatch('ProfileBadges', this.UserProfileModal.component.prototype).after('renderBadges', (component, args, retVal, setRetVal) => {
|
||||
const user = ReactHelpers.findProp(component, 'user');
|
||||
if (!user) return;
|
||||
const c = contributors.find(c => c.id === user.id);
|
||||
if (!c) return;
|
||||
|
||||
const element = ReactHelpers.React.createElement('div', {
|
||||
className: 'bd-profile-badges-outer',
|
||||
'data-userid': user.id
|
||||
const element = VueInjector.createReactElement({
|
||||
components: { BdBadge },
|
||||
data: { c },
|
||||
template: '<BdBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />',
|
||||
});
|
||||
|
||||
if (!retVal) {
|
||||
|
@ -205,43 +160,6 @@ export default class extends Module {
|
|||
}));
|
||||
} else retVal.props.children.splice(0, 0, element);
|
||||
});
|
||||
|
||||
this.UserProfileModal.component.prototype.componentDidMount = this.UserProfileModal.component.prototype.componentDidMount || (() => {});
|
||||
this.unpatchUserProfileModalMount = MonkeyPatch('ProfileBadges', this.UserProfileModal.component.prototype).after('componentDidMount', component => {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectProfileBadges(element);
|
||||
});
|
||||
|
||||
this.UserProfileModal.component.prototype.componentDidUpdate = this.UserProfileModal.component.prototype.componentDidUpdate || (() => {});
|
||||
this.unpatchUserProfileModalUpdate = MonkeyPatch('ProfileBadges', this.UserProfileModal.component.prototype).after('componentDidUpdate', component => {
|
||||
const element = ReactHelpers.ReactDOM.findDOMNode(component);
|
||||
if (!element) return;
|
||||
this.injectProfileBadges(element);
|
||||
});
|
||||
}
|
||||
|
||||
injectProfileBadges(element) {
|
||||
for (const beo of element.getElementsByClassName('bd-profile-badges-outer'))
|
||||
this.injectProfileBadge(beo);
|
||||
}
|
||||
|
||||
injectProfileBadge(root) {
|
||||
while (root.firstChild) {
|
||||
root.removeChild(root.firstChild);
|
||||
}
|
||||
|
||||
const { userid } = root.dataset;
|
||||
if (!userid) return;
|
||||
|
||||
const c = contributors.find(c => c.id == userid);
|
||||
if (!c) return;
|
||||
|
||||
VueInjector.inject(root, {
|
||||
components: { BdBadge },
|
||||
data: { c },
|
||||
template: '<BdBadge :developer="c.developer" :webdev="c.webdev" :contributor="c.contributor" />',
|
||||
}, DOM.createElement('span'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import { WebpackModules } from 'modules';
|
||||
import Vue from './vue';
|
||||
|
||||
export default class {
|
||||
|
@ -28,4 +29,43 @@ export default class {
|
|||
return vue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a React element that will render a Vue component.
|
||||
* @param {Object} options Options to pass to Vue
|
||||
* @return {React.Element}
|
||||
*/
|
||||
static createReactElement(options) {
|
||||
const React = WebpackModules.getModuleByName('React');
|
||||
return React.createElement(this.ReactCompatibility, {options});
|
||||
}
|
||||
|
||||
static get ReactCompatibility() {
|
||||
if (this._ReactCompatibility) return this._ReactCompatibility;
|
||||
|
||||
const React = WebpackModules.getModuleByName('React');
|
||||
const ReactDOM = WebpackModules.getModuleByName('ReactDOM');
|
||||
|
||||
return this._ReactCompatibility = class VueComponent extends React.Component {
|
||||
render() {
|
||||
return React.createElement('span');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const element = ReactDOM.findDOMNode(this);
|
||||
if (!element) return;
|
||||
this.vueInstance.$mount(element);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const element = ReactDOM.findDOMNode(this);
|
||||
if (!element) return;
|
||||
this.vueInstance.$mount(element);
|
||||
}
|
||||
|
||||
get vueInstance() {
|
||||
return this._vueInstance || (this._vueInstance = new Vue(this.props.options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue