diff --git a/BetterDiscordApp/src/modules/core.js b/BetterDiscordApp/src/modules/core.js index ffda02b..140c9fe 100644 --- a/BetterDiscordApp/src/modules/core.js +++ b/BetterDiscordApp/src/modules/core.js @@ -82,7 +82,7 @@ Core.prototype.init = async function() { Utils.log("Startup", "Loading Themes"); await themeModule.loadThemes(); - DOM.addStyle("customcss", atob(DataStore.getBDData("bdcustomcss"))); + DOM.addStyle("customcss", Buffer.from(DataStore.getBDData("bdcustomcss"), "base64").toString("utf8")); window.addEventListener("beforeunload", function() { if (settingsCookie["bda-dc-0"]) document.querySelector(".btn.btn-disconnect").click(); diff --git a/BetterDiscordApp/src/modules/dataStore.js b/BetterDiscordApp/src/modules/dataStore.js index ffec2fc..e4be4b5 100644 --- a/BetterDiscordApp/src/modules/dataStore.js +++ b/BetterDiscordApp/src/modules/dataStore.js @@ -22,8 +22,8 @@ export default new class DataStore { initialize() { try { - if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "binary"); - const data = JSON.parse(fs.readFileSync(this.BDFile, "binary")) + if (!fs.existsSync(this.BDFile)) fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); + const data = JSON.parse(fs.readFileSync(this.BDFile, "utf-8")) if (data.hasOwnProperty("settings")) this.data = data; if (!fs.existsSync(this.settingsFile)) return; let settings = __non_webpack_require__(this.settingsFile); @@ -62,7 +62,7 @@ export default new class DataStore { setSettingGroup(key, data) { this.data.settings[releaseChannel][key] = data; - fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "binary"); + fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); } getBDData(key) { @@ -71,7 +71,7 @@ export default new class DataStore { setBDData(key, value) { this.data[key] = value; - fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "binary"); + fs.writeFileSync(this.BDFile, JSON.stringify(this.data, null, 4), "utf-8"); } getPluginData(pluginName, key) { @@ -85,12 +85,12 @@ export default new class DataStore { if (value === undefined) return; if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; this.pluginData[pluginName][key] = value; - fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "binary"); + fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8"); } deletePluginData(pluginName, key) { if (this.pluginData[pluginName] === undefined) this.pluginData[pluginName] = {}; delete this.pluginData[pluginName][key]; - fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "binary"); + fs.writeFileSync(this.getPluginFile(pluginName), JSON.stringify(this.pluginData[pluginName], null, 4), "utf-8"); } }; \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/ApiPreview.jsx b/BetterDiscordApp/src/ui/ApiPreview.jsx index a312457..621e425 100644 --- a/BetterDiscordApp/src/ui/ApiPreview.jsx +++ b/BetterDiscordApp/src/ui/ApiPreview.jsx @@ -6,9 +6,6 @@ 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(){ @@ -32,7 +29,7 @@ export default class ApiPreview extends React.PureComponent { These components are here for the plugin devs. They can quickly embed any component below with this panel.
All these components have error handling. If you want none, add `.original` after the component path. - We do not recommend modifying these component by a plugin. Only do this if you know what you are doing. + We do not recommend modifying these component with plugins. Only do this if you know what you are doing. { @@ -42,238 +39,9 @@ export default class ApiPreview extends React.PureComponent { , 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
-
- {React.createElement(comp, getProps())} -
-
- } - let renderCode = () => { - return
-
- - JSX - - - - - React - - -
-
- } - let getStrForProp = (value, compPath, lang) => { - 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)){ - if(compPath === "Lightcord.Api.Components.general.Tabs"){ - if(lang === "react"){ - return `React.createElement("div", {style: { - marginTop: "20px", marginBottom: "20px" -}}, - React.createElement("div", {style: { - backgroundColor: "var(--background-secondary)", - padding: "30px 30px", - borderRadius: "8px" - }, className: "lc-tab-box-shadow" }, - React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs") - ) -)` - }else if(lang === "jsx"){ - return `
-
- Preview tabs -
-
` - } - } - 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], compPath, lang) - }else{ - let str = key+"=" - if(typeof props[key] === "string"){ - str += JSON.stringify(props[key]) - }else{ - str += `{${getStrForProp(props[key], compPath, lang)}}` - } - propStrings.push(str) - } - }) - let openTag - if(childrenProp){ - openTag = `<${compPath} ${propStrings.join(" ")}>` - let closeTag = `` - 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 = getStrForProp(children, compPath, lang) - } - 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], compPath, lang).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 += "}" - } - let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children) - return `React.createElement(${compPath}, ${propObject}, ${childrenData})` - } - } - let help = comp.help || {} - let info = help.info ? - {help.info} - : null - let warn = help.warn ? - {help.warn} - : null - let danger = help.danger ? - {help.danger} - : null - let error = help.error ? - {help.error} - : null - let success = help.success ? - {help.success} - : null - return (
- - {comp.displayName || comp.name} - - {info} - {success} - {warn} - {error} - {danger} - {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 [ - - {key} - , - { - 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}/>, -
- ] - })} - -
) + const compName = comp.displayName || comp.name + const compPath = `Lightcord.Api.Components.${Object.keys(window.Lightcord.Api.Components).find(e => window.Lightcord.Api.Components[e][compName])}.${compName}` + return }) ] } @@ -281,4 +49,244 @@ export default class ApiPreview extends React.PureComponent { get renders(){ } +} + +class ComponentPreview extends React.Component { + constructor(props){ + super(props) + this.state = { + tab: "preview", + elem: props.comp, + options: {} + } + } + + render(){ + const comp = this.props.comp + let AllPreviews = [] + if(comp.AllPreviews)AllPreviews = comp.AllPreviews + let state = this.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
+
+ {React.createElement(comp, getProps())} +
+
+ } + let renderCode = () => { + return
+
+ + JSX + + + {React.createElement(() => { + return + })} + + + React + + + {React.createElement(() => { + return + })} + +
+
+ } + let getStrForProp = (value, compPath, lang) => { + 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)){ + if(compPath === "Lightcord.Api.Components.general.Tabs"){ + if(lang === "react"){ + return `React.createElement("div", {style: { +marginTop: "20px", marginBottom: "20px" +}}, +React.createElement("div", {style: { +backgroundColor: "var(--background-secondary)", +padding: "30px 30px", +borderRadius: "8px" +}, className: "lc-tab-box-shadow" }, +React.createElement(Lightcord.Api.Components.general.Title, null, "Preview tabs") +) +)` + }else if(lang === "jsx"){ + return `
+
+ Preview tabs +
+
` + } + } + 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], compPath, lang) + }else{ + let str = key+"=" + if(typeof props[key] === "string"){ + str += JSON.stringify(props[key]) + }else{ + str += `{${getStrForProp(props[key], compPath, lang)}}` + } + propStrings.push(str) + } + }) + let openTag + if(childrenProp){ + openTag = `<${compPath} ${propStrings.join(" ")}>` + let closeTag = `` + 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 = getStrForProp(children, compPath, lang) + } + 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], compPath, lang).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 += "}" + } + let childrenData = typeof children === "string" && children.startsWith("React.createElement") ? children : JSON.stringify(children) + return `React.createElement(${compPath}, ${propObject}, ${childrenData})` + } + } + let help = comp.help || {} + let info = help.info ? + {help.info} + : null + let warn = help.warn ? + {help.warn} + : null + let danger = help.danger ? + {help.danger} + : null + let error = help.error ? + {help.error} + : null + let success = help.success ? + {help.success} + : null + return (
+ + {comp.displayName || comp.name} + + {info} + {success} + {warn} + {error} + {danger} + {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 [ + + {key} + , + { + return { + value: "opt-"+index, + label: JSON.stringify(e[Object.keys(e)[0]]) + } + })} value={"opt-"+(state.options[key] || "0")} onChange={(value) => { + this.setState({ + options: Object.assign({}, state.options, { + [key]: (value.value || "0").replace("opt-", "") + }) + }) + }} searchable={true}/>, +
+ ] + })} + { + this.setState({ + tab + }) + }}/> +
) + } } \ No newline at end of file diff --git a/BetterDiscordApp/src/ui/addonlist.jsx b/BetterDiscordApp/src/ui/addonlist.jsx index e4d1e0d..9dfab7c 100644 --- a/BetterDiscordApp/src/ui/addonlist.jsx +++ b/BetterDiscordApp/src/ui/addonlist.jsx @@ -135,8 +135,8 @@ export default class CardList extends BDV2.reactComponent { getAddons() { const sortedAddons = this.list.sort((a, b) => { 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 second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : b[this.state.sort]; + const first = a.plugin && a.plugin[`get${cap}`] ? this.getString(a.plugin[`get${cap}`]()) : this.getString(a[this.state.sort]); + const second = b.plugin && b.plugin[`get${cap}`] ? this.getString(b.plugin[`get${cap}`]()) : this.getString(b[this.state.sort]); if (typeof(first) == "string") return first.toLocaleLowerCase().localeCompare(second.toLocaleLowerCase()); if (first > second) return 1; if (second > first) return -1; diff --git a/BetterDiscordApp/src/ui/cssEditor.js b/BetterDiscordApp/src/ui/cssEditor.js index 47a0ff9..42f3943 100644 --- a/BetterDiscordApp/src/ui/cssEditor.js +++ b/BetterDiscordApp/src/ui/cssEditor.js @@ -72,7 +72,7 @@ export default class V2C_CssEditor extends BDV2.reactComponent { const _ccss = DataStore.getBDData("bdcustomcss"); let ccss = ""; if (_ccss && _ccss !== "") { - ccss = atob(_ccss); + ccss = Buffer.from(_ccss, "base64").toString("utf8"); } return ccss; } @@ -193,7 +193,7 @@ export default class V2C_CssEditor extends BDV2.reactComponent { } saveCss() { - DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue())); + DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); } detach() { diff --git a/BetterDiscordApp/src/ui/cssEditorDetached.js b/BetterDiscordApp/src/ui/cssEditorDetached.js index edc0cca..d7949f2 100644 --- a/BetterDiscordApp/src/ui/cssEditorDetached.js +++ b/BetterDiscordApp/src/ui/cssEditorDetached.js @@ -62,7 +62,7 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent { const _ccss = DataStore.getBDData("bdcustomcss"); let ccss = ""; if (_ccss && _ccss !== "") { - ccss = atob(_ccss); + ccss = Buffer.from(_ccss, "base64").toString("utf8"); } return ccss; } @@ -169,6 +169,6 @@ export default class V2C_CssEditorDetached extends BDV2.reactComponent { } saveCss() { - DataStore.setBDData("bdcustomcss", btoa(this.editor.session.getValue())); + DataStore.setBDData("bdcustomcss", Buffer.from(this.editor.session.getValue(), "utf-8").toString("base64")); } } \ No newline at end of file diff --git a/src/common/Settings.ts b/src/common/Settings.ts index 9139060..ebdaab5 100644 --- a/src/common/Settings.ts +++ b/src/common/Settings.ts @@ -1,63 +1,71 @@ -// Imported from main Discord package. - -import * as fs from "fs" -import * as path from "path" - -// TODO: sync fs operations could cause slowdown and/or freezes, depending on usage -// if this is fine, remove this todo -export default class Settings { - path: string; - lastSaved: string; - settings: any; - lastModified: number; - constructor(root:string) { - this.path = path.join(root, 'settings.json'); - console.log(this.path) - try { - this.lastSaved = fs.readFileSync(this.path, "utf8"); - this.settings = JSON.parse(this.lastSaved); - } catch (e) { - this.lastSaved = ''; - this.settings = {}; - } - this.lastModified = this._lastModified(); - } - - _lastModified() { - try { - return fs.statSync(this.path).mtime.getTime(); - } catch (e) { - return 0; - } - } - - get(key, defaultValue = false) { - if (this.settings.hasOwnProperty(key)) { - return this.settings[key]; - } - - return defaultValue; - } - - set(key, value) { - this.settings[key] = value; - } - - save() { - if (this.lastModified && this.lastModified !== this._lastModified()) { - console.warn('Not saving settings, it has been externally modified.'); - return; - } - - try { - const toSave = JSON.stringify(this.settings, null, 2); - if (this.lastSaved != toSave) { - this.lastSaved = toSave; - fs.writeFileSync(this.path, toSave, "utf8"); - this.lastModified = this._lastModified(); - } - } catch (err) { - console.warn('Failed saving settings with error: ', err); - } - } -} +// Imported from main Discord package. + +import * as fs from "fs" +import * as path from "path" + +// TODO: sync fs operations could cause slowdown and/or freezes, depending on usage +// if this is fine, remove this todo +export default class Settings { + path: string; + lastSaved: string; + settings: any; + lastModified: number; + constructor(root:string) { + this.path = path.join(root, 'settings.json'); + console.log(this.path) + try { + this.lastSaved = fs.readFileSync(this.path, "utf8"); + this.settings = JSON.parse(this.lastSaved); + } catch (e) { + this.lastSaved = ''; + this.settings = {}; + } + this.lastModified = this._lastModified(); + } + + _lastModified() { + try { + return fs.statSync(this.path).mtime.getTime(); + } catch (e) { + return 0; + } + } + + get(key, defaultValue = false) { + if (this.settings.hasOwnProperty(key)) { + return this.settings[key]; + } + + return defaultValue; + } + + set(key, value) { + this.settings[key] = value; + } + + delete(key){ + delete this.settings[key] + } + + exists(key){ + return key in this.settings + } + + save() { + if (this.lastModified && this.lastModified !== this._lastModified()) { + console.warn('Not saving settings, it has been externally modified.'); + return; + } + + try { + const toSave = JSON.stringify(this.settings, null, 2); + if (this.lastSaved != toSave) { + this.lastSaved = toSave; + fs.writeFileSync(this.path, toSave, "utf8"); + this.lastModified = this._lastModified(); + } + } catch (err) { + console.warn('Failed saving settings with error: ', err); + } + } +}