This commit is contained in:
Jean Ouina 2020-06-26 21:05:09 +02:00
parent 4c3cacd9fd
commit 508ef57eb2
80 changed files with 6391 additions and 1274 deletions

2
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

View File

@ -987,7 +987,7 @@ body .ace_closeButton:active {
display: flex; display: flex;
} }
#bd-settingspane-container h2.ui-form-title { h2.ui-form-title {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
line-height: 20px; line-height: 20px;
@ -995,32 +995,32 @@ body .ace_closeButton:active {
display: inline-block; display: inline-block;
margin-bottom: 20px; margin-bottom: 20px;
} }
.theme-dark #bd-settingspane-container h2.ui-form-title { .theme-dark h2.ui-form-title {
color: #f6f6f7; color: #f6f6f7;
} }
.theme-light #bd-settingspane-container h2.ui-form-title { .theme-light h2.ui-form-title {
color: #4f545c; color: #4f545c;
} }
#bd-settingspane-container .ui-switch-item { .ui-switch-item {
flex-direction: column; flex-direction: column;
margin-top: 8px; margin-top: 8px;
} }
#bd-settingspane-container .ui-switch-item h3 { .ui-switch-item h3 {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
line-height: 24px; line-height: 24px;
flex: 1; flex: 1;
} }
.theme-dark #bd-settingspane-container .ui-switch-item h3 { .theme-dark .ui-switch-item h3 {
color: #f6f6f7; color: #f6f6f7;
} }
.theme-light #bd-settingspane-container .ui-switch-item h3 { .theme-light .ui-switch-item h3 {
color: #4f545c; color: #4f545c;
} }
#bd-settingspane-container .ui-switch-item .style-description { .ui-switch-item .style-description {
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
line-height: 20px; line-height: 20px;
@ -1028,14 +1028,14 @@ body .ace_closeButton:active {
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid hsla(218,5%,47%,.3); border-bottom: 1px solid hsla(218,5%,47%,.3);
} }
.theme-dark #bd-settingspane-container .ui-switch-item .style-description { .theme-dark .ui-switch-item .style-description {
color: #72767d; color: #72767d;
} }
.theme-light #bd-settingspane-container .ui-switch-item .style-description { .theme-light .ui-switch-item .style-description {
color: rgba(114,118,125,.6); color: rgba(114,118,125,.6);
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper { .ui-switch-item .ui-switch-wrapper {
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
@ -1047,7 +1047,7 @@ body .ace_closeButton:active {
flex: 0 0 auto; flex: 0 0 auto;
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper input { .ui-switch-item .ui-switch-wrapper input {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
cursor: pointer; cursor: pointer;
@ -1056,7 +1056,7 @@ body .ace_closeButton:active {
z-index: 1; z-index: 1;
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch { .ui-switch-item .ui-switch-wrapper .ui-switch {
background: var(--bd-blue); background: var(--bd-blue);
position: absolute; position: absolute;
top: 0; top: 0;
@ -1068,7 +1068,7 @@ body .ace_closeButton:active {
transition: background .15s ease-in-out,box-shadow .15s ease-in-out,border .15s ease-in-out; transition: background .15s ease-in-out,box-shadow .15s ease-in-out,border .15s ease-in-out;
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch:before { .ui-switch-item .ui-switch-wrapper .ui-switch:before {
content: ""; content: "";
display: block; display: block;
width: 18px; width: 18px;
@ -1083,19 +1083,19 @@ body .ace_closeButton:active {
box-shadow: 0 3px 1px 0 rgba(0,0,0,.05),0 2px 2px 0 rgba(0,0,0,.1),0 3px 3px 0 rgba(0,0,0,.05); box-shadow: 0 3px 1px 0 rgba(0,0,0,.05),0 2px 2px 0 rgba(0,0,0,.1),0 3px 3px 0 rgba(0,0,0,.05);
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch.checked { .ui-switch-item .ui-switch-wrapper .ui-switch.checked {
background: var(--bd-blue); background: var(--bd-blue);
} }
#bd-settingspane-container .ui-switch-item .ui-switch-wrapper .ui-switch.checked:before { .ui-switch-item .ui-switch-wrapper .ui-switch.checked:before {
transform: translateX(20px); transform: translateX(20px);
} }
#bd-settingspane-container .scroller-wrap { .scroller-wrap {
height: 100%; height: 100%;
} }
#bd-settingspane-container .scroller-wrap .scroller { .scroller-wrap .scroller {
display: flex; display: flex;
} }
.content-column .ui-form-title:first-child { .content-column .ui-form-title:first-child {

File diff suppressed because one or more lines are too long

View File

@ -4614,8 +4614,7 @@
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"dev": true
}, },
"jsesc": { "jsesc": {
"version": "2.5.2", "version": "2.5.2",
@ -4799,7 +4798,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"requires": { "requires": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
} }
@ -5249,8 +5247,7 @@
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
"dev": true
}, },
"object-copy": { "object-copy": {
"version": "0.1.0", "version": "0.1.0",
@ -5667,6 +5664,16 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true "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==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"prr": { "prr": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@ -5757,6 +5764,21 @@
"safe-buffer": "^5.1.0" "safe-buffer": "^5.1.0"
} }
}, },
"react": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2"
}
},
"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=="
},
"read-pkg": { "read-pkg": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",

View File

@ -39,5 +39,8 @@
"webpack": "^4.29.6", "webpack": "^4.29.6",
"webpack-cli": "^3.2.3", "webpack-cli": "^3.2.3",
"@types/react-dom": "^16.9.8" "@types/react-dom": "^16.9.8"
},
"dependencies": {
"react": "^16.13.1"
} }
} }

View File

@ -89,11 +89,11 @@ export const settings = {
"React DevTools": {id: "reactDevTools", info: "Adds react developer tools to the devtools. Must be installed in Google Chrome on your pc.", implemented: true, hidden: true, cat: "core", category: "developer settings"}, "React DevTools": {id: "reactDevTools", info: "Adds react developer tools to the devtools. Must be installed in Google Chrome on your pc.", implemented: true, hidden: true, cat: "core", category: "developer settings"},
/** Lightcord */ /** Lightcord */
"Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"}, "Disable BetterDiscord": {id: "bd-disable", info: "Disable Betterdiscord (plugins, themes, etc) (Not implemented).", implemented: false, hidden: false, cat: "lightcord", category: "Lightcord"},
"Blur Personnal Informations":{id: "lightcord-6", info: "Blur sensitive informations like email, payment infos and more.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"}, "Blur Personnal Informations":{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"}, "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 Internal Developer Options. This allow the \"Experiments\" tab and the \"Developer Options\" tab. (must reopen settings)", 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. (must reopen settings)", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},
"Ad Block": {id: "lightcord-4", info: "Block any BOT that dm you with an invite link. Even in an embed.", 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"}, "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"}, "Disable typing": {id: "lightcord-7", info: "Don't let other see you're typing.", implemented: true, hidden: false, cat: "lightcord", category: "Lightcord"},

View File

@ -16,6 +16,8 @@ import PluginCertifier from "./pluginCertifier";
import distant, { uuidv4 } from "./distant"; import distant, { uuidv4 } from "./distant";
import EmojiModule from "./emojiModule" import EmojiModule from "./emojiModule"
import {remote as electron} from "electron" import {remote as electron} from "electron"
import v2 from "./v2";
import webpackModules from "./webpackModules";
function Core() { function Core() {
// Object.assign(bdConfig, __non_webpack_require__(DataStore.configFile)); // Object.assign(bdConfig, __non_webpack_require__(DataStore.configFile));
@ -47,6 +49,8 @@ Core.prototype.init = async function() {
return; return;
} }
Utils.suppressErrors(this.patchAttributes.bind(this), "LC Plugin Certifier Patch")();
/* /*
const latestLocalVersion = bdConfig.updater ? bdConfig.updater.LatestVersion : bdConfig.latestVersion; const latestLocalVersion = bdConfig.updater ? bdConfig.updater.LatestVersion : bdConfig.latestVersion;
if (latestLocalVersion > bdConfig.version) { if (latestLocalVersion > bdConfig.version) {
@ -135,6 +139,35 @@ Core.prototype.init = async function() {
} }
}; };
Core.prototype.patchAttributes = async function() {
let attribsPatchs = []
this.cancelPatchAttributes = function() {
attribsPatchs.forEach(e => e())
}
while(!v2.MessageComponent)await new Promise(resolve => setTimeout(resolve, 100))
window.Lightcord.Api.ensureExported(e => e.default && e.default.displayName && e.default.displayName.includes("UserPopout"))
.then(UserPopout => {
console.log(UserPopout)
const render = UserPopout.default.prototype.render
UserPopout.default.prototype.render = function(){
const returnValue = render.call(this, ...arguments)
console.log(returnValue, this.props)
return returnValue
}
})
attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => {
if(data.methodArguments[0].childrenMessageContent.props.message){ // this can be a blocked message (not opened)
data.returnValue.props["data-message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id
}
}}))
/*
attribsPatchs.push(Utils.monkeyPatch(v2.MessageComponent, "default", {after: (data) => {
data.returnValue.props["message-id"] = data.methodArguments[0].childrenMessageContent.props.message.id
}}))*/
}
Core.prototype.checkForGuilds = function() { Core.prototype.checkForGuilds = function() {
let timesChecked = 0; let timesChecked = 0;
return new Promise(resolve => { return new Promise(resolve => {
@ -176,6 +209,30 @@ Core.prototype.initSettings = function () {
} }
} }
} }
window.Lightcord.Api.ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections)
.then(settingModule => {
let getPredicateSections = settingModule.default.prototype.getPredicateSections
settingModule.default.prototype.getPredicateSections = function(){
let result = getPredicateSections.call(this, ...arguments)
if(result[1].section === "My Account"){ // user settings, not guild settings
let poped = []
poped.push(result.pop())
poped.push(result.pop())
poped.push(result.pop())
poped.push(result.pop())
result.push(...settingsPanel.renderSidebar())
while(poped[0]){
result.push(poped.pop())
}
}
return result
}
})
}; };
@ -204,7 +261,6 @@ Core.prototype.initObserver = function () {
if (node.getElementsByClassName(classNameSocialLinks).length) { if (node.getElementsByClassName(classNameSocialLinks).length) {
node.setAttribute("layer-id", "user-settings"); node.setAttribute("layer-id", "user-settings");
node.setAttribute("id", "user-settings"); node.setAttribute("id", "user-settings");
if (!document.getElementById("bd-settings-sidebar")) settingsPanel.renderSidebar();
} }
} }
@ -332,13 +388,15 @@ Core.prototype.patchGuildListItems = function() {
if (data.returnValue && data.thisObject) { if (data.returnValue && data.thisObject) {
const returnValue = data.returnValue; const returnValue = data.returnValue;
const guildData = data.thisObject.props; const guildData = data.thisObject.props;
returnValue.props.className += " bd-guild"; let className = returnValue.props.className
if (guildData.unread) returnValue.props.className += " bd-unread"; className += " bd-guild";
if (guildData.selected) returnValue.props.className += " bd-selected"; if (guildData.unread) className += " bd-unread";
if (guildData.audio) returnValue.props.className += " bd-audio"; if (guildData.selected) className += " bd-selected";
if (guildData.video) returnValue.props.className += " bd-video"; if (guildData.audio) className += " bd-audio";
if (guildData.badge) returnValue.props.className += " bd-badge"; if (guildData.video) className += " bd-video";
if (guildData.animatable) returnValue.props.className += " bd-animatable"; if (guildData.badge) className += " bd-badge";
if (guildData.animatable) className += " bd-animatable";
returnValue.props.className = className
return returnValue; return returnValue;
} }
}}); }});
@ -421,7 +479,6 @@ Core.prototype.patchMessageHeader = function() {
) )
); );
} }
// TODO: fix 12787689 badges duplicate
const id = uuidv4() const id = uuidv4()
children.push( children.push(
BDV2.React.createElement("div", { BDV2.React.createElement("div", {
@ -439,6 +496,10 @@ function applyBadges(id, user, chat){
process.nextTick(() => { process.nextTick(() => {
const div = document.getElementById("badges-"+id) const div = document.getElementById("badges-"+id)
if(!div || div.childNodes.length > 0)return if(!div || div.childNodes.length > 0)return
if(div.childNodes.length)return
let blockDiv = document.createElement("div")
blockDiv.style.display = "none"
div.appendChild(blockDiv)
const Anchor = WebpackModules.find(m => m.displayName == "Anchor"); const Anchor = WebpackModules.find(m => m.displayName == "Anchor");

View File

@ -149,7 +149,7 @@ export default new class DevMode {
if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join("")); if (el==el.ownerDocument.documentElement) names.unshift(el.tagName.toLowerCase()+Array.from(el.classList.entries()).map(e => "."+e).join(""));
else{ else{
for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++); for (var c=1,e=el;e.previousElementSibling;e=e.previousElementSibling,c++);
names.unshift(el.tagName.toLowerCase()+(el.className || "").split(" ").map(e => "."+e).join("")+":nth-child("+c+")"); names.unshift(el.tagName.toLowerCase()+((typeof el.className === "string" && el.className) || "").split(" ").filter(e => !!e).map(e => "."+e).join("")+":nth-child("+c+")");
} }
el=el.parentNode; el=el.parentNode;
} }

View File

@ -140,7 +140,7 @@ class LightcordError extends Error {
export const Constants = { export const Constants = {
SERVER_URL: "http://127.0.0.1", SERVER_URL: "http://127.0.0.1",
badges: [ // TODO: badges: [ // TODO:
{ /*{
name: "Lightcord User", name: "Lightcord User",
id: "01cfa7b0-7cdb-4b0e-8258-9c6a78235c93", id: "01cfa7b0-7cdb-4b0e-8258-9c6a78235c93",
defaultUsers: [], defaultUsers: [],
@ -148,7 +148,7 @@ export const Constants = {
"user" "user"
], ],
component: LightcordUserBadge component: LightcordUserBadge
}, { }, */{
name: "Lightcord Bug Hunter", name: "Lightcord Bug Hunter",
id: "f04698f5-816b-41e3-bd01-92291193d7a5", id: "f04698f5-816b-41e3-bd01-92291193d7a5",
defaultUsers: [ defaultUsers: [

View File

@ -38,6 +38,8 @@ PluginModule.prototype.loadPlugins = async function () {
bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, message: "start() could not be fired.", error: {message: err.message, stack: err.stack}}); bdpluginErrors.push({name: name, file: bdplugins[plugins[i]].filename, message: "start() could not be fired.", error: {message: err.message, stack: err.stack}});
} }
} }
await new Promise((resolve) => setTimeout(resolve, 10))
} }
this.savePluginData(); this.savePluginData();

View File

@ -28,17 +28,33 @@ import { remote } from "electron";
import AntiAdDM from "./AntiAdDM"; import AntiAdDM from "./AntiAdDM";
import blurPrivate from "./blurPrivate"; import blurPrivate from "./blurPrivate";
import disableTyping from "./disableTyping"; import disableTyping from "./disableTyping";
import ApiPreview from "../ui/ApiPreview";
import V2C_SettingsTitle from "../ui/settingsTitle";
import Switch from "../ui/switch";
import MarginTop from "../ui/margintop";
export default new class V2_SettingsPanel { export default new class V2_SettingsPanel {
constructor() { constructor() {
this.sideBarOnClick = this.sideBarOnClick.bind(this);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.updateSettings = this.updateSettings.bind(this); this.updateSettings = this.updateSettings.bind(this);
this.sidebar = new V2_SettingsPanel_Sidebar(this.sideBarOnClick); this.sidebar = new V2_SettingsPanel_Sidebar();
// this.buildPluginProps = this.buildPluginProps.bind(this);
// this.buildThemeProps = this.buildThemeProps.bind(this); this.registerComponents()
this.showOriginal = this.showOriginal.bind(this); }
registerComponents(){
/** Lightcord */
this.sidebar.register("lightcord", makeComponent(this.lightcordComponent.bind(this)))
this.sidebar.register("status", makeComponent(this.PresenceComponent.bind(this)))
this.sidebar.register("accountinfo", makeComponent(this.AccountInfosComponent.bind(this)))
this.sidebar.register("lcapipreview", makeComponent(this.ApiPreviewComponent.bind(this)))
/* Bandaged BD */
this.sidebar.register("core", makeComponent(this.coreComponent.bind(this)))
this.sidebar.register("customcss", makeComponent(this.customCssComponent.bind(this)))
this.sidebar.register("plugins", makeComponent(this.renderAddonPane("plugins")))
this.sidebar.register("themes", makeComponent(this.renderAddonPane("themes")))
} }
get root() { get root() {
@ -102,33 +118,6 @@ export default new class V2_SettingsPanel {
}, []); }, []);
} }
sideBarOnClick(id) {
const contentRegion = DOM.query(".contentRegion-3nDuYy, .content-region");
contentRegion.style.display = "none";
this.root.style.display = "";
switch (id) {
case "core":
this.renderCoreSettings();
break;
case "customcss":
this.renderCustomCssEditor();
break;
case "plugins":
case "themes":
this.renderAddonPane(id);
break;
case "lightcord":
this.renderLightcordSettings()
break
case "status":
this.renderPresenceSettings()
break
case "accountinfo":
this.renderAccountInfos()
break
}
}
onClick() {} onClick() {}
onChange(id, checked) { onChange(id, checked) {
@ -295,100 +284,62 @@ export default new class V2_SettingsPanel {
Object.assign(settingsRPC, DataStore.getSettingGroup("rpc")); Object.assign(settingsRPC, DataStore.getSettingGroup("rpc"));
} }
showOriginal() {
BDV2.reactDom.unmountComponentAtNode(this.root);
this.root.style.display = "none";
DOM.query("."+BDModules.get(e => e.contentRegion)[0].contentRegion.split(" ")[0]+", .content-region").style.display = "";
}
renderSidebar() { renderSidebar() {
const tabs = document.querySelectorAll("[class*='side-'] > [class*='item-']"); return this.sidebar.render();
for (const element of tabs) {
element.removeEventListener("click", this.showOriginal);
element.addEventListener("click", this.showOriginal);
}
this.sidebar.render();
} }
get coreComponent() { coreComponent() {
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true}, return BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings})
BDV2.react.createElement(SectionedSettingsPanel, {key: "cspanel", onChange: this.onChange, sections: this.coreSettings}),
BDV2.react.createElement(Tools, {key: "tools"})
);
} }
get lightcordComponent() { lightcordComponent() {
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true}, return [
BDV2.react.createElement(SectionedSettingsPanel, {key: "lspannel", onChange: this.onChange, sections: this.lightcordSettings}), this.lightcordSettings.map((section, i) => {
BDV2.react.createElement(Tools, {key: "tools"}) return [
); (i === 0 ? null : BDV2.react.createElement(MarginTop)),
} BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, section.title),
section.settings.map(setting => {
get PresenceComponent() { return BDV2.react.createElement(Switch, {id: setting.id, key: setting.id, data: setting, checked: settingsCookie[setting.id], onChange: (id, checked) => {
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true}, this.onChange(id, checked);
BDV2.react.createElement(V2C_PresenceSettings, { }})
key: "lppannel", })
onChange: this.onChange, ]
settings: this.PresenceSettings
}), }),
BDV2.react.createElement(Tools, {key: "tools"}) BDV2.react.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "yellow", onClick(){
); console.log("Should relaunch")
remote.app.relaunch({
args: remote.process.argv.slice(1).concat(["--disable-betterdiscord"])
})
remote.app.quit()
}}, "Relaunch without BetterDiscord")
]
} }
get AccountInfosComponent() { PresenceComponent() {
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true}, return BDV2.react.createElement(V2C_PresenceSettings, {
BDV2.react.createElement(V2C_AccountInfos, { key: "lppannel",
key: "lapannel" onChange: this.onChange,
}), settings: this.PresenceSettings
BDV2.react.createElement(Tools, {key: "tools"}) })
);
} }
get customCssComponent() { AccountInfosComponent() {
return BDV2.react.createElement(Scroller, {contentColumn: true, fade: true, dark: true}, return BDV2.react.createElement(V2C_AccountInfos, {
BDV2.react.createElement(CssEditor, {key: "csseditor"}), key: "lapannel"
BDV2.react.createElement(Tools, {key: "tools"}) })
);
} }
renderCoreSettings() { ApiPreviewComponent() {
const root = this.root; return BDV2.react.createElement(ApiPreview, {
if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i"); key: "lapipannel"
BDV2.reactDom.render(this.coreComponent, root); })
} }
renderLightcordSettings() { customCssComponent() {
const root = this.root; return BDV2.react.createElement(CssEditor, {key: "csseditor"})
if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
BDV2.reactDom.render(this.lightcordComponent, root);
} }
renderPresenceSettings() {
const root = this.root;
if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
BDV2.reactDom.render(this.PresenceComponent, root);
}
renderAccountInfos() {
const root = this.root;
if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
BDV2.reactDom.render(this.AccountInfosComponent, root);
}
renderCustomCssEditor() {
const root = this.root;
if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
BDV2.reactDom.render(this.customCssComponent, root);
}
// renderAddonPane(type) {
// const root = this.root;
// if (!root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
// BDV2.reactDom.render(this.contentComponent(type), root);
// }
renderAddonPane(type) { renderAddonPane(type) {
if (!this.root) return Utils.err("SettingsPanel", "FAILED TO LOCATE ROOT: .layer-3QrUeG .standardSidebarView-3F1I7i");
// I know this shouldn't be here, but when it isn't, // I know this shouldn't be here, but when it isn't,
// React refuses to change the button when going // React refuses to change the button when going
// between plugins and themes page... something // between plugins and themes page... something
@ -397,19 +348,6 @@ export default new class V2_SettingsPanel {
constructor(props) { constructor(props) {
super(props); super(props);
this.prefix = this.props.type.replace("s", ""); this.prefix = this.props.type.replace("s", "");
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
BDEvents.on(`${this.prefix}-reloaded`, this.onChange);
BDEvents.on(`${this.prefix}-loaded`, this.onChange);
BDEvents.on(`${this.prefix}-unloaded`, this.onChange);
}
componentWillUnmount() {
BDEvents.off(`${this.prefix}-reloaded`, this.onChange);
BDEvents.off(`${this.prefix}-loaded`, this.onChange);
BDEvents.off(`${this.prefix}-unloaded`, this.onChange);
} }
onChange() { onChange() {
@ -425,7 +363,17 @@ export default new class V2_SettingsPanel {
set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");}, set: function() {console.warn("Addon policy for plugins #5 https://github.com/rauenzi/BetterDiscordApp/wiki/Addon-Policies#plugins");},
get: () => originalRender get: () => originalRender
}); });
const list = type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes); return function(){
return BDV2.reactDom.render(BDV2.react.createElement(ContentList, {type, onChange: this.sideBarOnClick}, BDV2.react.createElement(CardList, {type, list})), this.root); return BDV2.react.createElement(ContentList, {type}, BDV2.react.createElement(CardList, {type}))
}
} }
}; };
function makeComponent(children){
class SettingComponent extends React.Component {
render(){
return children()
}
}
return SettingComponent
}

