Modifying LightcordApi

Adding category in settings for lightcordapi
making module alias
Fixing a little bit the minimal mode
This commit is contained in:
Jean Ouina 2020-07-15 01:50:02 +02:00
parent d3494666a2
commit 22ff405d40
31 changed files with 3635 additions and 2392 deletions

View File

@ -1593,10 +1593,18 @@ h2.ui-form-title {
/* Messages */
/* message divider */
.bd-minimal .divider-32i8lo {opacity: 0;}
/*.bd-minimal .divider-32i8lo {opacity: 0;}
There's no message divider anymore ??*/
/* message */
.bd-minimal .message-2qnXI6 {
padding-top: 0;
padding-bottom: 0;
}
/* message avatars */
.bd-minimal .large-3ChYtB {
.bd-minimal .message-2qnXI6 > .contents-2mQqc9 > .avatar-1BDn8e {
max-width: 20px;
max-height: 20px;
background-size: 100%;
@ -1606,24 +1614,35 @@ h2.ui-form-title {
}
/* message content */
.bd-minimal .content-3dzVd8 {
.bd-minimal .contents-2mQqc9 {
border-left: 2px solid #EBEBEB;
padding-left: 2px;
padding-left: 4px;
margin-left: -30px;
}
.bd-minimal .theme-dark .content-3dzVd8 {
.bd-minimal .theme-dark .contents-2mQqc9 {
border-left: 2px solid #303030;
}
/* message username */
.bd-minimal .username-_4ZSMR {
.bd-minimal .username-1A8OIy {
font-size: small;
}
/* message timestamp */
.bd-minimal .timestamp-3ZCmNB.timestampVisibleOnHover-2bQeI4 {
margin-left: -20px
}
/* message group */
.bd-minimal .container-1YxwTf {
/*.bd-minimal .container-1YxwTf {
padding: 5px;
}
Can't separate Message group as they're not marked anymore, instead add padding on the top.*/
.bd-minimal .groupStart-23k01U {
padding-top: 5px;
}
/* message embed */
.bd-minimal .embed-IeVjo6 {
padding: 2px;
@ -1635,27 +1654,43 @@ h2.ui-form-title {
min-width: 0px;
}
/* member list */
.bd-minimal .members-1998pB {
width: 180px;
}
/* member group */
.bd-minimal .membersGroup-v9BXpm {
padding: 0;
height: auto;
}
/* member list avatars */
.bd-minimal .members-1998pB .small-5Os1Bb {
/*.bd-minimal foreignObject[mask="url(#svg-mask-avatar-status-round-32)"] > img.avatar-VxgULZ {
max-width: 15px;
max-height: 15px;
background-size: 15px 15px;
}
}*/
/* member list avatars */
.bd-minimal .members-1998pB .small-5Os1Bb .status-oxiHuE {
height: 5px;
width: 5px;
.bd-minimal .member-3-YXUe > div > div.avatar-3uk_u9 > div > svg {
width: 30px;
height: 30px;
}
/* member list list item */
.bd-minimal .members-1998pB .member-3W1lQa {
padding: 5px;
.bd-minimal .member-3-YXUe > div > div.avatar-3uk_u9 > div {
width: 30px!important;
height: 30px!important;
}
/* member list item inner */
.bd-minimal .members-1998pB .memberInner-2CPc3V {
.bd-minimal .members-1998pB .member-3-YXUe {
transform: scale(0.9);
padding: 0;
margin: 0;
max-width: 200px;
}
.bd-minimal .member-3-YXUe > .layout-2DM8Md > .content-3QAtGj {
margin-left: -15px;
}
/* member list role label */
@ -1667,17 +1702,20 @@ h2.ui-form-title {
/* Channel List */
/* Hide Channel Mode */
.bd-minimal.bd-minimal-chan .channels-Ie2l6A {
.bd-minimal.bd-minimal-chan #channels {
display: none;
}
/* channel list guild name */
.bd-minimal .channels-Ie2l6A header span {
/*
.bd-minimal .container-2Rl01u:has(#channels) > header > h1 {
font-size: 12px;
}
:has() does not exist in css3. If we do not add a class from JS, we can't select it.*/
/* channel list */
.bd-minimal .channels-Ie2l6A {
.bd-minimal #channels {
width: 160px;
}
@ -1837,7 +1875,7 @@ h2.ui-form-title {
/* add/create server */
.bda-dark .theme-dark .root-1gCeng,
.bda-dark .theme-dark .theme-light .slide-2pHaq5 {
.bda-dark .slide-2pHaq5 {
background: #36393f;
}

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@ export const minimumDiscordVersion = "0.0.306";
export const currentDiscordVersion = (window.DiscordNative && window.DiscordNative.remoteApp && window.DiscordNative.remoteApp.getVersion && window.DiscordNative.remoteApp.getVersion()) || "0.0.306";
export const minSupportedVersion = "0.3.0";
export const bbdVersion = "0.3.4";
/*
export const LCChanelog = {
description: "Lightcord's changelog",
changes: [
@ -36,7 +37,7 @@ export const LCChanelog = {
const supportLink = Anchor ? BDV2.React.createElement(Anchor, {onClick: joinSupportServer}, "Join our Discord Server.") : BDV2.React.createElement("a", {className: `${AnchorClasses.anchor} ${AnchorClasses.anchorUnderlineOnHover}`, onClick: joinSupportServer}, "Join our Discord Server.");
return BDV2.React.createElement(TextElement, {size: TextElement.Sizes.SMALL, color: TextElement.Colors.STANDARD}, "Need support? ", supportLink);
})()
}
}*/
export const bbdChangelog = {
description: "BBD's changelog.",
changes: [
@ -91,7 +92,7 @@ export const settings = {
"Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"},
"Blur Personal Information": {id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Calling Ring Beat": {id: "lightcord-2", info: "Enable Discord's special calling beat.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Api Components\" tab.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Developer Options": {id: "lightcord-1", info: "Enable Discord's & Lightcord's Internal Developer Options. This allow the \"Experiments\" tab, the \"Developer Options\" tab and the \"Lightcord Api\" section.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Ad Block": {id: "lightcord-4", info: "Block any BOT that dms you with an invite link. Even in an embed.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Enable Lightcord Servers": {id: "lightcord-5", info: "Enable Lightcord's servers. Disabling this will disable custom badges.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},

View File

@ -54,8 +54,9 @@ window.BdApi = BdApi;
import Core from "./modules/core";
deprecateGlobal("mainCore", Core);
export default class CoreWrapper {
constructor(bdConfig) {
constructor(bdConfig, methods) {
Core.setConfig(bdConfig);
Core.setMethods(methods);
}
init() {

View File

@ -25,10 +25,16 @@ function Core() {
// this.init();
}
let methods
Core.prototype.setConfig = function(config) {
Object.assign(bdConfig, config);
};
Core.prototype.setMethods = function(m) {
methods = m
};
Core.prototype.init = async function() {
if (!Array.prototype.flat) {
Utils.alert("Not Supported", "BetterDiscord v" + bbdVersion + " does not support this old version (" + currentDiscordVersion + ") of Discord. Please update your Discord installation before proceeding.");

View File

@ -47,6 +47,8 @@ class BDSidebarHeader extends React.PureComponent {
}
}
let isClearingCache = false
export default new class V2_SettingsPanel {
constructor() {
@ -331,7 +333,7 @@ export default new class V2_SettingsPanel {
return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings})
}
lightcordComponent(sidebar) {
lightcordComponent(sidebar, forceUpdate) {
let appSettings = remote.getGlobal("appSettings")
return [
this.lightcordSettings.map((section, i) => {
@ -379,7 +381,38 @@ export default new class V2_SettingsPanel {
remote.app.quit()
},
wrapper: true
}, "Relaunch without BetterDiscord")
}, "Relaunch without BetterDiscord"),
React.createElement(Lightcord.Api.Components.inputs.Button, {
color: "yellow",
look: "ghost",
size: "medium",
hoverColor: "red",
onClick: () => {
if(isClearingCache)return
isClearingCache = true
Utils.showToast("Clearing cache...", {
type: "info"
})
forceUpdate()
remote.getCurrentWebContents().session.clearCache()
.then(() => {
Utils.showToast("Cache is cleared !", {
type: "success"
})
isClearingCache = false
forceUpdate()
}).catch(err => {
console.error(err)
Utils.showToast("An error occured. Check console for more informations.", {
type: "error"
})
isClearingCache = false
forceUpdate()
})
},
wrapper: true,
disabled: isClearingCache
}, "Clear cache")
]
}
@ -444,7 +477,7 @@ export default new class V2_SettingsPanel {
function makeComponent(children){
class SettingComponent extends React.Component {
render(){
return children(sidebar)
return children(sidebar, () => this.forceUpdate())
}
}
let sidebar

View File

@ -41,10 +41,25 @@ export default class V2_SettingsPanel_Sidebar {
id: "accountinfo"
}
]
if(window.Lightcord.Settings.devMode)items.push({
text: "Api Components Preview",
id: "lcapipreview"
})
return items
}
get LCDevItems(){
let items = []
if(!window.Lightcord.Settings.devMode)return items
items.push(...[
{
section: "DIVIDER"
},
{
section: "HEADER",
label: "Lightcord Api"
},
{
text: "Components Preview",
id: "lcapipreview"
}
])
return items
}
@ -61,6 +76,14 @@ export default class V2_SettingsPanel_Sidebar {
element: this.getComponent(e.id, sidebar)
}
}),
...this.LCDevItems.map(e => {
if(e.section)return e
return {
section: e.id,
label: e.text,
element: this.getComponent(e.id, sidebar)
}
}),
{
section: "DIVIDER"
},

File diff suppressed because one or more lines are too long

View File

@ -1101,6 +1101,16 @@
}
}
},
"@types/bandagedbd__bdapi": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@types/bandagedbd__bdapi/-/bandagedbd__bdapi-0.2.2.tgz",
"integrity": "sha512-igNQMnEticrue5LYXkKZGP6pGoN/mQTzUDJ2Jjr5T7wghgmkePTyk8r09N9Dm7fTjHH1aEmQmSSRv9s4Ce35bQ==",
"dev": true,
"requires": {
"@types/react": "*",
"@types/react-dom": "*"
}
},
"@types/json-schema": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",

View File

@ -18,6 +18,7 @@
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.0.0",
"@types/bandagedbd__bdapi": "^0.2.2",
"@types/react": "^16.9.36",
"@types/react-dom": "^16.9.8",
"@types/uuid": "^8.0.0",

View File

@ -23,17 +23,13 @@ module.exports = class LightcordApiExemple {
observer(changes) {} // Observer for the `document`. Better documentation than I can provide is found here: <https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver>
getSettingsPanel(){
let settings = [
{
component: "inputs.Button",
props: {
children: [
"sltsv"
],
color: "red"
}
}
]
return windows.Lightcord.Api.Utils.PluginUtils.renderSettings(settings)
const Markdown = BDModules.get(e => e.default && e.default.displayName === "Markdown")[0].default
let settings = Markdown.prototype.render.call({
props: Object.assign({
className: "",
children: "> warn\n> sltsv"
}, Markdown.defaultProps)
})
return window.Lightcord.Api.Utils.ReactToHTML(settings)
}
}

View File

@ -40,6 +40,11 @@ type ColorPickerProps = {
let ColorPickerModules
let isFetching = null
/**
* This componennt needs to be loaded. As a result, you may experience 100-300ms loading the first time.
* Render will return `null` before loaded.
*/
export default class ColorPicker extends React.PureComponent<ColorPickerProps, {value?:string,lastColor:any}> {
constructor(props:ColorPickerProps){
super(props)
@ -50,6 +55,13 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
this.onChange = this.onChange.bind(this)
}
/** Preload the component. */
static preload(){
if(ColorPicker.prototype.modules[0])return
if(isFetching)return
new ColorPicker({}).render()
}
onChange(val){
this.props.onChange(val)
this.setState({
@ -79,7 +91,10 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
] = this.modules
if(!ColorPickerComponent){
if(isFetching)isFetching.then(e => this.forceUpdate()) // support for multiple color picker
if(isFetching){ // support for multiple color picker
isFetching.then(() => this.forceUpdate())
return null
}
ColorPickerModules = null
let resolve
isFetching = new Promise(res => (resolve = res))
@ -160,7 +175,8 @@ export default class ColorPicker extends React.PureComponent<ColorPickerProps, {
}
static help = {
info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#yourcolor')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`"
info: "To convert hex colors to decimal, you can do `Lightcord.Api.Utils.HexColorToDecimal('#yourcolor')` and go back with `Lightcord.Api.Utils.DecimalColorToHex(7506394)`",
warn: "The component may not appear instantly. The component need to be loaded, so you could experience 50-300ms loading time depending on your internet connection."
}
}
let AllPreviews

View File

@ -0,0 +1,51 @@
import WebpackLoader from "../../modules/WebpackLoader"
import { notice, notices, events } from "./Notices"
import NOOP from "../../modules/noop"
let NoticeModules
export const defaultNotice:notice = {
text: "",
id: "unknown id",
onClick: NOOP,
buttonText: null,
type: "default"
}
export default class Notice extends React.Component<notice> {
static displayName = "LightcordNotice"
static defaultProps:notice = defaultNotice
get modules(){
return NoticeModules || (NoticeModules = [
WebpackLoader.find(e => e.noticeInfo)
])
}
render(){
const [
noticeClasses
] = this.modules
const className = noticeClasses["notice"+this.props.type.slice(0, 1).toUpperCase()+this.props.type.slice(1)]
if(!className){
notices.pop()
setImmediate(() => {
events.emit("noticeUpdate")
})
return null
}
const button = this.props.buttonText ? <button className={noticeClasses.button} onClick={() => {
notices.pop()
this.props.onClick()
events.emit("noticeUpdate")
}}>{this.props.buttonText}</button> : null
return <div className={className}>
<div className={noticeClasses.dismiss} role="button" tabIndex={0} onClick={() => {
notices.pop()
events.emit("noticeUpdate")
}} />
{this.props.text}
{button}
</div>
}
}

View File

@ -0,0 +1,50 @@
import Notice from "./Notice"
import uuid from "../../modules/uuid"
import { EventEmitter } from "events"
export const events = new EventEmitter()
export default class Notices extends React.Component<{container: any}> {
static displayName = "LightcordNotices"
static defaultProps = {}
constructor(props: Readonly<{ container: any }>){
super(props)
this.noticeHandler = this.noticeHandler.bind(this)
}
noticeHandler(){
this.forceUpdate()
}
componentWillMount(){
events.on("noticeUpdate", this.noticeHandler)
}
componentWillUnmount(){
events.off("noticeUpdate", this.noticeHandler)
}
render(){
if(!this.hasNotice)return null
const notice = notices[0]
return <Notice {...notice}></Notice>
}
get hasNotice(){
return notices.length > 0
}
}
export const notices:notice[] = []
export type noticeWithoutID = {
text: string,
buttonText?: string,
onClick?: () => void,
type: "default"|"info"|"success"|"danger"|"streamerMode"|"download"|"notification"|"premium"|"richPresence"|"premiumTier1"|"premiumTier2"|"facebook"|"brand"|"survey"|"spotify"
}
export type notice = {
id: string
} & noticeWithoutID

View File

@ -2,12 +2,16 @@ import WebpackLoader from "./modules/WebpackLoader"
import Components from "./components/components"
import uuid from "./modules/uuid"
import Utils from "./modules/Utils"
import DiscordTools from "./modules/DiscordTools"
import * as patchers from "./modules/patchers"
patchers.patch()
const LightcordApi = {
WebpackLoader: WebpackLoader,
Components: Components,
uuid: uuid,
Utils: Utils
Utils: Utils,
DiscordTools: DiscordTools
}
declare global {

View File

@ -0,0 +1,163 @@
import { notices, noticeWithoutID, notice, events as noticeEvents } from "../components/private/Notices";
import Utils from "./Utils";
import uuid from "./uuid";
import cloneNullProto from "./cloneNullProto";
import { EventEmitter } from "events";
import { defaultNotice } from "../components/private/Notice";
import excludeProperties from "./excludeProperties";
import NOOP from "./noop";
import WebpackLoader, { WebpackLoaderError } from "./WebpackLoader";
let soundModule
export default new class DiscordTools {
showNotice(data:NoticeData):Notice{
if(typeof data !== "object" || typeof data.text !== "string")throw new Error(`This notice is not valid. Given: ${Utils.formatJSObject(data)}`)
let newData = cloneNullProto(Object.assign({}, defaultNotice, data)) as notice
newData.id = uuid()
notices.push(newData)
noticeEvents.emit("noticeUpdate")
const notice = new Notice(newData)
return notice
}
get notices():Notice[]{
return notices.map(data => new Notice(data))
}
/**
* Quickly send notification (Even when no focused.)
* @param data The notification. Be sure to include all properties except functions cause they're optional.
* Notifications have a timeout of 3-5 seconds.
* They look like this: https://i.imgur.com/jzuxKKu.png
*/
showNotification(data:NotificationData):Notification{
const notification = new window.Notification(data.title, excludeProperties(data, [
"title",
"onClick",
"onClose",
"onShow"
]))
notification.onclick = data.onClick || NOOP
notification.onshow = data.onShow || NOOP
notification.onclose = data.onClose || NOOP
return notification
}
playSound(sound:Sound){
soundModule = soundModule || WebpackLoader.findByUniqueProperties(["createSound"])
if(!soundModule)throw new WebpackLoaderError("Couldn't find soundModule here.")
const created = soundModule.createSound(sound)
created.play()
return created
}
}
export type Sound = "call_calling"|"call_ringing"|"call_ringing_beat"|"ddr-down"|"ddr-left"|"ddr-right"|"ddr-up"|"deafen"|"discodo"|"disconnect"|"human_man"|"mention1"|"mention2"|"mention3"|"message1"|"message2"|"message3"|"mute"|"overlayunlock"|"ptt_start"|"ptt_stop"|"reconnect"|"robot_man"|"stream_ended"|"stream_started"|"stream_user_joined"|"stream_user_left"|"undeafen"|"unmute"|"user_join"|"user_leave"|"user_moved"
export type NotificationData = {
title: string,
body: string,
icon: string,
onShow?: () => void,
onClick?: () => void,
onClose?: () => void
}
export type NoticeData = noticeWithoutID
const EventHandler = function(){
if(this.removed !== this.state.removed){
if(this.removed){
this.emit("removed")
}
}
if(this.showing !== this.state.showing){
if(this.showing){
this.emit("showing", true)
}else{
this.emit("showing", false)
}
}
if(this.index !== this.state.index){
this.emit("index", this.index)
}
}
/** A notice interface for modifying it and subscribing to events. */
export class Notice extends EventEmitter {
constructor(data){
super()
this.data = data
this.state = {
removed: this.removed,
showing: this.showing,
index: this.index
}
let eventFunc = EventHandler.bind(this)
noticeEvents.on("noticeUpdate", eventFunc)
this.on("removed", () => {
noticeEvents.off("noticeUpdate", eventFunc)
})
}
state:{
removed:boolean,
showing:boolean,
index:number
}
get removed():boolean{
return !notices.find(e => e.id === this.id)
}
get showing():boolean{
return this.index === 0
}
get index():number{
return notices.findIndex(e => e.id === this.id)
}
get id(){
return this.data.id
}
get text(){
return this.data.text
}
set text(text){
this.data.text = text
noticeEvents.emit("noticeUpdate")
}
get type(){
return this.data.type
}
set type(type){
this.data.type = type
noticeEvents.emit("noticeUpdate")
}
get buttonText(){
return this.data.buttonText
}
set buttonText(buttonText:string){
this.data.buttonText = buttonText
noticeEvents.emit("noticeUpdate")
}
get onClick(){
return this.data.onClick
}
set onClick(onClick){
this.data.onClick = onClick
noticeEvents.emit("noticeUpdate")
}
remove(){
if(this.removed)return
notices.splice(this.index, 1)
noticeEvents.emit("noticeUpdate")
}
data:notice
}

View File

@ -31,4 +31,80 @@ export default new class Utils {
if(isNaN(res))throw new Error(`Invalid color: ${color}`)
return res
}
removeDa(className:string):string{
if(!className)return className
return className.split(" ").filter(e => !e.startsWith("da-")).join(" ")
}
FindReact(dom:Element, traverseUp:number = 0):React.Component|React.PureComponent{
/** took from https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging */
const key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
const domFiber = dom[key];
if (domFiber == null) return null;
// react <16
if (domFiber._currentElement) {
let compFiber = domFiber._currentElement._owner;
for (let i = 0; i < traverseUp; i++) {
compFiber = compFiber._currentElement._owner;
}
return compFiber._instance;
}
// react 16+
const GetCompFiber = fiber=>{
//return fiber._debugOwner; // this also works, but is __DEV__ only
let parentFiber = fiber.return;
while (typeof parentFiber.type == "string") {
parentFiber = parentFiber.return;
}
return parentFiber;
};
let compFiber = GetCompFiber(domFiber);
for (let i = 0; i < traverseUp; i++) {
compFiber = GetCompFiber(compFiber);
}
return compFiber.stateNode;
}
hasClass(classNames:string, className:string):boolean{
if(!classNames || !className)return false
const classnames = classNames.split(" ")
for(let classname of this.removeDa(className).split(" ")){
if(!classnames.includes(classname))return false
}
return true
}
formatJSObject(obj:any):string{
if(["string", "number", "boolean", "bigint", "undefined"].includes(typeof obj))return JSON.stringify(obj)
if(obj === null)return "null"
if(typeof obj === "function")return String(obj)
if(typeof obj === "symbol")return String(obj)
if(Array.isArray(obj)){
if(!obj.length)return "[]"
return `[\n ${obj.map(e => this.formatJSObject(e)).join(",\n ")}\n]`
}else{
const keys = Object.keys(obj)
if(keys.length === 0)return "{}"
return `{\n ${keys.map(key => {
let original = key
if(typeof key === "symbol")key = "["+String(key)+"]"
else{
if(typeof key === "number")key = String(key)
else{
console.log(key)
if(isNaN(parseInt(key[0]))){
key = this.formatJSObject(key)
}else if(/[^\w\d_$]/g.test(key)){
key = this.formatJSObject(key)
}
}
}
return `${key}: ${this.formatJSObject(obj[original])}`
})}\n}`
}
}
}

View File

@ -47,3 +47,11 @@ export default new class WebpackLoader {
})
}
}
export class WebpackLoaderError extends Error {
constructor(message:string = ""){
message += "\n\tThis error is related to Lightcord not being able to find a WebpackModule. \n\tPlease show this error and a few lines of logs above this error. \n\tOpen an issue on https://github.com/Lightcord/Lightcord or in their discord server."
super(message)
this.name = "WebpackLoaderError"
}
}

View File

@ -0,0 +1,61 @@
import Utils from "./Utils"
import Notices, { notices } from "../components/private/Notices"
export function patch(){
/** START NOTICE */
getModule(e => e.default && e.default.displayName === "ConnectedAppView")
.then(async (mod) => {
const appClasses = await getModule(e => e.hasNotice);
const buildRender = original => {
return function render(){
const returnValue = original.call(this, ...arguments)
const newchildren = []
let children = returnValue.props.children[1].props.children
if(!Array.isArray(children))children = [children]
newchildren.push(children[0])
newchildren.push(React.createElement(Notices, {container: this}))
newchildren.push(children[1])
returnValue.props.children[1].props.children = newchildren
returnValue.props.children[1].props.children[2].props.children[0].props.render = buildRenderChannelSidebar(returnValue.props.children[1].props.children[2].props.children[0].props.render)
return returnValue
}
}
const buildRenderChannelSidebar = original => {
return function renderChannelSidebar(){
const returnValue = original.call(this, ...arguments)
const hasNotice = notices.length > 0
if(!hasNotice)return returnValue
if(!Utils.hasClass(returnValue.props.className, appClasses.hasNotice)){
returnValue.props.className += " "+Utils.removeDa(appClasses.hasNotice)
}
return returnValue
}
}
mod.default.prototype.render = buildRender(mod.default.prototype.render);
(async function(){
const base = document.querySelector("."+Utils.removeDa(appClasses.base))
if(!base)throw new Error(`Could not find base here`)
const elem = Utils.FindReact(base) as any
elem.render = buildRender(elem.render)
elem.forceUpdate()
})()
})
/** END NOTICE */
}
function getModule(filter: (mod:any) => boolean):Promise<any>{
return new Promise((resolve) => {
window.Lightcord.Api.ensureExported(filter)
.then(resolve)
.catch(err => {
console.error("[LIGHTCORD]", err, filter)
})
})
}

View File

@ -0,0 +1,3 @@
module.exports = {
}

View File

@ -47,22 +47,11 @@ async function privateInit(){
ModuleLoader.get(e => e.getCurrentHub)[0].getCurrentHub().getClient().getOptions().enabled = false
// setting react in require cache
try{
window.React = require("react")
}catch(e){
const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].map(c => !!e[c]).includes(false))[0]
window.React = React
require.cache["react"] = React
}
const React = ModuleLoader.get(e => !["Component", "PureComponent", "Children", "createElement", "cloneElement"].map(c => !!e[c]).includes(false))[0]
window.React = React
try{
window.ReactDOM = require("react-dom")
}catch(e){
const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0]
window.ReactDOM = ReactDOM
require.cache["react-dom"] = ReactDOM
}
const ReactDOM = ModuleLoader.get(e => e.findDOMNode)[0]
window.ReactDOM = ReactDOM
//stop here if betterdiscord is disabled.
if(electron.remote.process.argv.includes("--disable-betterdiscord")){
@ -290,7 +279,7 @@ async function privateInit(){
DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme)
})
require("../../../../../LightcordApi/js/main.js")
require("lightcordapi/js/main.js")
/*
if(shouldShowPrompt){
@ -363,7 +352,7 @@ async function privateInit(){
dispatcher.subscribe(constants.ActionTypes.CONNECTION_OPEN || "CONNECTION_OPEN", onConn)
}*/
const BetterDiscord = window.BetterDiscord = window.mainCore = new(require("../../../../../BetterDiscordApp/js/main.js").default)(BetterDiscordConfig)
const BetterDiscord = window.BetterDiscord = window.mainCore = new(require("../../../../../BetterDiscordApp/js/main.js").default)(BetterDiscordConfig, require("./betterdiscord"))
const Utils = window.Lightcord.BetterDiscord.Utils
const DOMTools = window.Lightcord.BetterDiscord.DOM
@ -1247,6 +1236,10 @@ require.extensions[".jsbr"] = (m, filename) => {
fs.writeFileSync(tmpFile.name+".js", zlib.brotliDecompressSync(fs.readFileSync(filename)))
return require.extensions[".js"](m, tmpFile.name+".js")
}
require.extensions[".txt"] = (m, filename) => {
m.exports = fs.readFileSync(filename, "utf8")
return m.exports
}
const LightcordBDFolder = path.join(electron.remote.app.getPath("appData"), "Lightcord_BD")
@ -1337,7 +1330,7 @@ path.originalResolve = originalResolve
let blacklist
function isBlacklisted(id){
if(!blacklist)blacklist = fs.readFileSync(path.join(__dirname, "blacklist.txt"), "utf8").split(/[\n\r]+/g).map((line, index, lines) => {
if(!blacklist)blacklist = require("./blacklist.txt").split(/[\n\r]+/g).map((line, index, lines) => {
let id = ""
let comment = ""
line.split("#").forEach((idOrComment, index, array) => {

View File

@ -0,0 +1,253 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018, Nick Gavrilov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* From https://github.com/ilearnio/module-alias
*/
'use strict'
var BuiltinModule = require('module')
// Guard against poorly mocked module constructors
var Module = module.constructor.length > 1
? module.constructor
: BuiltinModule
var nodePath = require('path')
var modulePaths = []
var moduleAliases = {}
var moduleAliasNames = []
var oldNodeModulePaths = Module._nodeModulePaths
Module._nodeModulePaths = function (from) {
var paths = oldNodeModulePaths.call(this, from)
// Only include the module path for top-level modules
// that were not installed:
if (from.indexOf('node_modules') === -1) {
paths = modulePaths.concat(paths)
}
return paths
}
var oldResolveFilename = Module._resolveFilename
Module._resolveFilename = function (request, parentModule, isMain, options) {
for (var i = moduleAliasNames.length; i-- > 0;) {
var alias = moduleAliasNames[i]
if (isPathMatchesAlias(request, alias)) {
var aliasTarget = moduleAliases[alias]
// Custom function handler
if (typeof moduleAliases[alias] === 'function') {
var fromPath = parentModule.filename
aliasTarget = moduleAliases[alias](fromPath, request, alias)
if (!aliasTarget || typeof aliasTarget !== 'string') {
throw new Error('[module-alias] Expecting custom handler function to return path.')
}
}
request = nodePath.join(aliasTarget, request.substr(alias.length))
// Only use the first match
break
}
}
return oldResolveFilename.call(this, request, parentModule, isMain, options)
}
function isPathMatchesAlias (path, alias) {
// Matching /^alias(\/|$)/
if (path.indexOf(alias) === 0) {
if (path.length === alias.length) return true
if (path[alias.length] === '/') return true
}
return false
}
function addPathHelper (path, targetArray) {
path = nodePath.normalize(path)
if (targetArray && targetArray.indexOf(path) === -1) {
targetArray.unshift(path)
}
}
function removePathHelper (path, targetArray) {
if (targetArray) {
var index = targetArray.indexOf(path)
if (index !== -1) {
targetArray.splice(index, 1)
}
}
}
function addPath (path) {
var parent
path = nodePath.normalize(path)
if (modulePaths.indexOf(path) === -1) {
modulePaths.push(path)
// Enable the search path for the current top-level module
var mainModule = getMainModule()
if (mainModule) {
addPathHelper(path, mainModule.paths)
}
parent = module.parent
// Also modify the paths of the module that was used to load the
// app-module-paths module and all of it's parents
while (parent && parent !== mainModule) {
addPathHelper(path, parent.paths)
parent = parent.parent
}
}
}
function addAliases (aliases) {
for (var alias in aliases) {
addAlias(alias, aliases[alias])
}
}
function addAlias (alias, target) {
moduleAliases[alias] = target
// Cost of sorting is lower here than during resolution
moduleAliasNames = Object.keys(moduleAliases)
moduleAliasNames.sort()
}
/**
* Reset any changes maded (resets all registered aliases
* and custom module directories)
* The function is undocumented and for testing purposes only
*/
function reset () {
var mainModule = getMainModule()
// Reset all changes in paths caused by addPath function
modulePaths.forEach(function (path) {
if (mainModule) {
removePathHelper(path, mainModule.paths)
}
// Delete from require.cache if the module has been required before.
// This is required for node >= 11
Object.getOwnPropertyNames(require.cache).forEach(function (name) {
if (name.indexOf(path) !== -1) {
delete require.cache[name]
}
})
var parent = module.parent
while (parent && parent !== mainModule) {
removePathHelper(path, parent.paths)
parent = parent.parent
}
})
modulePaths = []
moduleAliases = {}
moduleAliasNames = []
}
/**
* Import aliases from package.json
* @param {object} options
*/
function init (options) {
if (typeof options === 'string') {
options = { base: options }
}
options = options || {}
var candidatePackagePaths
if (options.base) {
candidatePackagePaths = [nodePath.resolve(options.base.replace(/\/package\.json$/, ''))]
} else {
// There is probably 99% chance that the project root directory in located
// above the node_modules directory,
// Or that package.json is in the node process' current working directory (when
// running a package manager script, e.g. `yarn start` / `npm run start`)
candidatePackagePaths = [nodePath.join(__dirname, '../..'), process.cwd()]
}
var npmPackage
var base
for (var i in candidatePackagePaths) {
try {
base = candidatePackagePaths[i]
npmPackage = require(nodePath.join(base, 'package.json'))
break
} catch (e) {
// noop
}
}
if (typeof npmPackage !== 'object') {
var pathString = candidatePackagePaths.join(',\n')
throw new Error('Unable to find package.json in any of:\n[' + pathString + ']')
}
//
// Import aliases
//
var aliases = npmPackage._moduleAliases || {}
for (var alias in aliases) {
if (aliases[alias][0] !== '/') {
aliases[alias] = nodePath.join(base, aliases[alias])
}
}
addAliases(aliases)
//
// Register custom module directories (like node_modules)
//
if (npmPackage._moduleDirectories instanceof Array) {
npmPackage._moduleDirectories.forEach(function (dir) {
if (dir === 'node_modules') return
var modulePath = nodePath.join(base, dir)
addPath(modulePath)
})
}
}
function getMainModule () {
return require.main._simulateRepl ? undefined : require.main
}
module.exports = init
module.exports.addPath = addPath
module.exports.addAlias = addAlias
module.exports.addAliases = addAliases
module.exports.isPathMatchesAlias = isPathMatchesAlias
module.exports.reset = reset
module.exports.setMain = function(main){
require.main = main
}

View File

@ -0,0 +1 @@
An alias for react-dom

View File

@ -0,0 +1 @@
module.exports = window.ReactDOM

View File

@ -0,0 +1,11 @@
{
"name": "react-dom",
"version": "1.0.0",
"description": "An alias for react-dom",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1 @@
An alias for the react module.

View File

@ -0,0 +1 @@
module.exports = window.React

View File

@ -0,0 +1,11 @@
{
"name": "react",
"version": "1.0.0",
"description": "An alias for the react module.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

View File

@ -7,8 +7,16 @@ process.on("uncaughtException", console.error)
const ipcRenderer = require('./discord_native/ipc');
const electron = require("electron")
const moduleAlias = require("./BetterDiscord/loaders/module-alias")
const path = require("path")
electron.remote.getCurrentWindow().setBackgroundColor("#2f3136")
moduleAlias.setMain(module)
moduleAlias.addAlias("@lightcord/api", path.join(__dirname, "../../../../LightcordApi"))
moduleAlias.addAlias("lightcordapi", path.join(__dirname, "../../../../LightcordApi"))
moduleAlias.addPath(path.join(__dirname, "BetterDiscord", "modules"))
const TRACK_ANALYTICS_EVENT = 'TRACK_ANALYTICS_EVENT';
const TRACK_ANALYTICS_EVENT_COMMIT = 'TRACK_ANALYTICS_EVENT_COMMIT';

73
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "lightcord",
"version": "0.1.1",
"version": "0.1.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -721,12 +721,6 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@ -789,15 +783,6 @@
"dev": true,
"optional": true
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@ -884,12 +869,6 @@
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@ -962,17 +941,6 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -1005,35 +973,6 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"react-dom": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.19.1"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@ -1127,16 +1066,6 @@
"truncate-utf8-bytes": "^1.0.0"
}
},
"scheduler": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",

View File

@ -40,8 +40,6 @@
"@types/yauzl": "^2.9.1",
"cross-spawn": "^7.0.3",
"electron": "^8.4.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"terser": "^4.7.0",
"yazl": "^2.5.1"
}