View File

@ -9,8 +9,16 @@ import TooltipWrap from "../ui/tooltipWrap";
export default class V2_SettingsPanel_Sidebar { export default class V2_SettingsPanel_Sidebar {
constructor(onClick) { constructor() {
this.onClick = onClick; this.panels = {}
}
register(panel, component){
this.panels[panel] = component
}
getComponent(panel){
return this.panels[panel]
} }
get items() { get items() {
@ -30,7 +38,7 @@ export default class V2_SettingsPanel_Sidebar {
} }
get LCitems(){ get LCitems(){
return [ let items = [
{ {
text: "Lightcord Settings", text: "Lightcord Settings",
id: "lightcord" id: "lightcord"
@ -42,53 +50,43 @@ export default class V2_SettingsPanel_Sidebar {
id: "accountinfo" id: "accountinfo"
} }
] ]
} if(!!window.Lightcord.Settings.devMode)items.push({
text: "Api Components Preview",
get component() { id: "lcapipreview"
//<TooltipWrap color="black" side="top" text={title}> })
return items
const changelogButton = BDV2.react.createElement(TooltipWrap, {color: "black", side: "top", text: "BBD's Changelog"},
BDV2.react.createElement("div", {className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(bbdChangelog);}},
BDV2.react.createElement(History, {className: "bd-icon", size: "16px"})
)
);
const changelogButtonLC = BDV2.react.createElement(TooltipWrap, {color: "black", side: "top", text: "Lightcord's Changelog"},
BDV2.react.createElement("div", {className: "bd-changelog-button", onClick: () => {Utils.showChangelogModal(LCChanelog);}},
BDV2.react.createElement(History, {className: "bd-icon", size: "16px"})
)
);
return [
BDV2.react.createElement("span", null, BDV2.react.createElement(SideBar, {onClick: this.onClick, headerText: "Lightcord", headerButton: changelogButtonLC, items: this.LCitems})),
BDV2.react.createElement("span", null, BDV2.react.createElement(SideBar, {onClick: this.onClick, headerText: "Bandaged BD", headerButton: changelogButton, items: this.items}))
]
}
get root() {
const _root = DOM.query("#bd-settings-sidebar");
if (!_root) {
if (!this.injectRoot()) return null;
return this.root;
}
return _root;
}
injectRoot() {
const tabs = DOM.queryAll("[class*='side-'] > [class*='item-']:not([class*=Danger])");
const changeLog = tabs[tabs.length - 1];
if (!changeLog) return false;
changeLog.parentElement.insertBefore(DOM.createElement(`<div id="bd-settings-sidebar">`), changeLog.previousElementSibling);
return true;
} }
render() { render() {
const root = this.root; return [
if (!root) { {
console.log("FAILED TO LOCATE ROOT: [class*='side-'] > [class*='item-']:not([class*=Danger])"); section: "HEADER",
return; label: "Lightcord"
} },
BDV2.reactDom.render(this.component, root); ...this.LCitems.map(e => {
Utils.onRemoved(root, () => { return {
BDV2.reactDom.unmountComponentAtNode(root); section: e.id,
}); label: e.text,
element: this.getComponent(e.id)
}
}),
{
section: "DIVIDER"
},
{
section: "HEADER",
label: "Bandaged BD"
},
...this.items.map(e => {
return {
section: e.id,
label: e.text,
element: this.getComponent(e.id)
}
}),
{
section: "DIVIDER"
}
]
} }
} }

View File

@ -18,6 +18,8 @@ ThemeModule.prototype.loadThemes = async function () {
const theme = bdthemes[themes[i]]; const theme = bdthemes[themes[i]];
if (!themeCookie[theme.name]) themeCookie[theme.name] = false; if (!themeCookie[theme.name]) themeCookie[theme.name] = false;
if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css)); if (themeCookie[theme.name]) DOM.addStyle(DOM.escapeID(theme.id), unescape(theme.css));
await new Promise((resolve) => setTimeout(resolve, 10))
} }
for (const theme in themeCookie) { for (const theme in themeCookie) {
if (!bdthemes[theme]) delete themeCookie[theme]; if (!bdthemes[theme]) delete themeCookie[theme];

View File

@ -110,11 +110,16 @@ export default class Utils {
else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console else return console.error(methodName, "does not exist for", displayName); // eslint-disable-line no-console
} }
const origMethod = what[methodName]; const origMethod = what[methodName];
let canceled = false
const cancel = () => { const cancel = () => {
if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console if (!silent) console.log("unpatch", methodName, "of", displayName); // eslint-disable-line no-console
what[methodName] = origMethod; //what[methodName] = origMethod;
canceled = true // this allow to monkeypatch more than one time.
}; };
what[methodName] = function() { what[methodName] = function() {
if(canceled){
return origMethod.call(this, ...arguments)
}
const data = { const data = {
thisObject: this, thisObject: this,
methodArguments: arguments, methodArguments: arguments,

View File

@ -6,7 +6,6 @@ import V2C_SettingsTitle from "./settingsTitle";
*/ */
const React = BDV2.React; const React = BDV2.React;
let contentModule = BDModules.get(e => e.contentColumn)[0]
let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] let marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
let colorModule = BDModules.get(e => e.colorStandard)[0] let colorModule = BDModules.get(e => e.colorStandard)[0]
let sizeModule = BDModules.get(e => e.size32)[0] let sizeModule = BDModules.get(e => e.size32)[0]
@ -22,29 +21,27 @@ export default class V2C_AccountInfos extends React.Component {
} }
render() { render() {
if(!contentModule)contentModule = BDModules.get(e => e.contentColumn)[0]
if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0] if(!marginModule2)marginModule2 = BDModules.get(e => e.defaultMarginh5)[0]
if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0] if(!colorModule)colorModule = BDModules.get(e => e.colorStandard)[0]
if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0] if(!sizeModule)sizeModule = BDModules.get(e => e.size32)[0]
return (<div className={contentModule.contentColumn+" "+contentModule.contentColumnDefault+" content-column default"} return [
style={{padding: "60px 40px 0px"}}> <V2C_SettingsTitle text="Account Infos"/>,
<V2C_SettingsTitle text="Account Infos"/> <div>
<div> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> Profile
Profile </h5>
</h5> <CodeContent content={this.getProfileValue()} language="diff" />
<CodeContent content={this.getProfileValue()} language="diff" /> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> Statistics
Statistics </h5>
</h5> <CodeContent content={this.getStatistics()} language="diff" />
<CodeContent content={this.getStatistics()} language="diff" /> <h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}>
<h5 className={colorModule.colorStandard+" "+sizeModule.size14+" "+marginModule2.h5+" "+marginModule2.defaultMarginh5}> Connected Sessions
Connected Sessions </h5>
</h5> <CodeContent content={this.getSessionValue()} language="diff" />
<CodeContent content={this.getSessionValue()} language="diff" /> </div>,
</div> <div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div>
<div className={BDModules.get(e => e.marginBottom20)[0].marginBottom20}></div> ]
</div>)
} }
getSessionValue(){ getSessionValue(){

View File

@ -0,0 +1,232 @@
// Good luck to read my code, Even me can't read it properly.
import { stat } from "fs"
import { uuidv4 } from "../modules/distant"
import webpackModules from "../modules/webpackModules"
import { remote } from "electron"
import MarginTop from "./margintop"
const keys = {
settingTitle: uuidv4()
}
let formModule
export default class ApiPreview extends React.PureComponent {
constructor(){
super(...arguments)
this.state = {
states: []
}
}
render(){
if(!formModule)formModule = webpackModules.find(e => e.FormSection)
/**
* @type {Function[]}
*/
const allComponents = [...new Set(Object.keys(window.Lightcord.Api.Components).map(e => {
return Object.keys(window.Lightcord.Api.Components[e]).map(k => window.Lightcord.Api.Components[e][k])
}).flat())]
return [
<formModule.FormSection tag="h2" title="Lightcord's Api Availlable components">
<formModule.FormText type="description" className="" selectable={false}>
These components are here for the plugin devs. They can quickly embed any component below with this panel.
</formModule.FormText>
<MarginTop></MarginTop>
<Lightcord.Api.Components.inputs.Button color="brand" look="outlined" size="medium" hoverColor="green" onClick={() => {
remote.shell.openExternal("https://github.com/lightcord/lightcord/wiki/apis")
}} wrapper={false}>
Documentation
</Lightcord.Api.Components.inputs.Button>
</formModule.FormSection>,
allComponents.map(comp => {
let AllPreviews = []
if(comp.AllPreviews)AllPreviews = comp.AllPreviews
let onChange = (tab) => {
setState({
tab
})
}
let setState = (newState) => {
this.setState({
states: [Object.assign(state, newState)].concat(this.state.states.filter(e => e.elem !== comp))
})
}
let state = this.state.states.find(e => e.elem === comp)
if(!state){
state = {
tab: "preview",
elem: comp,
options: {}
}
this.state.states.push(state)
}
let getProps = () => {
let final = {}
AllPreviews.forEach(category => {
final[Object.keys(category[0])[0]] = category[0][Object.keys(category[0])[0]]
})
Object.keys(state.options).forEach(key => {
final[key] = AllPreviews.find(e => e.find(e => e[key]))[state.options[key]][key]
})
return final
}
let renderPreview = () => {
return <div style={{marginTop: "20px", marginBottom: "20px"}}>
<div style={{
backgroundColor: "var(--background-primary)",
padding: "30px 30px",
borderRadius: "8px"
}} className="lc-tab-box-shadow">
{React.createElement(comp, getProps())}
</div>
</div>
}
let renderCode = () => {
return <div style={{marginTop: "20px", marginBottom: "20px"}}>
<div style={{
backgroundColor: "var(--background-primary)",
padding: "30px 30px",
borderRadius: "8px"
}} className="lc-tab-box-shadow">
<window.Lightcord.Api.Components.general.SettingSubTitle>
JSX
</window.Lightcord.Api.Components.general.SettingSubTitle>
<window.Lightcord.Api.Components.general.CodeBlock language="jsx" content={generateCode("jsx")}/>
<window.Lightcord.Api.Components.general.SettingSubTitle>
React
</window.Lightcord.Api.Components.general.SettingSubTitle>
<window.Lightcord.Api.Components.general.CodeBlock language="js" content={generateCode("react")}/>
</div>
</div>
}
let getStrForProp = (value) => {
if(typeof value === "string"){
return value
}else if(typeof value === "boolean"){
return String(value)
}else if(typeof value === "function"){
return value.toString()
}else if(typeof value === "object"){
if(value && value.$$typeof && (value.$$typeof === Symbol.for("react.element") || value.$$typeof === 0xeac7)){
return "Your components here."
}
return JSON.stringify(value, null, " ")
}else if(typeof value === "number"){
return String(value)
}
return String(value)
}
let generateCode = function(lang){ // code formatting is hard
const compName = comp.displayName || comp.name
let categories = Object.keys(window.Lightcord.Api.Components)
const compCategory = categories.find(e => window.Lightcord.Api.Components[e][compName])
const compPath = `Lightcord.Api.Components.${compCategory}.${compName}`
const props = getProps()
if(lang === "jsx"){
let propStrings = []
let childrenProp = null
Object.keys(props).forEach(key => {
if(key == "children"){
childrenProp = getStrForProp(props[key])
}else{
let str = key+"="
if(typeof props[key] === "string"){
str += JSON.stringify(props[key])
}else{
str += `{${getStrForProp(props[key])}}`
}
propStrings.push(str)
}
})
let openTag
if(childrenProp){
openTag = `<${compPath} ${propStrings.join(" ")}>`
let closeTag = `</${compPath}>`
return `${openTag}\n ${childrenProp}\n${closeTag}`
}else{
openTag = `<${compPath} ${propStrings.join(" ")}/>`
return openTag
}
}else if(lang === "react"){
let children = props.children || null
delete props.children
if(children && children.$$typeof && (children.$$typeof === Symbol.for("react.element") || children.$$typeof === 0xeac7)){
children = "Your components here."
}
let propStrings = []
Object.keys(props).forEach(key => {
let visibleKey = /[^\w\d_]/g.test(key) ? JSON.stringify(key) : key
let str = visibleKey+": "
if(typeof props[key] === "string"){
str += JSON.stringify(props[key])
}else{
str += getStrForProp(props[key]).split("\n").map((str, i) => {
if(i === 0)return str
return " " + str
}).join("\n")
}
propStrings.push(str)
})
let propObject = "{"
if(propStrings.length){
propStrings.forEach((str, i) => {
let isLast = i === propStrings.length - 1
let isFirst = i === 0
if(!isFirst){
propObject += ","
}
propObject += "\n "
propObject += str
if(isLast){
propObject +="\n}"
}
})
}else{
propObject += "}"
}
return `React.createElement(${compPath}, ${propObject}, ${JSON.stringify(children)})`
}
}
return (<div>
<window.Lightcord.Api.Components.general.SettingsTitle>
{comp.displayName || comp.name}
</window.Lightcord.Api.Components.general.SettingsTitle>
{AllPreviews.map(category => {
if(category[0].onClick)return null
if(category[0].text)return null
if(category[0].children)return null
if(category.length === 1)return null
let key = Object.keys(category[0])[0]
return [
<window.Lightcord.Api.Components.general.SettingSubTitle>
{key}
</window.Lightcord.Api.Components.general.SettingSubTitle>,
<window.Lightcord.Api.Components.inputs.Dropdown options={category.map((e, index) => {
return {
value: "opt-"+index,
label: JSON.stringify(e[Object.keys(e)[0]])
}
})} value={"opt-"+(state.options[key] || "0")} onChange={(value) => {
setState({
options: Object.assign({}, state.options, {
[key]: (value.value || "0").replace("opt-", "")
})
})
}} searchable={true}/>,
<div style={{marginBottom: "8px"}}></div>
]
})}
<window.Lightcord.Api.Components.general.Tabs tabs={[{label: "Preview", id: "preview"}, {label: "Code", id: "code"}]}
active={state.tab} children={state.tab === "preview" ? renderPreview() : renderCode()} onChange={onChange}/>
</div>)
})
]
}
get renders(){
}
}

View File

@ -67,7 +67,7 @@ export default class V2C_PluginCard extends BDV2.reactComponent {
}; };
const thisNode = this.refs.cardNode; const thisNode = this.refs.cardNode;
const container = thisNode.closest(".scroller"); const container = thisNode.closest("div[class*=\"scroller-\"]")
if (!isHidden(container, thisNode)) return; if (!isHidden(container, thisNode)) return;
const thisNodeOffset = DOM.offset(thisNode); const thisNodeOffset = DOM.offset(thisNode);
const containerOffset = DOM.offset(container); const containerOffset = DOM.offset(container);

View File

@ -105,8 +105,12 @@ export default class CardList extends BDV2.reactComponent {
return typeof value == "string" ? value : value.toString(); return typeof value == "string" ? value : value.toString();
} }
get list(){
return this.props.type === "plugins" ? Object.values(bdplugins) : Object.values(bdthemes);
}
getAddons() { getAddons() {
const sortedAddons = this.props.list.sort((a, b) => { const sortedAddons = this.list.sort((a, b) => {
const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1); const cap = this.state.sort.charAt(0).toUpperCase() + this.state.sort.slice(1);
const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : a[this.state.sort]; const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : a[this.state.sort];
const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : b[this.state.sort]; const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : b[this.state.sort];
@ -153,28 +157,25 @@ export default class CardList extends BDV2.reactComponent {
}</Tooltip>; }</Tooltip>;
const addonCards = this.getAddons(); const addonCards = this.getAddons();
return <Scroller contentColumn={true} fade={true} dark={true}> return <ContentColumn title={`${this.props.type.toUpperCase()}${addonCards.length}`}>
<ContentColumn title={`${this.props.type.toUpperCase()}${addonCards.length}`}> <button key="folder-button" className="bd-button bd-pfbtn" onClick={this.openFolder.bind(this)}>Open {this.isPlugins ? "Plugin" : "Theme"} Folder</button>
<button key="folder-button" className="bd-button bd-pfbtn" onClick={this.openFolder.bind(this)}>Open {this.isPlugins ? "Plugin" : "Theme"} Folder</button> {!settingsCookie["fork-ps-5"] && refreshIcon}
{!settingsCookie["fork-ps-5"] && refreshIcon} <div className="bd-controls bd-addon-controls">
<div className="bd-controls bd-addon-controls"> <Search onChange={this.search} placeholder={`Search ${this.props.type}...`} />
<Search onChange={this.search} placeholder={`Search ${this.props.type}...`} /> <div className="bd-addon-dropdowns">
<div className="bd-addon-dropdowns"> <div className="bd-select-wrapper">
<div className="bd-select-wrapper"> <label className="bd-label">Sort by:</label>
<label className="bd-label">Sort by:</label> <Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
<Dropdown options={this.sortOptions} onChange={this.sort} style="transparent" />
</div>
<div className="bd-select-wrapper">
<label className="bd-label">Order:</label>
<Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
</div>
</div>
</div> </div>
<div className="bda-slist bd-addon-list">{addonCards}</div> <div className="bd-select-wrapper">
</ContentColumn> <label className="bd-label">Order:</label>
<Tools key="tools" /> <Dropdown options={this.directions} onChange={this.reverse} style="transparent" />
</Scroller>; </div>
</div>
</div>
<div className="bda-slist bd-addon-list">{addonCards}</div>
</ContentColumn>
} }
} }

View File

@ -8,15 +8,16 @@ export default class V2C_ContentColumn extends BDV2.reactComponent {
static get displayName() {return "ContentColumn";} static get displayName() {return "ContentColumn";}
render() { render() {
let contentModule = BDModules.get(e => e.contentColumn)[0] //let contentModule = BDModules.get(e => e.contentColumn)[0]
let title = this.props.title ? BDV2.react.createElement("h2", {className: "ui-form-title h2 margin-reset margin-bottom-20"}, this.props.title) : null
return [
title,
this.props.children
]
return BDV2.react.createElement( return BDV2.react.createElement(
"div", "div",
{className: contentModule.contentColumn + " "+contentModule.contentColumnDefault+" content-column default"}, {className: contentModule.contentColumn + " "+contentModule.contentColumnDefault+" content-column default", style: {padding: "60px 40px 0px"}},
BDV2.react.createElement( title,
"h2",
{className: "ui-form-title h2 margin-reset margin-bottom-20"},
this.props.title
),
this.props.children this.props.children
); );
} }

View File

@ -87,10 +87,7 @@ export default class V2C_CssEditor extends BDV2.reactComponent {
const self = this; const self = this;
const {detached} = self.state; const {detached} = self.state;
let contentModule = BDModules.get(e => e.contentColumn)[0] return [
return BDV2.react.createElement(
"div",
{className: contentModule.contentColumn+" "+contentModule.contentColumnDefault+" content-column default", style: {padding: "60px 40px 0px"}},
detached && BDV2.react.createElement( detached && BDV2.react.createElement(
"div", "div",
{id: "editor-detached"}, {id: "editor-detached"},
@ -162,7 +159,7 @@ export default class V2C_CssEditor extends BDV2.reactComponent {
) )
) )
) )
); ]
} }
onClick(arg) { onClick(arg) {

View File

@ -0,0 +1,8 @@
let marginModule = BDModules.get(e => e.marginTop60)[0]
export default class MarginTop extends React.Component {
render(){
if(!marginModule)marginModule = BDModules.get(e => e.marginTop60)[0]
return <div className={marginModule.marginTop60}></div>
}
}

View File

@ -10,6 +10,7 @@ import { defaultRPC, settingsRPC } from "../0globals";
import CustomRichPresence from "../modules/CustomRichPresence" import CustomRichPresence from "../modules/CustomRichPresence"
import timestampRender from "./timestampRender" import timestampRender from "./timestampRender"
import { remote } from "electron"; import { remote } from "electron";
import MarginTop from "./margintop";
const React = BDV2.React; const React = BDV2.React;
@ -114,7 +115,6 @@ export default class V2C_PresenceSettings extends React.Component {
get modules(){ get modules(){
return V2C_PresenceSettingsModules || (V2C_PresenceSettingsModules = [ return V2C_PresenceSettingsModules || (V2C_PresenceSettingsModules = [
BDModules.get(e => e.contentColumn)[0],
BDModules.get(e => e.marginBottom20)[0] BDModules.get(e => e.marginBottom20)[0]
]) ])
} }
@ -122,32 +122,32 @@ export default class V2C_PresenceSettings extends React.Component {
render() { render() {
console.log("Rerendering rpc manager") console.log("Rerendering rpc manager")
let [ let [
contentModule,
marginModule marginModule
] = this.modules ] = this.modules
return (<div className={contentModule.contentColumn+" "+contentModule.contentColumnDefault+" content-column default"} return [
style={{padding: "60px 40px 0px"}}> <V2C_SettingsGroup title="RichPresence Settings" settings={this.props.settings} onChange={this.props.onChange}/>,
<V2C_SettingsGroup title="RichPresence Settings" settings={this.props.settings} onChange={this.props.onChange}/> <MarginTop />,
<V2C_SettingsTitle text="RichPresence"/> <V2C_SettingsTitle text="RichPresence"/>,
<div> <div>
{/** options */} {/** options */}
{this.optionsComponents} {this.optionsComponents}
</div> </div>,
<div> <div>
<V2C_SettingsTitle text="Preview"/> <MarginTop />
{/** preview */} <V2C_SettingsTitle text="Preview"/>
<RpcPreview settings={this}/> {/** preview */}
</div> <RpcPreview settings={this}/>
<div className={marginModule.marginBottom20}></div> </div>,
<button style={{opacity: 0.01}} onClick={window.ohgodohfuck}> <div className={marginModule.marginBottom20}></div>,
Oh god Oh fuck <button style={{opacity: 0.01}} onClick={window.ohgodohfuck}>
</button> Oh god Oh fuck
<button style={{opacity: 0.01}} onClick={() => { </button>,
remote.shell.openExternal("https://www.youtube.com/watch?v=LkYa7rps_g4") <button style={{opacity: 0.01}} onClick={() => {
}}> remote.shell.openExternal("https://www.youtube.com/watch?v=LkYa7rps_g4")
See ? I pulled a litle sneaky on ya }}>
</button> See ? I pulled a litle sneaky on ya
</div>) </button>
]
} }
get optionsComponents(){ get optionsComponents(){
@ -354,9 +354,9 @@ class InputNumber extends React.PureComponent {
</h5> </h5>
{this.input} {this.input}
{setting.id === "timestamps.start" ? {setting.id === "timestamps.start" ?
<Lightcord.Api.Components.inputs.Button text="Copy current timestamp" onClick={() => { <Lightcord.Api.Components.inputs.Button onClick={() => {
DiscordNative.clipboard.copy(Date.now()+"") DiscordNative.clipboard.copy(Date.now()+"")
}} color="brand"/> : null} }} color="brand">Copy current timestamp</Lightcord.Api.Components.inputs.Button> : null}
</div> </div>
<Divider/> <Divider/>
</div>) </div>)
@ -468,7 +468,9 @@ class DiscordButton extends React.Component {
return (<div className={rowModule.row+" "+marginModule.marginBottom20}> return (<div className={rowModule.row+" "+marginModule.marginBottom20}>
<div className={`${rowModule.item} ${flexModule.flexChild}`}> <div className={`${rowModule.item} ${flexModule.flexChild}`}>
<Lightcord.Api.Components.inputs.Button text={this.props.title} onClick={this.props.onClick} color="brand"/> <Lightcord.Api.Components.inputs.Button onClick={this.props.onClick} color="brand">
{this.props.title}
</Lightcord.Api.Components.inputs.Button>
</div> </div>
</div>) </div>)
} }
@ -612,7 +614,7 @@ class Popout extends React.Component {
</div> </div>
})() })()
return (<div className="lc-userPopout"> return (<div className="lc-userPopout lc-tab-box-shadow">
<div class={rootModule1.userPopout} role="dialog" tabindex="-1"> <div class={rootModule1.userPopout} role="dialog" tabindex="-1">
<div class={rootModule1.headerPlaying}> <div class={rootModule1.headerPlaying}>
<div class={`${flexModule1.flex} ${stylingModule1.vertical} ${stylingModule1.justifyCenter} ${stylingModule1.alignCenter} ${stylingModule1.noWrap} ${rootModule1.headerTop}`} style={{flex: "1 1 auto"}}> <div class={`${flexModule1.flex} ${stylingModule1.vertical} ${stylingModule1.justifyCenter} ${stylingModule1.alignCenter} ${stylingModule1.noWrap} ${rootModule1.headerTop}`} style={{flex: "1 1 auto"}}>
@ -773,7 +775,7 @@ class Profile extends React.Component {
})() })()
return [ return [
<div className="lc-tab"> <div className="lc-tab lc-tab-box-shadow">
<div class={`${flexModule1.flex} ${stylingModule1.vertical} ${stylingModule1.justifyStart} ${stylingModule1.alignStretch} ${stylingModule1.noWrap} ${rootModule1.root}`} style={{flex: "1 1 auto"}}> <div class={`${flexModule1.flex} ${stylingModule1.vertical} ${stylingModule1.justifyStart} ${stylingModule1.alignStretch} ${stylingModule1.noWrap} ${rootModule1.root}`} style={{flex: "1 1 auto"}}>
<div class={rootModule1.topSectionPlaying}> <div class={rootModule1.topSectionPlaying}>
<header class={rootModule1.header}> <header class={rootModule1.header}>
@ -854,8 +856,7 @@ class Profile extends React.Component {
</div> </div>
</div> </div>
</div> </div>
</div>, </div>
<div class="lc- "></div>
] ]
} }
} }

View File

@ -1,6 +1,7 @@
import BDV2 from "../modules/v2"; import BDV2 from "../modules/v2";
import SettingsGroup from "./settingsGroup"; import SettingsGroup from "./settingsGroup";
import MarginTop from "./margintop";
export default class V2C_SectionedSettingsPanel extends BDV2.reactComponent { export default class V2C_SectionedSettingsPanel extends BDV2.reactComponent {
@ -9,12 +10,11 @@ export default class V2C_SectionedSettingsPanel extends BDV2.reactComponent {
} }
render() { render() {
let columnModule = BDModules.get(e => e.contentColumnDefault)[0] return this.props.sections.map((section, i) => {
return BDV2.react.createElement( return [
"div", {className: columnModule.contentColumn+" "+columnModule.contentColumnDefault+" content-column default"}, i !== 0 ? BDV2.react.createElement(MarginTop, {}) : null,
this.props.sections.map(section => { BDV2.react.createElement(SettingsGroup, Object.assign({}, section, {onChange: this.props.onChange}))
return BDV2.react.createElement(SettingsGroup, Object.assign({}, section, {onChange: this.props.onChange})); ]
}) })
);
} }
} }

View File

@ -8,7 +8,7 @@ export default class V2C_SettingsTitle extends BDV2.reactComponent {
render() { render() {
return BDV2.react.createElement( return BDV2.react.createElement(
"h2", "h2",
{className: "ui-form-title h2 margin-reset margin-bottom-20 "+BDModules.get(e => e.marginTop60)[0].marginTop60}, {className: "ui-form-title h2 margin-reset margin-bottom-20"},
this.props.text this.props.text
); );
} }

21
DiscordJS/README.md Normal file
View File

@ -0,0 +1,21 @@
# Lightcord's Discord.js
### What is this ?
Lightcord includes a Discord.js-like api. This is written in this folder.
### Where can I get the documentation ?
[Documentation here](https://discord.js.org/#/docs/main/11.6.4/general/welcome)
Discord.js on Lightcord is following Discord.js 11.6.4 specs.
### How do I use it ?
Discord.js can be accessed under the following global properties
| Name | Global Properties | alias | Limitations |
|------------------------------|-------------------------|-------------------------|-----------------------------------------------------------------------|
| DiscordJS main module | window.DiscordJS | none | none |
| DiscordJS client | window.DiscordJSClient | window.DiscordJS.client | none |
| DiscordJS Client constructor | window.DiscordJS.Client | none | Can't be constructed because there's no need for more than one client |
### Deprecations
1. Any method that only bot can use (it will throw a `DiscordJSError` saying that Lightcord can't do that)
3. Any method

1729
DiscordJS/js/main.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,248 @@
"version": "0.1.5", "version": "0.1.5",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.5.tgz", "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.5.tgz",
"integrity": "sha512-CU1q0UXQUpFNzNB7gufgoisDHP7n+T3tkqTsp3MNUkVJ5+hS3BCvME8uCXAUFlz+6T2FbTCu75A+yQ7HMKqRKw==" "integrity": "sha512-CU1q0UXQUpFNzNB7gufgoisDHP7n+T3tkqTsp3MNUkVJ5+hS3BCvME8uCXAUFlz+6T2FbTCu75A+yQ7HMKqRKw=="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"requires": {
"fill-range": "^7.0.1"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
},
"enhanced-resolve": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz",
"integrity": "sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==",
"requires": {
"graceful-fs": "^4.1.2",
"memory-fs": "^0.5.0",
"tapable": "^1.0.0"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"requires": {
"prr": "~1.0.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"requires": {
"to-regex-range": "^5.0.1"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
"memory-fs": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
"integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
"requires": {
"errno": "^0.1.3",
"readable-stream": "^2.0.1"
}
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"picomatch": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"tapable": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA=="
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"requires": {
"is-number": "^7.0.0"
}
},
"ts-loader": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-7.0.5.tgz",
"integrity": "sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig==",
"requires": {
"chalk": "^2.3.0",
"enhanced-resolve": "^4.0.0",
"loader-utils": "^1.0.2",
"micromatch": "^4.0.0",
"semver": "^6.0.0"
}
},
"typescript": {
"version": "3.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz",
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
"dev": true
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
} }
} }
} }

View File

@ -4,11 +4,16 @@
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "watch": "tsc --watch",
"build": "tsc"
}, },
"author": "jeanouina", "author": "jeanouina",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@discordjs/collection": "^0.1.5" "@discordjs/collection": "^0.1.5",
"ts-loader": "^7.0.5"
},
"devDependencies": {
"typescript": "^3.9.5"
} }
} }

View File

@ -2,25 +2,52 @@ import { EventEmitter } from "events";
import DiscordJSError from "../util/DiscordJSError"; import DiscordJSError from "../util/DiscordJSError";
import Collection from "@discordjs/collection"; import Collection from "@discordjs/collection";
import { Snowflake, Channel } from ".."; import { Snowflake, Channel } from "..";
import { channelsModule } from "../util/DiscordToModules"; import { channelsModule, guildModule, UserModule, DispatcherModule } from "../util/DiscordToModules";
import { createChannel } from "../util/util"; import { createChannel, createGuild, createMessage, createUser } from "../util/util";
import Guild from "../structures/Guild";
import ClientUser from "../structures/ClientUser";
import User from "../structures/User";
let hasInit = false let hasInit = false
function setupEvents(client:Client){
let dispatcher = DispatcherModule
dispatcher.subscribe("CONNECTION_OPEN", () => {
client.emit("self.ready")
})
dispatcher.subscribe("MESSAGE_CREATE", action => {
if(action.optimistic)return // disable not sent messages
action.message && client.emit("self.messageCreate", action.message)
})
}
export default class Client extends EventEmitter { export default class Client extends EventEmitter {
constructor(){ constructor(){
super()
if(hasInit)throw new DiscordJSError("Cannot initialized more than one client.") if(hasInit)throw new DiscordJSError("Cannot initialized more than one client.")
hasInit = true hasInit = true
super()
setupEvents(this)
this.on("self.ready", () => {
try{
this.user = new ClientUser(UserModule.getCurrentUser())
console.log(this.user)
}catch(e){
console.error(e)
console.log(UserModule.getCurrentUser, UserModule, typeof UserModule.getCurrentUser)
}
this.emit("ready")
})
this.on("self.messageCreate", (message) => {
this.emit("messageCreate", createMessage(message))
})
} }
/** Warnings and overrides for functions that are not compatible. */ user:ClientUser = null
async login():Promise<never>{
throw new DiscordJSError("Client#login is not supported. DiscordJS on lightcord will use the connected account.")
}
get token():never{
throw new DiscordJSError("Client#token is not supported. DiscordJS on lightcord will use the connected account.")
}
get broadcasts(){ get broadcasts(){
return [] // not giving any since they're not supported. return [] // not giving any since they're not supported.
@ -34,4 +61,27 @@ export default class Client extends EventEmitter {
const channels = Object.values(channelsModule.getAllChannels()) const channels = Object.values(channelsModule.getAllChannels())
return new Collection(channels.map(e => ([e.id, createChannel(e)]))) return new Collection(channels.map(e => ([e.id, createChannel(e)])))
} }
get emojis():Collection<Snowflake, any /* Emoji */>{ // TODO: Return Emojis
return new Collection()
}
get guilds():Collection<Snowflake, Guild>{
const channels = Object.values(guildModule.getAllGuilds())
return new Collection(channels.map(e => ([e.id, createGuild(e)])))
}
get users():Collection<Snowflake, User>{
const users = Object.values(UserModule.getUsers())
return new Collection(users.map(e => [e.id, createUser(e)]))
}
/** Warnings and overrides for functions that are not compatible. */
async login():Promise<never>{
throw new DiscordJSError("Client#login is not supported. DiscordJS on lightcord will use the connected account.")
}
get token():never{
throw new DiscordJSError("Client#token is not supported. DiscordJS on lightcord will use the connected account.")
}
} }

View File

@ -6,10 +6,12 @@ import Snowflake from "../util/Snowflake"
export default class BaseChannel extends BaseStructure { export default class BaseChannel extends BaseStructure {
id:string id:string
deleted:boolean deleted:boolean
DiscordChannel:DiscordChannel
constructor(channel:DiscordChannel){ constructor(channel:DiscordChannel){
super() super()
this.id = channel.id this.id = channel.id
this.deleted = false this.deleted = false
this.DiscordChannel = channel
} }
get createdAt():Date{ get createdAt():Date{
@ -21,5 +23,5 @@ export default class BaseChannel extends BaseStructure {
delete() { delete() {
return channelsModule.delete(this.id); return channelsModule.delete(this.id);
} }
} }

View File

@ -0,0 +1,6 @@
import User from "./User";
import { DiscordUser } from "../util/DiscordToModules";
export default class ClientUser extends User {
}

View File

@ -0,0 +1,299 @@
import { Snowflake, Channel } from ".."
import { DiscordGuild, channelsModule, guildModule, UserSettingsModule, ConstantsModule, CdnModule, AckModule } from "../util/DiscordToModules"
import BaseStructure from "./BaseStructure"
import { createChannel, createGuildMember, createRole, UserResolvable, resolveUserID } from "../util/util"
import Collection from "@discordjs/collection"
import SnowflakeUtil from "../util/Snowflake"
import GuildMember from "./GuildMember"
import { MessageNotificationType } from "../util/Constants"
import Role from "./Role"
import DiscordJSError from "../util/DiscordJSError"
export default class Guild extends BaseStructure {
DiscordGuild:DiscordGuild
constructor(data:DiscordGuild){
super()
this.DiscordGuild = data
}
get id(): Snowflake{
return this.DiscordGuild.id
}
get afkChannel():Channel{
if(!this.afkChannelID)return null
return createChannel(channelsModule.getChannel(this.afkChannelID))
}
get afkChannelID():Snowflake{
return this.DiscordGuild.afkChannelId
}
get afkTimeout():number{
return this.DiscordGuild.afkTimeout
}
get applicationID():Snowflake{
return this.DiscordGuild.application_id
}
get available():boolean{
return true
}
get channels():Collection<Snowflake, Channel>{{
return this.client.channels.filter(channel => channel.guild_id === this.id)
}}
get createdAt():Date{
return SnowflakeUtil.deconstruct(this.id).date
}
get createdTimestamp():number{
return this.createdAt.getTime()
}
get defaultChannel(){
return this.channels.get(this.id)
}
get defaultMessageNotifications():number{
return this.DiscordGuild.defaultMessageNotifications
}
deleted:boolean = false
get embedEnabled(){
return true
}
get emojis(){
return this.client.emojis.filter(e => e.guild_id === this.id)
}
get explicitContentFilter(){
return this.DiscordGuild.explicitContentFilter
}
get features(){
return Array.from(this.DiscordGuild.features)
}
get icon(){
return this.DiscordGuild.icon
}
get iconURL(){
return this.DiscordGuild.getIconURL().replace(".webp", ".jpg")
}
get joinedAt(){
return new Date(this.DiscordGuild.joinedAt)
}
get joinedTimestamp(){
return this.DiscordGuild.joinedAt.getTime()
}
get large(){
return false
}
get me(){
return this.members.find(member => member.id === this.client.user.id)
}
get memberCount(){
return guildModule.getMemberCount(this.id)
}
get members(){
return new Collection<Snowflake, GuildMember>(guildModule.getMembers(this.id).map(member => [member.userId, createGuildMember(member)]))
}
get messageNotifications():MessageNotificationType{
return MessageNotificationType[guildModule.getMessageNotifications(this.id)] as unknown as MessageNotificationType
}
get mfaLevel(){
return this.DiscordGuild.mfaLevel
}
get mobilePush():boolean{
return guildModule.getNotificationsState().userGuildSettings[this.id].mobile_push
}
get muted(){
return guildModule.getNotificationsState().userGuildSettings[this.id].muted
}
get name(){
return this.DiscordGuild.name
}
get nameAcronym(){
return this.DiscordGuild.acronym
}
get owner(){
return this.members.get(this.ownerID)
}
get ownerID(){
return this.DiscordGuild.ownerId
}
get position(){
let guildPositions = UserSettingsModule.getAllSettings().guildPositions
if(!guildPositions)return 0
return guildPositions.indexOf(this.id)
}
get presences(){ // TODO: Add activities.
return new Collection()
}
get region(){
return this.DiscordGuild.region
}
get roles(){
return new Collection<Snowflake, Role>(Object.values(this.DiscordGuild.roles).map(role => [role.id, createRole(role)]))
}
get splash(){
return this.DiscordGuild.splash
}
get splashURL(){
return CdnModule.getGuildSplashURL({
id: this.id,
splash: this.splash,
size: ConstantsModule.SPLASH_SIZE
})
}
get suppressEveryone(){
return guildModule.getNotificationsState().userGuildSettings[this.id].suppress_everyone
}
get systemChannel(){
return this.client.channels.get(this.systemChannelID)
}
get systemChannelID(){
return this.DiscordGuild.systemChannelId
}
get verificationLevel(){
return this.DiscordGuild.verificationLevel
}
get verified(){
return this.features.includes("VERIFIED")
}
get voiceConnection(){// TODO: Implement if possible VoiceConnection. Maybe not fully like discord.js, but at least can see if you're connected.
return null
}
get banner(){
return this.DiscordGuild.banner
}
get bannerURL(){
return CdnModule.getGuildBannerURL({
id: this.id,
banner: this.banner
})
}
get description(){
return this.DiscordGuild.description
}
get embedChannel(){ // TODO: Implement embedChannel
return null
}
get embedChannelID(){ // TODO: Implement embedChannelID
return null
}
get maximumMembers(){ // TODO: Correctly Implement maximumMembers if possible
return 250000
}
get maximumPresences(){ // TODO: Correctly Implement maximumPresences if possible
return 5000
}
get widgetEnabled(){ // TODO: Correctly Implement widgetEnabled if possible
return false
}
get widgetChannelID(){ // TODO: Correctly Implement widgetChannelID if possible
return null
}
get widgetChannel(){ // TODO: Correctly Implement widgetChannel if possible
return null
}
get vanityURLCode(){
return this.DiscordGuild.vanityURLCode
}
/** FUNCTIONS */
async acknowledge(){
AckModule.bulkAck(this.channels.filter(e => e.type === "text").map(e => {
return {
channelId: e.id,
messageId: channelsModule.lastMessageId(e.id)
}
}))
}
addMember(...args:any):Promise<GuildMember>{
return Promise.reject(new DiscordJSError("This method is not available on Lightcord."))
}
allowDMs(allow:boolean){
let restricted = UserSettingsModule.getAllSettings().restrictedGuilds
if(allow){
if(!restricted.includes(this.id))return Promise.resolve(this)
restricted = restricted.filter(e => e !== this.id)
}else{
if(restricted.includes(this.id))return Promise.resolve(this)
restricted.push(this.id)
}
return UserSettingsModule.updateRemoteSettings({
restrictedGuilds: restricted
}).then(() => this)
}
ban(user:UserResolvable, {
days = 0,
reason = null
}: {
days?: number,
reason?: string
}){
let id = resolveUserID(user)
if(!id)return Promise.reject(new DiscordJSError("Given user could not be resolved to an user ID."))
}
fetch():Promise<Guild>{ // Guild is synced by Discord. Only refreshing from cache.
let guild = guildModule.getGuild(this.id)
if(!guild){
this.deleted = true
return Promise.resolve(this)
}
this.DiscordGuild = guild
return Promise.resolve(this)
}
}

View File

@ -0,0 +1,17 @@
import BaseChannel from "./BaseChannel";
import { DiscordChannel, guildModule } from "../util/DiscordToModules";
import { createChannel, createGuild } from "../util/util";
export default class GuildChannel extends BaseChannel {
constructor(channel:DiscordChannel){
super(channel)
}
get guild(){
return createGuild(guildModule.getGuild(this.DiscordChannel.guild_id))
}
get guild_id(){
return this.DiscordChannel.guild_id
}
}

View File

@ -0,0 +1,14 @@
import BaseStructure from "./BaseStructure";
import { DiscordGuildMember } from "../util/DiscordToModules";
export default class GuildMember extends BaseStructure {
DiscordGuildMember: DiscordGuildMember
constructor(data: DiscordGuildMember){
super()
this.DiscordGuildMember = data
}
get id(){
return this.DiscordGuildMember.userId
}
}

View File

@ -0,0 +1,15 @@
import BaseStructure from "./BaseStructure";
import { DiscordMessage, UserModule } from "../util/DiscordToModules";
import User from "./User";
export default class Message extends BaseStructure {
DiscordMessage:DiscordMessage
constructor(data:DiscordMessage){
super()
this.DiscordMessage = data
}
get author(){
return new User(UserModule.getUser(this.DiscordMessage.author.id))
}
}

View File

@ -0,0 +1,7 @@
import BaseStructure from "./BaseStructure";
export default class Presence extends BaseStructure {
constructor(data){
super()
}
}

View File

@ -0,0 +1,10 @@
import BaseStructure from "./BaseStructure";
import { DiscordRole } from "../util/DiscordToModules";
export default class Role extends BaseStructure {
DiscordRole:DiscordRole
constructor(data:DiscordRole){
super()
this.DiscordRole = data
}
}

View File

@ -1,3 +1,14 @@
export class TextChannel extends TextBasedChannel, GuildChannel { import { applyMixins } from "../util/util";
import GuildChannel from "./GuildChannel";
import { DiscordChannel } from "../util/DiscordToModules";
import { ChannelTypes } from "../util/Constants";
export class TextChannel extends GuildChannel/* implements TextBasedChannel*/ {
constructor(data:DiscordChannel){
super(data)
}
get type(): ChannelTypes.TEXT{
return ChannelTypes.TEXT
}
} }

View File

@ -0,0 +1,74 @@
import BaseStructure from "./BaseStructure";
import { DiscordUser, CdnModule, UserModule } from "../util/DiscordToModules";
export default class User extends BaseStructure {
DiscordUser:DiscordUser
constructor(data:DiscordUser){
super()
this.DiscordUser = data
}
get id(){
return this.DiscordUser.id
}
get avatar(){
return this.DiscordUser.avatar
}
get avatarURL(){
return this.DiscordUser.avatarURL
}
get bot(){
return this.DiscordUser.bot
}
get createdAt(){
return new Date(this.DiscordUser.createdAt)
}
get createdTimestamp(){
return this.createdAt.getTime()
}
get defaultAvatarURL(){
return CdnModule.getDefaultAvatarURL(this.discriminator)
}
get discriminator(){
return this.DiscordUser.discriminator
}
get displayAvatarURL(){
return CdnModule.getUserAvatarURL({
id: this.id,
avatar: this.avatar,
bot: this.bot,
discriminator: this.discriminator
}, "png", 4096)
}
get dmChannel(){
return this.client.channels.find(e => e.type === "dm" && e.recipient.id === this.id)
}
get lastMessage(){ // TODO: Implement User#lastMessage
return null
}
get lastMessageID(){ // TODO: Implement User#lastMessageID
return null
}
get note(){
let note = UserModule.getNote(this.id)
if(!note || !note.note)return null
return note.note
}
get presence(){
return null
}
}

View File

@ -0,0 +1,22 @@
import { Snowflake } from ".."
export enum ChannelTypes {
TEXT = "text",
DM = "dm",
VOICE = "voice",
GROUP_DM = "group",
CATEGORY = "category",
NEWS = "news",
STORE = "store"
}
export enum MessageNotificationType {
"EVERYTHING",
"MENTIONS",
"NOTHING",
"INHERIT"
}
export type Status = "online" | "idle" | "invisible" | "offline" | "dnd"
export const AVATAR_SIZE = 4096

View File

@ -1,4 +1,6 @@
import { Snowflake } from ".." import { Snowflake } from ".."
import { lazyLoad } from "./LazyLoader"
import { Status } from "./Constants"
export default function getModule(name:string){ export default function getModule(name:string){
return exports[name+"Module"] return exports[name+"Module"]
@ -6,35 +8,220 @@ export default function getModule(name:string){
const BDModules = window.BDModules const BDModules = window.BDModules
function requireModule(filter: (module:any) => boolean){ export function requireModule(filter: (module:any) => boolean){
let module = BDModules.get(filter)[0] let module = BDModules.get(filter)[0]
return module && module.default || module return module && module.default || module
} }
const channelsModuleInternal1:{ export const channelsModule = lazyLoad<typeof import("./modules/channels")>("channels")
getChannel(id:Snowflake): DiscordChannel,
getChannels(): { export const guildModule = lazyLoad<typeof import("./modules/guilds")>("guilds")
[k:string]: DiscordChannel
export const UserSettingsModule = lazyLoad<typeof import("./modules/userSettings")>("userSettings")
export const ConstantsModule = lazyLoad<typeof import("./modules/constants")>("constants")
export const UserModule = lazyLoad<typeof import("./modules/users")>("users")
export const CdnModule = lazyLoad<typeof import("./modules/cdn")>("cdn")
export const AckModule = lazyLoad<typeof import("./modules/acknowledge")>("acknowledge")
export const DispatcherModule = lazyLoad<typeof import("./modules/dispatcher")>("dispatcher")
export interface DiscordMessage {
guild_id?: Snowflake,
attachments: {
filename: string,
height: number,
id: Snowflake,
proxy_url: string,
size: number,
url: string,
width: number
}[],
author: {
avatar?: string,
discriminator: string,
id: Snowflake,
username: string,
publicFlags?: number
}, },
getDMFromUserId(id:string):Snowflake, channel_id: Snowflake,
getDMUserIds():Snowflake[], content: string,
getFollowerStatsForChannel(id: Snowflake):{ edited_timestamp?: number,
loadingStatus: "succeeded"|"failed", embeds: any[],
lastFetched: number, flags: number,
channelsFollowing: number, id: Snowflake,
guildMembers: number, member: {
guildsFollowing: number, deaf: boolean,
usersSeenEver: number, hoisted_role?: Snowflake,
subscribersGainedSinceLastPost: number, joined_at: string,
subscribersLostSinceLastPost: number mute: boolean,
roles: Snowflake[]
}, },
getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake> mention_roles: Snowflake[],
} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel) mentions: Snowflake[],
export const channelsModule = { pinned: boolean,
getChannel: channelsModuleInternal1.getChannel, timestamp: string,
getAllChannels: channelsModuleInternal1.getChannels tts: boolean,
type: number
} }
export interface DiscordUser {
createdAt: Date,
avatarURL: string,
tag: string,
hasPremiumSubscription: boolean,
id: Snowflake,
username: string,
usernameNormalized: string,
discriminator: string,
avatar?: string,
email?: string,
verified: boolean,
bot: boolean,
system: boolean,
mfaEnabled: boolean,
mobile: boolean,
desktop: boolean,
premiumType?: undefined|1|2,
flags: number,
publicFlags: number,
phone?: string,
nsfwAllowed?: boolean,
getAvatarURL(format:string):string,
getAvatarSource():string,
isClaimed(): boolean,
isPhoneVerified(): boolean,
toString(): string,
hasFlag(flag:number):boolean,
hasFreePremium():boolean,
hasUrgentMessages():boolean,
isNonUserBot():boolean,
isLocalBot():boolean,
isVerifiedBot():boolean,
isSystemUser():boolean,
isStaff():boolean
}
export interface DiscordUserSettings {
locale: string,
status: Status,
showCurrentGame: boolean,
inlineAttachmentMedia: boolean,
inlineEmbedMedia: boolean,
gifAutoPlay: boolean,
renderEmbeds: boolean,
renderReactions: boolean,
animateEmoji: boolean,
showInAppNotifications: boolean,
streamNotificationsEnabled: boolean,
theme: "dark"|"light",
enableTTSCommand: boolean,
useRichChatTextBox: boolean,
messageDisplayCompact: boolean,
convertEmoticons: boolean,
renderSpoilers: "ALWAYS"|"ON_CLICK"|"IF_MODERATOR",
restrictedGuilds: Snowflake[],
defaultGuildsRestricted: boolean,
explicitContentFilter: number,
friendSourceFlags: {
all?: boolean,
friends?: boolean,
guilds?: boolean
},
developerMode: boolean,
guildPositions: Snowflake[],
guildFolders: {
guildIds: Snowflake[],
folderId?: number,
folderName?: string,
folderColor?: number
},
detectPlatformAccounts: boolean,
afkTimeout: number,
timezoneOffset: number,
installShortcutDesktop: boolean,
installShortcutStartMenu: boolean,
customStatus: {
text: string,
expires_at: string
emoji_id?: string
emoji_name?: string
},
lowQualityImageMode: boolean,
allowAccessibilityDetection: boolean,
contactSyncEnabled: boolean,
nativePhoneIntegrationEnabled: boolean,
useSystemTheme: number,
disableGamesTab: boolean,
expressionPickerWidth: "min"|"max",
emojiPickerCollapsedSections: string[],
sync: {}
}
export interface DiscordGuild {
acronym: string,
id: Snowflake,
name: string,
description?: string,
ownerId: Snowflake,
icon?: string,
splash?: string,
banner?: string,
features: Set<Feature>,
preferredLocale: string,
roles: {
[ID: string]: DiscordRole
},
afkChannelId?: Snowflake,
afkTimeout: number,
systemChannelId?: Snowflake,
verificationLevel: number,
region: string,
joinedAt: Date,
defaultMessageNotifications: number,
mfaLevel: number,
application_id?: Snowflake,
explicitContentFilter: number,
vanityURLCode?: string,
premiumTier: number,
premiumSubscriberCount: number,
systemChannelFlags: number,
discoverySplash?: string,
rulesChannelId?: Snowflake,
publicUpdatesChannelId?: Snowflake,
maxVideoChannelUsers: number,
getIconURL(format?: string):string,
getIconSource(format?: string):string,
getSplashURL(size: number):string,
getApplicationId():Snowflake,
toString():string,
isOwner(user_id: Snowflake):boolean,
isOwnerWithRequiredMfaLevel(user_id: Snowflake):boolean,
isNew(days: number):boolean,
hasFeature(feature: Feature): boolean,
getRole(role_id: Snowflake): DiscordRole,
getMaxEmojiSlots():number
}
export interface DiscordRole {
color: number,
hoist: boolean,
id: Snowflake,
managed: boolean,
mentionnable: boolean,
name: string,
permissions: number,
position: number,
originalPosition: number,
colorString: string
}
export type Feature = "INVITE_SPLASH" | "VIP_REGIONS" | "VANITY_URL" | "VERIFIED" | "PARTNERED" | "PUBLIC" | "COMMERCE" | "NEWS" |
"DISCOVERABLE" | "FEATURABLE" | "ANIMATED_ICON" | "BANNER" | "PUBLIC_DISABLED" | "WELCOME_SCREEN_ENABLED"
export interface DiscordChannel { export interface DiscordChannel {
application_id?: Snowflake, application_id?: Snowflake,
bitrate?: number, bitrate?: number,
@ -68,3 +255,12 @@ interface DiscordRecipient {
avatar?: string, avatar?: string,
public_flags: number public_flags: number
} }
export interface DiscordGuildMember {
userId: Snowflake,
nick?: string,
roles: DiscordRole[],
colorString?: string,
hoistRoleId?: Snowflake,
premiumSince?: Date
}

View File

@ -0,0 +1,35 @@
const cache = {}
export function lazyLoad<T>(id:string):T{
if(cache[id])return cache[id]
let mdl = null
let setModule = () => {
if(!mdl)mdl = require("./modules/"+id)
}
const handlers:ProxyHandler<{}> = {
get(target, prop){
setModule()
return mdl[prop]
},
set(target, prop, value){
setModule()
mdl[prop] = value
return true
},
apply(target, thisArg, args){
setModule()
mdl.apply(this, args)
},
construct(target, args){
setModule()
const prototype = Object.create(mdl.prototype)
handlers.apply(target, prototype, args)
return prototype
}
}
let proxy = new Proxy({}, handlers)
return cache[id] = proxy as T
}

View File

@ -0,0 +1,13 @@
import { requireModule } from "../DiscordToModules"
import { Snowflake } from "../.."
let acknowledgeModuleInternal1:{
bulkAck(channels:{
channelId: Snowflake,
messageId: Snowflake
}[]):void
} = requireModule(e => e && e.bulkAck)
export = {
bulkAck: acknowledgeModuleInternal1.bulkAck
}

View File

@ -0,0 +1,35 @@
import { requireModule } from "../DiscordToModules"
import { Snowflake } from "../.."
let cdnModuleInternal1:{
getUserAvatarURL(user:{
id: Snowflake,
avatar: string,
discriminator: string,
bot: boolean
}, format?:string, size?: number):string,
getGuildSplashURL(infos:{
id: Snowflake,
splash: string,
size?: number
}): string,
getGuildBannerURL(infos:{
id: Snowflake,
banner: string
}): string
} = requireModule(e => e.default && e.default.getGuildSplashURL)
let cdnModuleInternal2:{
DEFAULT_AVATARS: string[]
} = requireModule(e => e.default && e.default.DEFAULT_AVATARS)
export = {
getUserAvatarURL: cdnModuleInternal1.getUserAvatarURL,
getGuildSplashURL: cdnModuleInternal1.getGuildSplashURL,
getGuildBannerURL: cdnModuleInternal1.getGuildBannerURL,
getDefaultAvatarURL(discriminator){
let asset = cdnModuleInternal2.DEFAULT_AVATARS[(typeof discriminator === "string" ? parseInt(discriminator) || 0 : isNaN(discriminator) ? 0 : discriminator) % cdnModuleInternal2.DEFAULT_AVATARS.length]
return `${location.protocol}://${window["GLOBAL_ENV"].ASSET_ENDPOINT}/assets/${asset}`
}
}

View File

@ -0,0 +1,58 @@
import {requireModule, DiscordChannel} from "../DiscordToModules"
import { Snowflake } from "../.."
let channelsModuleInternal1:{
getChannel(id:Snowflake): DiscordChannel,
getChannels(): {
[k:string]: DiscordChannel
},
getDMFromUserId(id:string):Snowflake,
getDMUserIds():Snowflake[],
getFollowerStatsForChannel(id: Snowflake):{
loadingStatus: "succeeded"|"failed",
lastFetched: number,
channelsFollowing: number,
guildMembers: number,
guildsFollowing: number,
usersSeenEver: number,
subscribersGainedSinceLastPost: number,
subscribersLostSinceLastPost: number
},
getGDMsForRecipients(recipients: Snowflake[]):Set<Snowflake>
} = requireModule(e => e.default && e.default.getChannels && e.default.getChannel)
let channelsModuleInternal2:{
deleteChannel(id: Snowflake):void
}
let channelsModuleInternal3:{
hasUnread(channel_id:Snowflake):boolean,
hasCategoryUnread(channel_id:Snowflake):boolean,
getUnreadCount(channel_id:Snowflake):number,
getMentionCount(channel_id:Snowflake):number,
ackMessageId(channel_id:Snowflake):Snowflake,
lastMessageId(channel_id:Snowflake):Snowflake,
getOldestUnreadMessageId(channel_id:Snowflake):Snowflake,
getOldestUnreadTimestamp(channel_id:Snowflake):number,
isEstimated(channel_id:Snowflake):boolean,
hasUnreadPins(channel_id:Snowflake):boolean,
getAllReadStates()
} = requireModule(e => e.default && e.default.lastMessageId)
function set3(){
if(channelsModuleInternal3)return
channelsModuleInternal3 = requireModule(e => e.default && e.default.lastMessageId)
}
export = {
getChannel: channelsModuleInternal1.getChannel,
getAllChannels: channelsModuleInternal1.getChannels,
get delete():typeof channelsModuleInternal2.deleteChannel{
return channelsModuleInternal2 ? channelsModuleInternal2.deleteChannel : (channelsModuleInternal2 = requireModule(e => e.default && e.default.deleteChannel), channelsModuleInternal2.deleteChannel)
},
get lastMessageId(){
set3()
return channelsModuleInternal3.lastMessageId
}
}

View File

@ -0,0 +1,3 @@
import { requireModule } from "../DiscordToModules";
export = requireModule(e => e.API_HOST)

View File

@ -0,0 +1,48 @@
import { requireModule } from "../DiscordToModules";
export = requireModule(m=>m.Dispatcher&&m.default&&m.default.dispatch) as {
_subscriptions: any,
_waitQueue: (() => void)[],
_processingWaitQueue: boolean,
_currentDispatchActionType: string | null,
_orderedActionHandlers: any,
_orderedCallbackTokens: any,
_lastID: number,
_dependencyGraph: any
isDispatching():boolean,
/**
* If the dispatcher isn't dispatching, then dispatch
*/
maybeDispatch(event: {type: string, [k: string]:any}):void,
/**
* Wait until the dispatcher finished dispatching, then dispatch
*/
dirtyDispatch(event: {type: string, [k: string]:any}):void,
/**
* dispatch
*/
dispatch(event: {type: string, [k: string]:any}):void,
/** Same as dispatcher.dispatch */
_dispatch(event: {type: string, [k: string]:any}):void,
/**
* Intercept events before they happens, if returning true, then the event will be blocked from dispatching.
*/
setInterceptor(interceptor: (event) => boolean):void,
/**
* Wait until the dispatcher has finished dispatching
*/
wait(waiting: () => void):void,
/** Subscribe to an event */
subscribe(event: string, listener: (event: {type: string, [k: string]:any}) => void):void,
/**
* Unsubscribe from the event.
*/
unsubscribe(event: string, listener: (event: {type: string, [k: string]:any}) => void):void,
register(name: string, actionHandler: any, storeDidChange: any):void
addDependencies,
_invalidateCaches,
_processWaitQueue,
_computeOrderedActionHandlers,
_computeOrderedCallbackTokens
}

View File

@ -0,0 +1,134 @@
import { DiscordGuild, requireModule, DiscordGuildMember } from "../DiscordToModules"
import { Snowflake } from "../.."
let guildModuleInternal1:{
getGuilds():{
[k:string]:DiscordGuild
},
getGuild(id: Snowflake):DiscordGuild
} = requireModule(e => e.default && e.default.getGuilds && e.default.getGuild && !e.default.isFetching)
let guildModuleInternal2:{
getMemberCount(id:Snowflake):number,
getMemberCounts():{
[k:string]:number
}
} = requireModule(e => e.default && e.default.getMemberCounts && e.default.getMemberCount)
let guildModuleInternal3:{
getAllGuildsAndMembers():{
[guild_id:string]: {
[user_id:string]: DiscordGuildMember
}
},
memberOf(user_id:Snowflake):Snowflake[],
getNicknames(user_id:Snowflake):string[],
isMember(guild_id:Snowflake, user_id:Snowflake):boolean,
getMemberIds(guild_id:Snowflake):Snowflake[],
getMembers(guild_id:Snowflake):DiscordGuildMember[],
getKeyedMembers(guild_id:Snowflake): {
[user_id:string]: DiscordGuildMember
},
getMember(guild_id:Snowflake, user_id:Snowflake):DiscordGuildMember,
getNick(guild_id:Snowflake, user_id:Snowflake):string
} = requireModule(e => e.default && e.default.getMembers)
type guildState = {
suppress_everyone: boolean,
suppress_roles: boolean,
mobile_push: boolean,
muted: boolean,
message_notifications: 0|1|2,
channel_overrides: {
[channel_id:string]: {
channel_id: Snowflake,
message_notifications: 0|1|2|3,
mute_config: {
end_time?: string,
selected_time_window: number
},
muted: boolean
}
},
guild_id?: Snowflake,
mute_config: {
end_time?: string,
selected_time_window: number
},
version: 26,
}
let guildModuleInternal4:{
getState():{
collapsedGuilds:any,
userGuildSettings:{
null: guildState,
[guild_id:string]: guildState
}
},
sSuppressEveryoneEnabled(guild_id:Snowflake):boolean,
isSuppressRolesEnabled(guild_id:Snowflake):boolean,
isSuppressRolesEnabled(guild_id:Snowflake):boolean,
isMuted(guild_id:Snowflake):boolean,
getMuteConfig(guild_id:Snowflake):guildState["mute_config"],
getMessageNotifications(guild_id:Snowflake):0|1|2,
getChannelOverrides(guild_id:Snowflake):guildState["channel_overrides"],
getChannelMessageNotifications(guild_id:Snowflake, channel_id:Snowflake):0|1|2|3,
getChannelMuteConfig(guild_id:Snowflake, channel_id:Snowflake):guildState["channel_overrides"]["id"]["mute_config"],
getMutedChannels(guild_id:Snowflake):Set<Snowflake>,
isChannelMuted(guild_id:Snowflake, channel_id:Snowflake):boolean,
_isCategoryMuted(guild_id:Snowflake, channel_id:Snowflake):boolean,
_resolvedMessageNotifications(infos:{
guild_id: Snowflake,
id: Snowflake,
parent_id?: Snowflake
}):0|1|2,
isGuildOrCategoryOrChannelMuted(guild_id: Snowflake, channel_id: Snowflake):boolean,
allowNoMessages(infos:{
guild_id: Snowflake,
id: Snowflake,
parent_id?: Snowflake
}):boolean,
(infos:{
guild_id: Snowflake,
id: Snowflake,
parent_id?: Snowflake
}):boolean,
isGuildCollapsed(guild_id: Snowflake):boolean,
getAllSettings(): {
userGuildSettings: {
null: guildState,
[guild_id:string]: guildState
},
mutedChannels: {
null: Set<Snowflake>,
[guild_id:string]: Set<Snowflake>
},
collapsedGuilds: any
}
} = requireModule(e => e.default && e.default.isGuildOrCategoryOrChannelMuted)
let guildModuleInternal5:{
kickUser(guild_id:Snowflake, user_id:Snowflake, reason:string):Promise<any>
banUser(guild_id:Snowflake, user_id:Snowflake, days:number, reason:string):Promise<any>
} = requireModule(e => e.default && e.default.banUser)
export = {
getAllGuilds: guildModuleInternal1.getGuilds,
getGuild: guildModuleInternal1.getGuild,
getMemberCount: guildModuleInternal2.getMemberCount,
getMemberCounts: guildModuleInternal2.getMemberCounts,
getMembers: guildModuleInternal3.getMembers,
getMember: guildModuleInternal3.getMember,
getMemberIds: guildModuleInternal3.getMemberIds,
isMember: guildModuleInternal3.isMember,
memberOf: guildModuleInternal3.memberOf,
getNick: guildModuleInternal3.getNick,
getMessageNotifications: guildModuleInternal4.getMessageNotifications,
getNotificationsState: guildModuleInternal4.getState,
banUser: guildModuleInternal5.banUser,
kickUser: guildModuleInternal5.kickUser
}

View File

View File

@ -0,0 +1,21 @@
import { requireModule, DiscordUserSettings } from "../DiscordToModules";
import { Snowflake } from "../..";
let userSettingModuleInternal1:{
isGuildRestricted(guild_id:Snowflake):boolean,
getAllSettings():DiscordUserSettings
} = requireModule(e => e.default && e.default.isGuildRestricted)
let userSettingModuleInternal2:{
updateRemoteSettings(settings:Partial<DiscordUserSettings>):any,
updateLocalSettings(settings:Partial<DiscordUserSettings>):void
} = requireModule(e => e.default && e.default.updateLocalSettings)
export = {
getAllSettings: userSettingModuleInternal1.getAllSettings,
getSetting: (setting) => {
return userSettingModuleInternal1.getAllSettings()[setting]
},
...userSettingModuleInternal2
}

View File

@ -0,0 +1,33 @@
import { requireModule, DiscordUser } from "../DiscordToModules";
import { Snowflake } from "../..";
let userModuleInteral1:{
getUser(user_id: Snowflake):DiscordUser,
getUsers():{
[user_id:string]: DiscordUser
},
forEach(callbackfn: (user:DiscordUser) => void):void,
findByTag(username:string, discriminator:string):DiscordUser,
filter(filterdn: (user:DiscordUser) => boolean, sort?:boolean):DiscordUser[],
getCurrentUser():DiscordUser,
getNullableCurrentUser():DiscordUser
} = requireModule(e => e.default && e.default.getUser)
let userModuleInteral2:{
getNote(user_id: Snowflake):{
loading: boolean,
note?: string
}
} = requireModule(e => e.default && e.default.getNote)
export = {
getUser: userModuleInteral1.getUser,
getUsers: userModuleInteral1.getUsers,
forEach: userModuleInteral1.forEach,
findByTag: userModuleInteral1.findByTag,
filter: userModuleInteral1.filter,
getCurrentUser: userModuleInteral1.getCurrentUser,
getNullableCurrentUser: userModuleInteral1.getNullableCurrentUser,
getNote: userModuleInteral2.getNote
}

View File

@ -1,15 +1,42 @@
import { DiscordChannel } from "./DiscordToModules" import { DiscordChannel, DiscordGuild, DiscordGuildMember, DiscordRole, DiscordMessage, DiscordUser } from "./DiscordToModules"
import { Channel } from ".."; import { Channel, Snowflake } from "..";
import BaseChannel from "../structures/BaseChannel";
import Guild from "../structures/Guild";
import { TextChannel } from "../structures/TextChannel";
import GuildMember from "../structures/GuildMember";
import Role from "../structures/Role";
import User from "../structures/User";
import Message from "../structures/Message";
export function createChannel(channel:DiscordChannel):Channel{ export function createChannel(channel:DiscordChannel):Channel{
let constructor = channels[channel.type] let constructor = channels[channel.type] || BaseChannel
return new constructor(channel) return new constructor(channel)
} }
const channels:(new(channel:DiscordChannel) => Channel)[] = [ const channels:(new(channel:DiscordChannel) => Channel)[] = [
TextChann TextChannel
] ]
export function createGuild(guild:DiscordGuild):Guild{
return new Guild(guild)
}
export function createGuildMember(member:DiscordGuildMember):GuildMember{
return new GuildMember(member)
}
export function createRole(role:DiscordRole):Role{
return new Role(role)
}
export function createMessage(message:DiscordMessage):Message{
return new Message(message)
}
export function createUser(user:DiscordUser):User{
return new User(user)
}
export function applyMixins(derivedCtor: any, baseCtors: any[]) { export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => { baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
@ -69,3 +96,16 @@ export function binaryToID(num:string):string {
return dec; return dec;
} }
export type UserResolvable = User | Snowflake | Message | Guild | GuildMember
export function resolveUserID(user:UserResolvable){
if(typeof user === "string")return user // ID
if(user instanceof User)return user.id // User
if(user instanceof Message)return user.author.id // Message Author
if(user instanceof Guild)return user.ownerID // Guild
if(user instanceof GuildMember)return user.id // GuildMember
return null
}

View File

@ -0,0 +1,56 @@
const path = require("path");
//const CircularDependencyPlugin = require("circular-dependency-plugin");
module.exports = {
mode: "development",
target: "node",
devtool: "inline-source-map",
entry: "./src/index.ts",
output: {
filename: "main.js",
path: path.resolve(__dirname, "js"),
library: "LightcordApi",
libraryTarget: "commonjs2"
},
externals: {
electron: `electron`,
fs: `fs`,
path: `path`,
events: `events`,
rimraf: `rimraf`,
yauzl: `yauzl`,
mkdirp: `mkdirp`,
request: `request`,
"node-fetch": "node-fetch",
"uuid/v1": "uuid/v1",
"uuid/v4": "uuid/v4"
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
alias: {
"react$": path.resolve(__dirname, "src", "alias", "react.ts"),
"react-dom$": path.resolve(__dirname, "src", "alias", "react-dom.ts")
}
},
module: {
rules: [{
test: /\.jsx?$/,
loader: "babel-loader",
exclude: /node_modules/,
query: {
presets: [
["@babel/env", {
targets: {
node: "12.8.1",
chrome: "78"
}
}], "@babel/react"
]
}
}, {
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
}]
}
};

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,9 @@ import TextInput from "./inputs/TextInput"
import Dropdown from "./inputs/Dropdown" import Dropdown from "./inputs/Dropdown"
import Title from "./general/Title" import Title from "./general/Title"
import SettingsTitle from "./general/SettingsTitle" import SettingsTitle from "./general/SettingsTitle"
import Tabs, { Tab } from "./general/Tabs"
import SettingSubTitle from "./general/SettingSubTitle"
import CodeBlock from "./general/CodeBlock"
export default { export default {
inputs: { inputs: {
@ -19,6 +22,9 @@ export default {
}, },
general: { general: {
Title: Title, Title: Title,
SettingsTitle: SettingsTitle SettingsTitle: SettingsTitle,
SettingSubTitle: SettingSubTitle,
Tabs: Tabs,
CodeBlock: CodeBlock
} }
} }

View File

@ -0,0 +1,67 @@
import WebpackLoader from "../../modules/WebpackLoader"
type CodeBlockProps = {
language?: string,
content: string
}
let CodeBlockModules
export default class CodeBlock extends React.Component<CodeBlockProps> {
static defaultProps:CodeBlockProps = {
language: "plaintext",
content: ""
}
get modules(){
return CodeBlockModules || (CodeBlockModules = [
WebpackLoader.find(e => e.markup),
WebpackLoader.find(e => e.messageContent),
WebpackLoader.find(e => e.scrollbarGhostHairline),
WebpackLoader.find(e => e.highlight),
WebpackLoader.find(e => e.marginBottom8)
])
}
render(){
let [
messageModule1,
messageModule2,
scrollbarModule1,
hightlightJS,
marginModule1
] = this.modules
const code = this.props.language === "plaintext" ? <code className={`${scrollbarModule1.scrollbarGhostHairline} hljs`}>
{this.props.content}
</code> : <code className={`${scrollbarModule1.scrollbarGhostHairline} hljs`}
dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}} />
return (<div className={`${messageModule1.markup} ${messageModule2.messageContent}`}>
<pre>
{code}
</pre>
<div className={marginModule1.marginBottom8}></div>
</div>)
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
content: "console.log(\"Exemple code\")"
}
])
AllPreviews.push([
{
language: "js"
},
{
language: "plaintext"
},
...CodeBlock.prototype.modules[3].listLanguages().filter(e => e !== "js").map(e => ({language: e}))
])
return AllPreviews
})()
}
}
let AllPreviews

View File

@ -0,0 +1,67 @@
import WebpackLoader from "../../modules/WebpackLoader"
type CodeBlockProps = {
language?: string,
content: string
}
let CodeBlockModules
export default class CodeBlock extends React.Component<CodeBlockProps> {
static defaultProps:CodeBlockProps = {
language: "plaintext",
content: ""
}
get modules(){
return CodeBlockModules || (CodeBlockModules = [
WebpackLoader.find(e => e.markup),
WebpackLoader.find(e => e.messageContent),
WebpackLoader.find(e => e.scrollbarGhostHairline),
WebpackLoader.find(e => e.highlight),
WebpackLoader.find(e => e.marginBottom8)
])
}
render(){
let [
messageModule1,
messageModule2,
scrollbarModule1,
hightlightJS,
marginModule1
] = this.modules
const code = this.props.language === "plaintext" ? <code className={`${scrollbarModule1.scrollbarGhostHairline} hljs`}>
{this.props.content}
</code> : <code className={`${scrollbarModule1.scrollbarGhostHairline} hljs`}
dangerouslySetInnerHTML={{__html: hightlightJS.highlight(this.props.language, this.props.content).value}} />
return (<div className={`${messageModule1.markup} ${messageModule2.messageContent}`}>
<pre>
{code}
</pre>
<div className={marginModule1.marginBottom8}></div>
</div>)
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
content: "console.log(\"Exemple code\")"
}
])
AllPreviews.push([
{
language: "js"
},
{
language: "plaintext"
},
...CodeBlock.prototype.modules[3].listLanguages().filter(e => e !== "js").map(e => ({language: e}))
])
return AllPreviews
})()
}
}
let AllPreviews

View File

@ -0,0 +1,54 @@
import WebpackLoader from "../../modules/WebpackLoader"
import { ReactNode } from "react"
import Utils from "../../modules/Utils"
type SettingSubTitleProps = {
children: ReactNode
className?: string
}
let TitleModules
export default class SettingSubTitle extends React.Component<SettingSubTitleProps> {
static defaultProps:SettingSubTitleProps = {
children: [],
className: ""
}
get modules(){
return TitleModules || (TitleModules = [
WebpackLoader.find(e => typeof e.marginTop60 === "string"),
WebpackLoader.findByUniqueProperties(["h5"]),
window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["size14"]),
window.Lightcord.Api.WebpackLoader.findByUniqueProperties(["colorStandard"])
])
}
render(){
let [
marginModule,
titleModule,
sizeModule,
colorModule
] = this.modules
let props = this.props
let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h5} ${marginModule.marginBottom4}`
if(props.className)className =+ " "+props.className
return React.createElement("h5", {className}, props.children)
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
children: "Exemple title"
}
])
return AllPreviews
})()
}
}
let AllPreviews

View File

@ -1,7 +1,6 @@
import WebpackLoader from "../../modules/WebpackLoader" import WebpackLoader from "../../modules/WebpackLoader"
import Title from "./Title" import Title from "./Title"
import { ReactNode } from "react" import { ReactNode } from "react"
import Utils from "../../modules/Utils"
type SettingsTitleProps = { type SettingsTitleProps = {
children: ReactNode children: ReactNode
@ -9,25 +8,9 @@ type SettingsTitleProps = {
} }
let TitleModules let TitleModules
export default class SettingsTitle extends React.Component<SettingsTitleProps, SettingsTitleProps> { export default class SettingsTitle extends React.Component<SettingsTitleProps> {
constructor(props: SettingsTitleProps){ constructor(props: SettingsTitleProps){
super(props) super(props)
props = SettingsTitle.normalizeProps(props)
this.state = Object.create(props)
}
static normalizeProps(props: SettingsTitleProps):SettingsTitleProps{
props = Object.create(props)
if(!props || typeof props !== "object")props = {children: []}
if(typeof props.className !== "string")delete props.className
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
}
let finals = Object.assign({}, ...levels)
return finals
} }
get modules(){ get modules(){
@ -41,14 +24,29 @@ export default class SettingsTitle extends React.Component<SettingsTitleProps, S
marginModule marginModule
] = this.modules ] = this.modules
let props = SettingsTitle.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){
this.state = Object.create(props)
}
let className = `${marginModule.marginTop60} ${marginModule.marginBottom20}` let className = `${marginModule.marginTop60} ${marginModule.marginBottom20}`
if(props.className)className =+ " "+props.className if(props.className)className =+ " "+props.className
return React.createElement(Title, {className}, props.children) return React.createElement(Title, {className}, props.children)
} }
static defaultProps:SettingsTitleProps = {
children: [""],
className: ""
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
children: "Exemple title"
}
])
return AllPreviews
})()
}
} }
let AllPreviews

View File

@ -0,0 +1,139 @@
import { ReactNode } from "react"
import NOOP from "../../modules/noop"
import Title from "./Title"
export default class Tabs extends React.Component<{
children?: ReactNode,
tabs: {label: string, id: string}[],
active?: string
onChange?: (tab: string) => void
}, {
active: string
}> {
static defaultProps = {
children: null,
tabs: {label: "No tabs was passed to <Tabs>.", id: "none"},
active: null,
onChange: NOOP
}
constructor(props){
super(props)
this.state = {
active: this.props.active || null
}
}
tabsElements:Tab[] = []
get tabs():Tabs["props"]["tabs"]{
return this.props.tabs || []
}
changeTab(tab:string){
if(tab === this.state.active)return
if(this.props.onChange)this.props.onChange(tab)
this.setState({
active: tab
})
this.tabsElements.forEach(e => e.setActive(tab === e.props.id))
}
render(){
return (<div className="lc-tabWrapper">
<div className="lc-tabnav" style={{flex: "0 1 auto"}}>
{this.tabs.map(tab => {
return React.createElement(Tab, {TabContainer: this, title: tab.label, id: tab.id, key: btoa(tab.label+":"+tab.id)})
})}
</div>
<div className="lc-tab">
{this.props.children}
</div>
</div>)
}
isActive(tab){
return this.state.active === tab
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
children: (<div style={{marginTop: "20px", marginBottom: "20px"}}>
<div style={{
backgroundColor: "var(--background-primary)",
padding: "30px 30px",
borderRadius: "8px"
}} className="lc-tab-box-shadow">
<Title>Preview tabs</Title>
</div>
</div>)
}
], [
{
tabs: [
{
label: "Active tab",
id: "1"
},
{
label: "Unactive tab",
id: "2"
}
]
}
], [
{
active: "1"
}
], [
{
onChange: (tabId) => {}
}
])
return AllPreviews
})()
}
}
let AllPreviews
export class Tab extends React.Component<{
TabContainer: Tabs,
title: string,
id: string
}, {
active: boolean
}> {
constructor(props){
super(props)
this.state = {
active: props.TabContainer.isActive(props.id)
}
this.props.TabContainer.tabsElements.push(this)
}
setActive(isActive:boolean){
this.setState({
active: !!isActive
})
}
render(){
let className = `lc-navItem`
if(this.state.active){
className += ` lc-navItemActive`
}else{
className += ` lc-navItemInactive`
}
return (<div className={className} onClick={()=>{
this.props.TabContainer.changeTab(this.props.id)
}}>
{this.props.title}
</div>)
}
}

View File

@ -9,32 +9,9 @@ type TitleProps = {
} }
let TitleModules let TitleModules
export default class Title extends React.Component<TitleProps, TitleProps> { export default class Title extends React.Component<TitleProps> {
constructor(props: TitleProps){ constructor(props: TitleProps){
super(props) super(props)
props = Title.normalizeProps(props)
this.state = Object.create(props)
}
_key: string
get key(){
return this._key || uuid()
}
static normalizeProps(props: TitleProps):TitleProps{
props = Object.create(props)
if(!props || typeof props !== "object")props = {children: []}
if(!props.children)props.children = []
if(typeof props.className !== "string")props.className = ""
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
}
let finals = Object.assign({}, ...levels)
return finals
} }
get modules(){ get modules(){
@ -52,14 +29,29 @@ export default class Title extends React.Component<TitleProps, TitleProps> {
titleModule titleModule
] = this.modules ] = this.modules
let props = Title.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){
this.state = props
}
let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h2} ${titleModule.defaultColor} ${titleModule.defaultMarginh2}` let className = `${colorModule.colorStandard} ${sizeModule.size14} ${titleModule.h2} ${titleModule.defaultColor} ${titleModule.defaultMarginh2}`
if(props.className)className += " "+props.className if(props.className)className += " "+props.className
return React.createElement("h2", {className, key: this.key}, props.children) return React.createElement("h2", {className}, props.children)
}
static defaultProps:TitleProps = {
children: null,
className: null
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([
{
children: "Exemple title"
}
])
return AllPreviews
})()
} }
} }
let AllPreviews

View File

@ -1,29 +1,31 @@
import WebpackLoader from "../../modules/WebpackLoader" import WebpackLoader from "../../modules/WebpackLoader"
import { MouseEventHandler } from "react" import { MouseEventHandler, ReactNode } from "react"
let ButtonModules let ButtonModules
export default class Button extends React.Component<{
text: string, type ButtonProps = {
onClick: MouseEventHandler, children?: ReactNode,
color?: ButtonColor onClick?: MouseEventHandler,
}> { color?: ButtonColor,
constructor(props:{ wrapper?: boolean,
text: string, look?: ButtonLook,
onClick: MouseEventHandler, size?: ButtonSize,
color?: ButtonColor hoverColor?: ButtonHovers
}){ }
if(props.color){ export default class Button extends React.Component<ButtonProps, {hover: boolean}> {
props.color = props.color.toLowerCase() as ButtonColor constructor(props:ButtonProps){
if(!["brand", "grey", "red", "green", "yellow", "primary", "link", "white", "black", "transparent"].includes(props.color)){
props.color = "brand"
}
}else{
props.color = "brand"
}
props.onClick = typeof props.onClick === "function" ? props.onClick : () => {}
super(props) super(props)
this.state = {
hover: false
}
} }
static Colors:ButtonColor[] = ["brand", "grey", "red", "green", "yellow", "primary", "link", "white", "black", "transparent"]
static Looks:ButtonLook[] = ["filled", "inverted", "outlined", "ghost", "link", "blank"]
static Sizes:ButtonSize[] = ["small", "medium", "large", "xlarge", "min", "max", "icon", "none"]
static HoverColors: ButtonHovers[] = ["default", ...Button.Colors]
get modules(){ // caching modules get modules(){ // caching modules
return ButtonModules || (ButtonModules = [ return ButtonModules || (ButtonModules = [
WebpackLoader.findByUniqueProperties(["_horizontal"]), WebpackLoader.findByUniqueProperties(["_horizontal"]),
@ -41,20 +43,140 @@ export default class Button extends React.Component<{
colorsModule, colorsModule,
] = this.modules ] = this.modules
return (<div className={buttonModule.buttonWrapper}> let props:ButtonProps = {}
<button type="button" className={`${flexModule.flexChild} ${euhModule1.button} ${euhModule1.lookFilled} ${colorsModule.ButtonColors[this.props.color.toUpperCase()]} ${euhModule1.sizeSmall} ${euhModule1.grow}`} style={{flex: "0 1 auto"}} onClick={this.props.onClick}> if(this.props){
<div className={euhModule1.contents}>{this.props.text}</div> if("color" in this.props){
</button> props.color = this.props.color
</div>) }
if("children" in this.props){
props.children = this.props.children
}
if("onClick" in this.props){
props.onClick = this.props.onClick
}
if("wrapper" in this.props){
props.wrapper = !!this.props.wrapper
}
if("look" in this.props){
props.look = this.props.look
}
if("size" in this.props){
props.size = this.props.size
}
if("hoverColor" in this.props){
props.hoverColor = this.props.hoverColor
}
}
if(props.color){
props.color = props.color.toLowerCase() as ButtonColor
if(!Button.Colors.includes(props.color)){
props.color = Button.Colors[0]
}
}else{
props.color = Button.Colors[0]
}
if(props.look){
props.look = props.look.toLowerCase() as ButtonLook
if(!Button.Looks.includes(props.look)){
props.look = Button.Looks[0]
}
}else{
props.look = Button.Looks[0]
}
if(props.size){
props.size = props.size.toLowerCase() as ButtonSize
if(!Button.Sizes.includes(props.size)){
props.size = Button.Sizes[0]
}
}else{
props.size = Button.Sizes[0]
}
if(props.hoverColor){
props.hoverColor = props.hoverColor.toLowerCase() as ButtonHovers
if(!Button.HoverColors.includes(props.hoverColor)){
props.hoverColor = Button.HoverColors[0]
}
}else{
props.hoverColor = Button.HoverColors[0]
}
let buttonSize = props.size ? colorsModule.ButtonSizes[props.size.toUpperCase()] || "" : ""
if(buttonSize)buttonSize = " " + buttonSize
let hoverColor = props.hoverColor ? colorsModule.ButtonHovers[props.hoverColor.toUpperCase()] || "" : ""
if(hoverColor)hoverColor = " " + hoverColor
props.onClick = typeof props.onClick === "function" ? props.onClick : () => {}
if(typeof props.wrapper !== "boolean")props.wrapper = true
let hover = this.state.hover ? euhModule1.hasHover : ""
if(hover)hover = " " + hover
let button = <button type="button"
className={`${flexModule.flexChild} ${euhModule1.button} ${colorsModule.ButtonLooks[props.look.toUpperCase()]} ${colorsModule.ButtonColors[props.color.toUpperCase()]}${buttonSize}${hoverColor}${hover} ${euhModule1.grow}`}
style={{flex: "0 1 auto"}} onClick={this.props.onClick} onMouseEnter={(ev) => {
if(!hoverColor)return
this.setState({hover: true})
}} onMouseLeave={(ev) => {
if(!hoverColor)return
this.setState({hover: false})
}}>
<div className={euhModule1.contents}>{props.children}</div>
</button>
if(props.wrapper){
return <div className={buttonModule.buttonWrapper}>
{button}
</div>
}
return button
} }
static get AllPreviews(){ static get AllPreviews(){
return AllPreviews || (AllPreviews = [ return AllPreviews || (() => {
AllPreviews = []
]) let colors = []
for(let color of Button.Colors){
colors.push({
color: color
})
}
AllPreviews.push(colors)
let looks = []
for(let look of Button.Looks){
looks.push({
look: look
})
}
AllPreviews.push(looks)
let sizes = []
for(let size of Button.Sizes){
sizes.push({
size: size
})
}
AllPreviews.push(sizes)
let hovers = []
for(let hover of Button.HoverColors){
hovers.push({
hoverColor: hover
})
}
AllPreviews.push(hovers)
AllPreviews.push([{children: "Test Button"}])
AllPreviews.push([{onClick: () => {}}])
AllPreviews.push([{wrapper: false}])
return AllPreviews
})()
} }
} }
let AllPreviews
export type ButtonColor = "brand" | "grey" | "red" | "green" | "yellow" | "primary" | "link" | "white" | "black" | "transparent" export type ButtonColor = "brand" | "grey" | "red" | "green" | "yellow" | "primary" | "link" | "white" | "black" | "transparent"
export type ButtonLook = "filled" | "inverted" | "outlined" | "ghost" | "link" | "blank"
export type ButtonSize = "none" | "small" | "medium" | "large" | "xlarge" | "min" | "max" | "icon"
export type ButtonHovers = "default" | ButtonColor

View File

@ -2,6 +2,7 @@ import NOOP from "../../modules/noop"
import WebpackLoader from "../../modules/WebpackLoader" import WebpackLoader from "../../modules/WebpackLoader"
import { ReactNode, CSSProperties } from "react" import { ReactNode, CSSProperties } from "react"
import Utils from "../../modules/Utils" import Utils from "../../modules/Utils"
import unfreeze from "../../modules/Unfreeze"
type DropdownProps = { type DropdownProps = {
className?: string, className?: string,
@ -42,47 +43,38 @@ type themeOverride = {
} }
let DropdownModules let DropdownModules
export default class Dropdown extends React.Component<DropdownProps, DropdownProps> { export default class Dropdown extends React.Component<DropdownProps, {value: string|null}> {
constructor(props:DropdownProps){ constructor(props:DropdownProps){
super(props) super(props)
props = Dropdown.normalizeProps(props)
this.state = props
this.onChange = this.onChange.bind(this) this.onChange = this.onChange.bind(this)
this.state = {
value: props.value || null
}
} }
static normalizeProps(props:DropdownProps):DropdownProps{ static defaultProps:DropdownProps = {
props = Object.create(props) className: null,
if(!props || typeof props !== "object")props = {} error: null,
if(typeof props.className !== "string")delete props.className options: [{
if(typeof props.darkThemeColorOverrides !== "object" || !props.darkThemeColorOverrides)delete props.darkThemeColorOverrides
if(typeof props.disabled !== "boolean")props.disabled = false
if(typeof props.error !== "string")delete props.error
if(typeof props.isMulti !== "boolean")props.isMulti = false
if(typeof props.lightThemeColorOverrides !== "object" || !props.lightThemeColorOverrides)delete props.lightThemeColorOverrides
if(typeof props.multiValueRenderer !== "function")delete props.multiValueRenderer
if(typeof props.valueRenderer !== "function")delete props.valueRenderer
if(typeof props.optionRenderer !== "function")delete props.optionRenderer
if(typeof props.onChange !== "function")props.onChange = NOOP
if(!Array.isArray(props.options))props.options = [{
value: "none", value: "none",
"label": "No options - No options was passed to Dropdown. If you meant to put an empty dropdown, input an empty array." "label": "No options - No options was passed to Dropdown. If you meant to put an empty dropdown, input an empty array."
}] }],
if(typeof props.searchable !== "boolean")props.searchable = false valueRenderer: null,
if(typeof props.styleOverrides !== "object")delete props.styleOverrides multiValueRenderer: null,
if(typeof props.value !== "string")props.value = null optionRenderer: null,
onChange: NOOP,
let levels = [props] value: null,
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){ disabled: false,
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))) searchable: false,
} clearable: false,
let finals = Object.assign({}, ...levels) styleOverrides: null,
lightThemeColorOverrides: null,
return finals darkThemeColorOverrides: null,
isMulti: false
} }
onChange(value){ onChange(value){
console.log(value) this.props.onChange(value)
this.state.onChange(value)
this.setState({ this.setState({
value: value value: value
}) })
@ -99,14 +91,58 @@ export default class Dropdown extends React.Component<DropdownProps, DropdownPro
DropdownComponent DropdownComponent
] = this.modules ] = this.modules
let props = Dropdown.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){ let returnValue = <DropdownComponent {...props} onChange={this.onChange} value={this.state.value}/>
this.state = Object.create(props) return returnValue
}
return <DropdownComponent {...this.props} onChange={this.onChange}/>
} }
get value(){ get value(){
return this.state.value return this.state.value
} }
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([{
error: null
}, {
error: "An error occured"
}], [{
options: [
{
value: "option1",
label: "Option 1"
},
{
value: "option2",
label: "Option 2"
},
{
value: "option3",
label: "Option 3"
}
]
}], [{
value: "option1"
}], [{
disabled: false
}, {
disabled: true
}], [{
searchable: true
}, {
searchable: false
}], [{
clearable: true
}, {
clearable: false
}], [{
isMulti: false
}, {
isMulti: true
}])
return AllPreviews
})()
}
} }
let AllPreviews

View File

@ -19,51 +19,31 @@ type RadioGroupProps = {
} }
let RadioGroupModule let RadioGroupModule
export default class RadioGroup extends React.Component<RadioGroupProps, RadioGroupProps> { export default class RadioGroup extends React.Component<RadioGroupProps, {value?: string}> {
static defaultProps:RadioGroupProps = {
options: [{
value: "none",
name: "No options",
desc: "No options was passed to Choices. If you meant to display no options at all, please pass an empty array.",
color: "#f04747"
}],
value: null,
disabled: false,
size: "medium",
itemType: "bar",
infoClassName: null,
onChange: NOOP
}
constructor(props:RadioGroupProps){ constructor(props:RadioGroupProps){
super(props) super(props)
props = RadioGroup.normalizeProps(props)
this.state = props
this.onChange = this.onChange.bind(this) this.onChange = this.onChange.bind(this)
} this.state = {
value: props.value
static normalizeProps(props:RadioGroupProps):RadioGroupProps{
props = Object.create(props)
if(!props || typeof props !== "object")props = {}
let defaultOptions = false
if(!props.options || !Array.isArray(props.options)){
props.options = [{
value: "none",
name: "No options",
desc: "No options was passed to Choices. If you meant to display no options at all, please pass an empty array.",
color: "#f04747"
}]
defaultOptions = true
} }
if(!props.value || typeof props.value !== "string"){
if(defaultOptions){
props.value = "none"
}else{
props.value = null
}
}
if(typeof props.disabled !== "boolean")props.disabled = false
if(typeof props.size !== "string" || !["small", "medium"].includes(props.size.toLowerCase()))props.size = "medium"
if(typeof props.itemType !== "string" || !["bar", "panel"].includes(props.itemType))props.itemType = "bar"
if(typeof props.infoClassName !== "string")props.infoClassName = ""
if(!props.onChange || typeof props.onChange !== "function")props.onChange = NOOP
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
}
let finals = Object.assign({}, ...levels)
return finals
} }
onChange(ev){ onChange(ev){
this.state.onChange(ev.value) this.props.onChange(ev.value)
this.setState({ this.setState({
value: ev.value value: ev.value
}) })
@ -80,16 +60,54 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
RadioGroupComponent RadioGroupComponent
] = this.modules ] = this.modules
let props = RadioGroup.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){ return <RadioGroupComponent options={props.options} onChange={this.onChange} value={this.state.value} disabled={props.disabled}
this.state = Object.create(props)
}
return <RadioGroupComponent options={props.options} onChange={this.onChange} value={props.value} disabled={props.disabled}
size={RadioGroupComponent.Sizes[props.size.toUpperCase()]} itemType={RadioGroupComponent.ItemTypes[props.itemType.toUpperCase()]} size={RadioGroupComponent.Sizes[props.size.toUpperCase()]} itemType={RadioGroupComponent.ItemTypes[props.itemType.toUpperCase()]}
infoClassName={props.infoClassName}/> infoClassName={props.infoClassName}/>
} }
get value(){ get value():string|null{
return this.state.value return this.state.value
} }
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([{
options: [
{
value: "option1",
name: "Option 1",
desc: "description 1"
},
{
value: "option2",
name: "Option 2",
desc: "description 2"
},
{
value: "option3",
name: "Option 3",
desc: "description 3"
}
]
}], [{
value: "option1"
}], [{
disabled: false
}, {
disabled: true
}], [{
size: "medium"
}, {
size: "small"
}], [{
itemType: "bar"
}, {
itemType: "panel"
}])
return AllPreviews
})()
}
} }
let AllPreviews

View File

@ -19,36 +19,13 @@ let SwitchModules
export default class Switch extends React.Component<SwitchProps, {value: boolean}> { export default class Switch extends React.Component<SwitchProps, {value: boolean}> {
constructor(props:SwitchProps){ constructor(props:SwitchProps){
super(props) super(props)
props = Switch.normalizeProps(props)
this.state = Object.create(props) this.state = {
value: props.value || false
}
this.onChange = this.onChange.bind(this) this.onChange = this.onChange.bind(this)
} }
static normalizeProps(props:SwitchProps){
props = Object.create(props)
if(!props)props = {}
if(!props.id || typeof props.id !== "string")props.id = null
if(!props.onChange || typeof props.onChange !== "function")props.onChange = NOOP
if(!props.value || typeof props.value !== "boolean")props.value = false
if(!props.fill || typeof props.fill !== "string")props.fill = null
if(!props.theme || !["default", "clear"].includes(props.theme.toLowerCase()))props.theme = "default"
if(!("disabled" in props) || typeof props.disabled !== "boolean")props.disabled = false
if(!props.className || typeof props.className !== "string")props.className = ""
if(!props.size || !["default", "mini"].includes(props.size.toLowerCase()))props.size = "default"
if(!props.style || typeof props.style !== "object")props.style = {}
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
}
let finals = Object.assign({}, ...levels)
return finals
}
get modules(){ get modules(){
return SwitchModules || (SwitchModules = [ return SwitchModules || (SwitchModules = [
WebpackLoader.find(e => e.default && e.default.displayName === "Switch").default WebpackLoader.find(e => e.default && e.default.displayName === "Switch").default
@ -60,10 +37,7 @@ export default class Switch extends React.Component<SwitchProps, {value: boolean
SwitchComponent SwitchComponent
] = this.modules ] = this.modules
let props = Switch.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){
this.state = Object.create(props)
}
return (<SwitchComponent id={props.id} onChange={this.onChange} value={this.state.value || false} fill={props.fill} return (<SwitchComponent id={props.id} onChange={this.onChange} value={this.state.value || false} fill={props.fill}
theme={SwitchComponent.Themes[props.theme.toUpperCase()]} disabled={props.disabled} className={props.className} theme={SwitchComponent.Themes[props.theme.toUpperCase()]} disabled={props.disabled} className={props.className}
size={SwitchComponent.Sizes[props.size.toUpperCase()]} style={props.style}/>) size={SwitchComponent.Sizes[props.size.toUpperCase()]} style={props.style}/>)
@ -79,4 +53,46 @@ export default class Switch extends React.Component<SwitchProps, {value: boolean
get value(){ get value(){
return this.state.value return this.state.value
} }
static defaultProps = {
id: null,
onChange: NOOP,
value: false,
fill: null,
theme: "default",
disabled: false,
className: null,
size: "default",
style: {}
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([{onChange: (value) => {}}])
AllPreviews.push([{
value: false
}], [{
theme: "default"
}, {
theme: "clear"
}], [{
disabled: false
}, {
disabled: true
}], [{
id: "api-preview-switch"
}], [{
fill: null
}], [{
size: "default"
}, {
size: "mini"
}], [{
style: {}
}])
return AllPreviews
})()
}
} }
let AllPreviews

View File

@ -23,46 +23,18 @@ type TextAreaProps = {
} }
let TextAreaModules let TextAreaModules
export default class TextArea extends React.Component<TextAreaProps, TextAreaProps> { export default class TextArea extends React.Component<TextAreaProps, {value: string}> {
constructor(props){ constructor(props:TextAreaProps){
super(props) super(props)
props = TextArea.normalizeProps(props)
this.state = Object.create(props)
this.onChange = this.onChange.bind(this) this.onChange = this.onChange.bind(this)
this.onFocus = this.onFocus.bind(this) this.onFocus = this.onFocus.bind(this)
this.onBlur = this.onBlur.bind(this) this.onBlur = this.onBlur.bind(this)
this.onKeyDown = this.onKeyDown.bind(this) this.onKeyDown = this.onKeyDown.bind(this)
}
static normalizeProps(props:TextAreaProps):TextAreaProps { this.state = {
props = Object.create(props) value: props.value || ""
if(!props)props = {}
if(!props.name || typeof props.name !== "string")props.name = ""
if(!props.disabled || typeof props.disabled !== "boolean")props.disabled = false
if(typeof props.placeholder !== "string")props.placeholder = ""
if(typeof props.autoFocus !== "boolean")props.autoFocus = false
if(typeof props.resizeable !== "boolean")props.resizeable = false
if(typeof props.flex !== "boolean")props.flex = false
if(typeof props.autosize !== "boolean")props.autosize = false
if(typeof props.rows !== "number")props.rows = 3
if(typeof props.value !== "string")props.value = ""
if(typeof props.error !== "string")props.error = null
if(props.maxLength && typeof props.maxLength !== "number")props.maxLength = 999
if(typeof props.className !== "string")props.className = ""
if(typeof props.id !== "string")props.id = null
if(typeof props.onChange !== "function")props.onChange = NOOP
if(typeof props.onFocus !== "function")props.onFocus = NOOP
if(typeof props.onBlur !== "function")props.onBlur = NOOP
if(typeof props.onKeyDown !== "function")props.onKeyDown = NOOP
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
} }
let finals = Object.assign({}, ...levels)
return finals
} }
get modules(){ get modules(){
@ -72,22 +44,22 @@ export default class TextArea extends React.Component<TextAreaProps, TextAreaPro
} }
onChange(value, name){ onChange(value, name){
this.state.onChange(value, name) this.props.onChange(value, name)
this.setState({ this.setState({
value value
}) })
} }
onFocus(ev, name){ onFocus(ev, name){
this.state.onFocus(ev, name) this.props.onFocus(ev, name)
} }
onBlur(ev, name){ onBlur(ev, name){
this.state.onBlur(ev, name) this.props.onBlur(ev, name)
} }
onKeyDown(ev){ onKeyDown(ev){
this.state.onKeyDown(ev) this.props.onKeyDown(ev)
} }
render(){ render(){
@ -95,15 +67,97 @@ export default class TextArea extends React.Component<TextAreaProps, TextAreaPro
TextAreaComponent TextAreaComponent
] = this.modules ] = this.modules
let props = TextArea.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){
this.state = Object.create(props)
}
return <TextAreaComponent {...props} onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur} onKeyDown={this.onKeyDown}/> return <TextAreaComponent {...props} onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur} onKeyDown={this.onKeyDown} value={this.state.value}/>
} }
get value(){ get value(){
return this.state.value return this.state.value || ""
}
static defaultProps:TextAreaProps = {
name: null,
disabled: false,
placeholder: null,
autoFocus: false,
resizeable: false,
flex: false,
autosize: false,
rows: 3,
value: "",
error: null,
maxLength: null,
className: null,
id: null,
onChange: NOOP,
onFocus: NOOP,
onBlur: NOOP,
onKeyDown: NOOP
}
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([{
name: "api-preview-textarea"
}])
AllPreviews.push([{
disabled: false
}, {
disabled: true
}])
AllPreviews.push([{
placeholder: null
}])
AllPreviews.push([{
autoFocus: false
}, {
autoFocus: true
}])
AllPreviews.push([{
resizeable: false
}, {
resizeable: true
}])
AllPreviews.push([{
flex: false
}, {
flex: true
}])
AllPreviews.push([{
autosize: false
}, {
autosize: true
}])
AllPreviews.push([{
rows: 3
}, {
rows: 2
}, {
rows: 1
}])
AllPreviews.push([{
value: ""
}])
AllPreviews.push([{
error: null
}, {
error: "Example error"
}])
AllPreviews.push([{
maxLength: 100
}])
AllPreviews.push([{
className: ""
}])
AllPreviews.push([{
inputClassName: ""
}])
AllPreviews.push([{
id: "api-preview-textarea"
}])
return AllPreviews
})()
} }
} }
let AllPreviews

View File

@ -19,44 +19,34 @@ type TextInputProps = {
} }
let TextInputModules let TextInputModules
export default class TextInput extends React.PureComponent<TextInputProps, TextInputProps> { export default class TextInput extends React.PureComponent<TextInputProps, {value: string}> {
hasSet: boolean hasSet: boolean
constructor(props: TextInputProps){ constructor(props: TextInputProps){
super(props) super(props)
props = TextInput.normalizeProps(props)
this.state = props
console.log(this.state)
this.onChange = this.onChange.bind(this) this.onChange = this.onChange.bind(this)
this.onFocus = this.onFocus.bind(this) this.onFocus = this.onFocus.bind(this)
this.onBlur = this.onBlur.bind(this) this.onBlur = this.onBlur.bind(this)
this.state = {
value: props.value || ""
}
} }
static normalizeProps(props:TextInputProps):TextInputProps { static defaultProps:TextInputProps = {
props = Object.create(props) name: "",
if(!props)props = {} size: "default",
if(!props.name || typeof props.name !== "string")props.name = "" disabled: false,
if(!props.size || !["default", "mini"].includes(props.size))props.size = "default" placeholder: "",
if(!props.disabled || typeof props.disabled !== "boolean")props.disabled = false value: "",
if(typeof props.placeholder !== "string")props.placeholder = "" error: null,
if(typeof props.value !== "string")props.value = "" maxLength: 999,
if(typeof props.error !== "string")props.error = null className: "",
if(props.maxLength && typeof props.maxLength !== "number")props.maxLength = 999 inputClassName: "",
if(typeof props.className !== "string")props.className = "" id: null,
if(typeof props.inputClassName !== "string")props.inputClassName = "" onChange: NOOP,
if(typeof props.id !== "string")props.id = null onFocus: NOOP,
if(typeof props.onChange !== "function")props.onChange = NOOP onBlur: NOOP
if(typeof props.onFocus !== "function")props.onFocus = NOOP
if(typeof props.onBlur !== "function")props.onBlur = NOOP
let levels = [props]
while(Utils.getNestedProps(props, levels.map(e => "__proto__").join("."))){
levels.push(Utils.getNestedProps(props, levels.map(e => "__proto__").join(".")))
}
let finals = Object.assign({}, ...levels)
return finals
} }
get modules(){ get modules(){
@ -67,7 +57,7 @@ export default class TextInput extends React.PureComponent<TextInputProps, TextI
onChange(value, name){ onChange(value, name){
this.hasSet = false this.hasSet = false
this.state.onChange(value, name, this) this.props.onChange(value, name, this)
if(this.hasSet)return // prevent event if the onChange has changed the value. if(this.hasSet)return // prevent event if the onChange has changed the value.
this.setState({ this.setState({
value value
@ -76,11 +66,11 @@ export default class TextInput extends React.PureComponent<TextInputProps, TextI
} }
onFocus(ev, name){ onFocus(ev, name){
this.state.onFocus(ev, name, this) this.props.onFocus(ev, name, this)
} }
onBlur(ev, name){ onBlur(ev, name){
this.state.onBlur(ev, name, this) this.props.onBlur(ev, name, this)
} }
render(){ render(){
@ -88,16 +78,12 @@ export default class TextInput extends React.PureComponent<TextInputProps, TextI
TextAreaComponent TextAreaComponent
] = this.modules ] = this.modules
let props = TextInput.normalizeProps(this.state || this.props) let props = this.props
if(!this.state){ return <TextAreaComponent {...props} onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur} value={this.state.value} />
this.state = props
}
console.log(props)
return <TextAreaComponent {...props} onChange={this.onChange} onFocus={this.onFocus} onBlur={this.onBlur}/>
} }
get value(){ get value(){
return this.state.value return this.state.value || ""
} }
setValue(value:string){ setValue(value:string){
@ -107,4 +93,41 @@ export default class TextInput extends React.PureComponent<TextInputProps, TextI
this.forceUpdate() this.forceUpdate()
this.hasSet = true this.hasSet = true
} }
static get AllPreviews(){
return AllPreviews || (() => {
AllPreviews = []
AllPreviews.push([{
name: "api-preview-textinput"
}], [{
size: "default"
}, {
size: "mini"
}], [{
disabled: false
}, {
disabled: true
}], [{
placeholder: ""
}], [{
value: ""
}], [{
error: null
}, {
error: "Example error"
}], [{
maxLength: 999
}], [{
className: ""
}], [{
inputClassName: ""
}], [{
id: "api-preview-textinput"
}], [{
onChange: (value: string, name: string) => {}
}])
return AllPreviews
})()
}
} }
let AllPreviews

View File

@ -16,7 +16,7 @@ export default new class PluginUtilities {
renderSettingsToReact(settings:SettingItem[]){ renderSettingsToReact(settings:SettingItem[]){
let items = [] let items = []
settings.forEach(item => { settings.forEach(item => {
console.log(item)
if(typeof item !== "object")return items.push(item) if(typeof item !== "object")return items.push(item)
if(item.props && "children" in item.props){ if(item.props && "children" in item.props){
if(!Array.isArray(item.props.children))item.props.children = [item.props.children] if(!Array.isArray(item.props.children))item.props.children = [item.props.children]

View File

@ -0,0 +1,24 @@
export default function unfreeze(o) {
var oo = undefined;
if (o instanceof Array) {
oo = [];
var clone = function(v) {
oo.push(v)
};
o.forEach(clone);
} else if (o instanceof String) {
oo = new String(o).toString();
} else if (typeof o == 'object') {
oo = {};
for (var property in o) {
oo[property] = o[property];
}
}
return oo;
}
declare global {
interface ObjectConstructor {
unfreeze: <T extends any>(obj: Readonly<T>) => T
}
}
Object.unfreeze = unfreeze

View File

@ -33,7 +33,7 @@ async function main(){
for(let file of fs.readdirSync(folder, {withFileTypes: true})){ for(let file of fs.readdirSync(folder, {withFileTypes: true})){
if(file.isFile()){ if(file.isFile()){
let filepath = path.join(folder, file.name) let filepath = path.join(folder, file.name)
if(predicate(filepath)){ if(predicate(filepath), false){
await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..") await compile(filepath, path.join(filepath.replace(folders.startDir, folders.newDir)), "..")
}else{ }else{
await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir)) await fs.promises.copyFile(filepath, filepath.replace(folders.startDir, folders.newDir))

View File

@ -54,6 +54,8 @@ async function privateInit(){
window.React = React window.React = React
require.cache["react"] = React require.cache["react"] = React
} }
try{ try{
window.ReactDOM = require("react-dom") window.ReactDOM = require("react-dom")
}catch(e){ }catch(e){
@ -62,6 +64,105 @@ async function privateInit(){
require.cache["react-dom"] = ReactDOM require.cache["react-dom"] = ReactDOM
} }
//stop here if betterdiscord is disabled.
if(electron.remote.process.argv.includes("--disable-betterdiscord")){
let formComponents
let margins
let ButtonModules
class LightcordSettings extends React.Component {
render(){
if(!formComponents)formComponents = ModuleLoader.get(e => e.FormSection)[0]
if(!margins)margins = ModuleLoader.get(e => e.marginTop60)[0]
let [
flexModule,
euhModule1,
buttonModule,
colorsModule,
] = this.ButtonModules
return React.createElement("div", {}, [
React.createElement(formComponents.FormSection, {
className: "",
tag: "h2",
title: "Lightcord's Settings"
}, [
React.createElement("div", { className: buttonModule.buttonWrapper }, [
React.createElement("button", {
type: "button",
className: `${flexModule.flexChild} ${euhModule1.button} ${euhModule1.lookFilled} ${colorsModule.ButtonColors.YELLOW} ${euhModule1.sizeSmall} ${euhModule1.grow}`,
style: { flex: "0 1 auto" },
onClick: () => {
console.log("Should relaunch")
electron.remote.app.relaunch({
args: electron.remote.process.argv.slice(1).filter(e => e !== "--disable-betterdiscord")
})
electron.remote.app.quit()
}
},
React.createElement("div", { className: euhModule1.contents }, "Relaunch with BetterDiscord"))
])
])
])
}
get ButtonModules(){ // caching modules
return ButtonModules || (ButtonModules = [
ModuleLoader.get(e => e["_horizontal"])[0],
ModuleLoader.get(e => e["colorTransparent"])[0],
ModuleLoader.get(e => e["buttonWrapper"])[0],
ModuleLoader.get(e => e["ButtonColors"])[0]
])
}
}
// fix notifications here
let dispatcher = ModuleLoader.get(m=>m.Dispatcher&&m.default&&m.default.dispatch)[0].default
dispatcher.subscribe("USER_SETTINGS_UPDATE", (data) => {
DiscordNative.ipc.send("UPDATE_THEME", data.settings.theme)
})
let constants = ModuleLoader.get(m=>m.API_HOST)[0]
// add menu to re enable BetterDiscord
constants.UserSettingsSections = Object.freeze(Object.assign({}, constants.UserSettingsSections, {LIGHTCORD: "Lightcord"}))
ensureExported(e => e.default && e.default.prototype && e.default.prototype.getPredicateSections)
.then(settingModule => {
let getPredicateSections = settingModule.default.prototype.getPredicateSections
settingModule.default.prototype.getPredicateSections = function(){
let result = getPredicateSections.call(this, ...arguments)
console.log(result)
if(result[1].section === "My Account"){ // user settings, not guild settings
let poped = []
poped.push(result.pop())
poped.push(result.pop())
poped.push(result.pop())
poped.push(result.pop())
result.push({
section: "HEADER",
label: "Lightcord"
}, {
section: constants.UserSettingsSections.LIGHTCORD,
label: "Lightcord",
element: LightcordSettings
}, {
section: "DIVIDER"
})
while(poped[0]){
result.push(poped.pop())
}
}
return result
}
})
return
}
let createSoundOriginal = ModuleLoader.get((e) => e.createSound)[0].createSound let createSoundOriginal = ModuleLoader.get((e) => e.createSound)[0].createSound
ModuleLoader.get((e) => e.createSound)[0].createSound = function(sound){ ModuleLoader.get((e) => e.createSound)[0].createSound = function(sound){
let isCalling = sound === "call_ringing_beat" || sound === "call_ringing" let isCalling = sound === "call_ringing_beat" || sound === "call_ringing"
@ -153,7 +254,7 @@ async function privateInit(){
*/ */
let DiscordJS let DiscordJS
try{ try{
//DiscordJS = require("../../../../../DiscordJS").default DiscordJS = require("../../../../../DiscordJS").default
}catch(err){ }catch(err){
console.error(err) console.error(err)
DiscordJS = null DiscordJS = null
@ -368,7 +469,13 @@ async function privateInit(){
} }
} }
let returnValue = _handleDispatch.call(this, ...arguments) let returnValue = _handleDispatch.call(this, ...arguments)
if(event === "READY" && DiscordJS)DiscordJS.client.emit("ready") if(event === "READY" && DiscordJS){
try{
DiscordJS.client.emit("self.ready", data)
}catch(e){
console.error("[DiscordJS Error]", e)
}
}
return returnValue return returnValue
} }
function cancelGatewayPrototype(methodName){ function cancelGatewayPrototype(methodName){
@ -963,6 +1070,96 @@ async function privateInit(){
} }
})().catch(() => {}) })().catch(() => {})
let usedWebhooks = {}
ensureExported(e => e && e.Request && e.Request.prototype && e.Request.prototype.end)
.then(RequestModule => {
console.log("RequestModule", RequestModule)
const end = RequestModule.Request.prototype.end
RequestModule.Request.prototype.end = function(){
if(this.url.endsWith("/messages") && /\/channels\/\d+\/messages/g.test(this.url) && this.method === "POST"){ // sending message
let channelId = this.url.split("/channels/")[1].split("/messages")[0]
if(usedWebhooks[channelId]){ // webhook is availlable
let webhook = usedWebhooks[channelId]
let url = `/webhooks/${webhook.id}/${webhook.token}?wait=true`
this.url = url
}
}
return end.call(this, ...arguments)
}
})
ensureExported(e => e.default && e.default.displayName === "Webhook")
.then(webhookComponent => {
const renderEdit = webhookComponent.default.prototype.renderEdit
webhookComponent.default.prototype.renderEdit = function(){
const webhook = this.props.webhook
let returnValue = renderEdit.call(this, ...arguments)
returnValue.props.children = [returnValue.props.children]
let message = usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id ? "Stop talking with this webhook" : "Talk with this webhook"
returnValue.props.children.push(React.createElement(window.Lightcord.Api.Components.inputs.Button, {color: "green", wrapper: false, onClick(){
if(usedWebhooks[webhook.channel_id] && usedWebhooks[webhook.channel_id].id === webhook.id){
delete usedWebhooks[webhook.channel_id]
}else{
usedWebhooks[webhook.channel_id] = {
id: webhook.id,
token: webhook.token
}
}
webhookPanels.forEach(e => e())
}}, message))
return returnValue
}
})
let webhookPanels = []
let getComp = (comp) => {
class SettingsWebhooks extends React.PureComponent {
constructor(props){
super(props)
}
componentWillMount(){
this.id = uuid()
this.component = new comp(this.props)
let func = () => {
this.component.forceUpdate()
}
func.id = this.id
webhookPanels.push(func)
}
componentWillUnmount(){
this.component = null
webhookPanels = webhookPanels.filter(e => e.id !== this.id)
}
render(){
return this.component.render()
}
static displayName = "SettingsWebhooks"
}
return SettingsWebhooks
}
ensureExported(e => e.default && e.default.displayName === "FluxContainer(SettingsWebhooks)")
.then(webhooksComponents => {
let comp = webhooksComponents.default
webhooksComponents.default = getComp(comp)
ModuleLoader.get(e => e.default && e.default.displayName === "FluxContainer(FluxContainer(SettingsWebhooks))")
.forEach(mod => {
mod.default = getComp(mod.default)
})
})
Utils.monkeyPatch(await ensureExported(e => e.default && e.default.displayName == "AuthBox"), "default", {after: (data) => { Utils.monkeyPatch(await ensureExported(e => e.default && e.default.displayName == "AuthBox"), "default", {after: (data) => {
const children = Utils.getNestedProp(data.returnValue, "props.children.props.children.props.children") const children = Utils.getNestedProp(data.returnValue, "props.children.props.children.props.children")
children.push(React.createElement(require("./tokenLogin").default, {})) children.push(React.createElement(require("./tokenLogin").default, {}))
@ -1035,7 +1232,7 @@ function ensureGuildClasses(){
}) })
} }
global.ensureExported = function ensureExported(filter, maxTime = 500){ var ensureExported = global.ensureExported = function ensureExported(filter, maxTime = 500){
let tried = 0 let tried = 0
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let mod = ModuleLoader.get(filter)[0] let mod = ModuleLoader.get(filter)[0]

View File

@ -46,14 +46,12 @@
flex-direction: row; flex-direction: row;
} }
.lc-tab { .lc-tab {
border-radius: 8px; /*border-radius: 8px;*/
overflow: hidden; overflow: hidden;
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
width: 600px; width: 600px;
max-width: 100%; max-width: 100%;
-webkit-box-shadow: 0 0 20px 2px rgba(4,4,5,.3);
box-shadow: 0 0 20px 2px rgba(4,4,5,.3);
-webkit-transform: scale(.85); -webkit-transform: scale(.85);
transform: scale(.85); transform: scale(.85);
-webkit-box-sizing: border-box; -webkit-box-sizing: border-box;
@ -74,6 +72,10 @@
-ms-flex-direction: column; -ms-flex-direction: column;
flex-direction: column; flex-direction: column;
} }
.lc-tab-box-shadow {
-webkit-box-shadow: 0 0 20px 2px rgba(4,4,5,.3);
box-shadow: 0 0 20px 2px rgba(4,4,5,.3);
}
.lc-navItem { .lc-navItem {
padding: 14px 20px; padding: 14px 20px;
position: relative; position: relative;

View File

@ -42,10 +42,37 @@ global.BDModules = Modules
function setReq(){ function setReq(){
try{ try{
req = webpackJsonp.push([[], {__extra_id__: (module, exports, req) => module.exports = req}, [["__extra_id__"]]]); req = webpackJsonp.push([[], {__extra_id__: (mdl, exports, req) => mdl.exports = req}, [["__extra_id__"]]]);
if(req){ if(req){
delete req.m.__extra_id__; delete req.m.__extra_id__;
delete req.c.__extra_id__; delete req.c.__extra_id__;/*
Object.defineProperty(req.c, "0", {
get: () => ({
i: 0,
l: true,
exports: require("react")
}),
configurable: false,
enumerable: true
})
Object.defineProperty(req.c, "856", {
get: () => ({
i: 856,
l: true,
exports: require("react")
}),
configurable: false,
enumerable: true
})
Object.defineProperty(req.c, "71", {
get: () => ({
i: 71,
l: true,
exports: require("react-dom")
}),
configurable: false,
enumerable: true
})*/
} }
}catch(e){ }catch(e){
req = undefined req = undefined

View File

@ -30,7 +30,8 @@ if(process.platform === "win32"){
id: this.id, id: this.id,
title: this.title, title: this.title,
body: this.body, body: this.body,
icon: this.icon icon: this.icon,
theme: settingStore ? settingStore.default.theme : "dark"
}) })
} }
@ -52,7 +53,9 @@ if(process.platform === "win32"){
} }
} }
let settingStore
ensureExported((e => e.default && e.default.theme)) ensureExported((e => e.default && e.default.theme))
.then(themeStore => { .then(themeStore => {
settingStore = themeStore
ipcRenderer.send("UPDATE_THEME", themeStore.default.theme) ipcRenderer.send("UPDATE_THEME", themeStore.default.theme)
}).catch(console.error) }).catch(console.error)

View File

@ -43,8 +43,6 @@ const DiscordNative = {
const BetterDiscord = require("./BetterDiscord") const BetterDiscord = require("./BetterDiscord")
const _setImmediate = setImmediate;
const _clearImmediate = clearImmediate;
process.once('loaded', () => { process.once('loaded', () => {
// Implementing DiscordNative // Implementing DiscordNative
global.DiscordNative = DiscordNative; global.DiscordNative = DiscordNative;
@ -56,13 +54,14 @@ process.once('loaded', () => {
global.Buffer = Buffer global.Buffer = Buffer
global.require = require global.require = require
// We keep these two functions in global because electron doesn't put these global.setImmediate = function(callback, ...args){
// nodejs APIs in the module scope, and these two functions return setTimeout(callback, 0, ...args);
// aren't harmful at all. };
global.setImmediate = _setImmediate; global.clearImmediate = clearTimeout;
global.clearImmediate = _clearImmediate;
const buildInfo = electron.remote.getGlobal("BuildInfo")
console.log("%c%s", "color: #3767ad;font-size:25px", 'Lightcord Client\nhttps://github.com/Lightcord/Lightcord'); console.log("%c%s", "color: #3767ad;font-size:25px", 'Lightcord Client\nhttps://github.com/Lightcord/Lightcord');
console.log("%c%s", "font-size:15px", `Version: ${buildInfo.version}\nCommit: ${buildInfo.commit || "Unknown"}`)
let ftime = Date.now() let ftime = Date.now()

View File

@ -121,6 +121,10 @@ function handleNotificationsClear() {
} }
function handleNotificationShow(e, notification) { function handleNotificationShow(e, notification) {
if(lastUsedTheme !== notification.theme){
lastUsedTheme = notification.theme
webContentsSend(notificationWindow, "UPDATE_THEME", lastUsedTheme)
}
notifications.push(notification); notifications.push(notification);
updateNotifications(); updateNotifications();
setTimeout(() => { setTimeout(() => {

View File

@ -2,6 +2,14 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<style>
.notification {
border-radius: 10px;
}
.notification-icon {
border-radius: 10px
}
</style>
</head> </head>
<body> <body>
<div id="notifications-mount"></div> <div id="notifications-mount"></div>

View File

@ -5743,9 +5743,14 @@
} }
}), i.default.createElement("div", { }), i.default.createElement("div", {
className: "notification-body" className: "notification-body"
}, i.default.createElement("header", null, this.props.title), i.default.createElement("p", {className: "notif-text theme-"+this.props.theme}, this.props.body))), i.default.createElement("div", { }, i.default.createElement("header", null, this.props.title),
className: "notification-logo" i.default.createElement("p", {
})) className: "notif-text theme-"+this.props.theme
}, truncateString(this.props.body, 40)))),
i.default.createElement("div", {
className: "notification-logo"
})
)
} }
}), }),
f = (0, o.default)({ f = (0, o.default)({
@ -5801,6 +5806,12 @@
} }
}); });
t.Notifications = f t.Notifications = f
function truncateString(str, num) {
if (str.length <= num) {
return str
}
return str.slice(0, num) + '...'
}
}, function(e, t, n) { }, function(e, t, n) {
"use strict"; "use strict";
var r = n(0), var r = n(0),

77
package-lock.json generated
View File

@ -42,6 +42,12 @@
"integrity": "sha512-+KQ+/koZ7sJXnf5cnCANofY6yXAdYJNEoVZEuWcwJfuWbUp9u6l09I7KhwD+ivU+cdz7JId4V5ukxscWtHdSuw==", "integrity": "sha512-+KQ+/koZ7sJXnf5cnCANofY6yXAdYJNEoVZEuWcwJfuWbUp9u6l09I7KhwD+ivU+cdz7JId4V5ukxscWtHdSuw==",
"dev": true "dev": true
}, },
"@types/electron-devtools-installer": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@types/electron-devtools-installer/-/electron-devtools-installer-2.2.0.tgz",
"integrity": "sha512-HJNxpaOXuykCK4rQ6FOMxAA0NLFYsf7FiPFGmab0iQmtVBHSAfxzy3MRFpLTTDDWbV0yD2YsHOQvdu8yCqtCfw==",
"dev": true
},
"@types/events": { "@types/events": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@ -698,6 +704,12 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" "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": { "jsbn": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@ -760,6 +772,15 @@
"dev": true, "dev": true,
"optional": 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": { "lowercase-keys": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@ -846,6 +867,12 @@
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" "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": { "object-keys": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@ -912,6 +939,17 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true "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": { "proto-list": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -944,6 +982,35 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" "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": { "readable-stream": {
"version": "2.3.7", "version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
@ -1037,6 +1104,16 @@
"truncate-utf8-bytes": "^1.0.0" "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": { "semver": {
"version": "7.3.2", "version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",

View File

@ -27,12 +27,15 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@types/auto-launch": "^5.0.1", "@types/auto-launch": "^5.0.1",
"@types/electron-devtools-installer": "^2.2.0",
"@types/mkdirp": "^1.0.0", "@types/mkdirp": "^1.0.0",
"@types/node": "^12.12.39", "@types/node": "^12.12.39",
"@types/rimraf": "^3.0.0", "@types/rimraf": "^3.0.0",
"@types/uuid": "^8.0.0", "@types/uuid": "^8.0.0",
"@types/yauzl": "^2.9.1", "@types/yauzl": "^2.9.1",
"electron": "8.3.0", "electron": "8.3.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"terser": "^4.7.0" "terser": "^4.7.0"
} }
} }