diff --git a/src/data/strings.js b/src/data/strings.js index 0f91fa6e..fdf8a351 100644 --- a/src/data/strings.js +++ b/src/data/strings.js @@ -195,6 +195,7 @@ export default { }, Addons: { title: "{{name}} v{{version}} by {{author}}", + byline: "by {{author}}", openFolder: "Open {{type}} Folder", reload: "Reload", addonSettings: "Settings", diff --git a/src/modules/componentpatcher.js b/src/modules/componentpatcher.js index e9ed923e..d6c050fc 100644 --- a/src/modules/componentpatcher.js +++ b/src/modules/componentpatcher.js @@ -149,4 +149,6 @@ export default new class ComponentPatcher { }); } -}; \ No newline at end of file +}; + +// as part of utility classes, i would like a way to distinguish channel types from the .content-3at_AU element. other than that, can't think of anything \ No newline at end of file diff --git a/src/modules/pluginmanager.js b/src/modules/pluginmanager.js index fad1991f..7638cb7f 100644 --- a/src/modules/pluginmanager.js +++ b/src/modules/pluginmanager.js @@ -35,6 +35,7 @@ export default new class PluginManager extends AddonManager { const errors = super.initialize(); this.setupFunctions(); Settings.registerPanel("plugins", Strings.Panels.plugins, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.plugins, this.addonList, this.state, { + type: this.prefix, folder: this.addonFolder, onChange: this.togglePlugin.bind(this), reload: this.reloadPlugin.bind(this), @@ -76,10 +77,10 @@ export default new class PluginManager extends AddonManager { const PluginClass = addon.exports; const thePlugin = new PluginClass(); addon.instance = thePlugin; - addon.name = thePlugin.getName() || addon.name; - addon.author = thePlugin.getAuthor() || addon.author || "No author"; - addon.description = thePlugin.getDescription() || addon.description || "No description"; - addon.version = thePlugin.getVersion() || addon.version || "No version"; + addon.name = thePlugin.getName ? thePlugin.getName() : addon.name || "No name"; + addon.author = thePlugin.getAuthor ? thePlugin.getAuthor() : addon.author || "No author"; + addon.description = thePlugin.getDescription ? thePlugin.getDescription() : addon.description || "No description"; + addon.version = thePlugin.getVersion ? thePlugin.getVersion() : addon.version || "No version"; try { if (typeof(addon.instance.load) == "function") addon.instance.load(); } diff --git a/src/modules/thememanager.js b/src/modules/thememanager.js index 4c393e7f..0a1c36b7 100644 --- a/src/modules/thememanager.js +++ b/src/modules/thememanager.js @@ -22,6 +22,7 @@ export default new class ThemeManager extends AddonManager { initialize() { const errors = super.initialize(); Settings.registerPanel("themes", Strings.Panels.themes, {element: () => SettingsRenderer.getAddonPanel(Strings.Panels.themes, this.addonList, this.state, { + type: this.prefix, folder: this.addonFolder, onChange: this.toggleTheme.bind(this), reload: this.reloadTheme.bind(this), diff --git a/src/structs/psconnection.js b/src/structs/psconnection.js index c8bf5a3d..74227fe7 100644 --- a/src/structs/psconnection.js +++ b/src/structs/psconnection.js @@ -54,7 +54,7 @@ export default new class PublicServersConnection { if (term) queries.push(`term=${term.replace(/ /g, "%20")}`); if (from) queries.push(`from=${from}`); const query = `?${queries.join("&")}`; - + try { const response = await fetch(`${this.endPoint}${query}`, {method: "GET"}); const data = await response.json(); diff --git a/src/styles/builtins/publicservers.css b/src/styles/builtins/publicservers.css index 7d15d7a6..6010e95f 100644 --- a/src/styles/builtins/publicservers.css +++ b/src/styles/builtins/publicservers.css @@ -256,7 +256,12 @@ .bd-pagination button:hover, .bd-pagination button:active { opacity: 1; - background: transparent; + background: var(--background-accent); +} + +.bd-pagination button:active { + opacity: 1; + background: var(--background-secondary); } .bd-pagination button svg { diff --git a/src/styles/ui/addonlist.css b/src/styles/ui/addonlist.css index 76e48b7d..4895a5e4 100644 --- a/src/styles/ui/addonlist.css +++ b/src/styles/ui/addonlist.css @@ -36,11 +36,9 @@ display: flex; flex-direction: column; margin-bottom: 20px; - padding: 16px; border-radius: 5px; overflow: hidden; - background: var(--background-secondary-alt); - border: 1px solid var(--background-tertiary); + background: var(--background-secondary); } .bd-addon-list.bd-grid-view .bd-addon-card { @@ -49,6 +47,8 @@ .bd-addon-list .bd-addon-header { color: var(--header-primary); + background: var(--background-secondary-alt); + padding: 16px; font-size: 14px; line-height: 20px; font-weight: 600; @@ -58,8 +58,42 @@ overflow: hidden; } +.bd-addon-header .bd-icon { + margin-right: 8px; +} + +.bd-title, +.bd-name, +.bd-meta { + display: inline; +} + +.bd-title { + flex: 1; +} + +.bd-meta { + color: var(--text-muted); +} + +.bd-name::after, +.bd-version::after { + display: inline; + content: " "; +} + +.bd-grid-view .bd-title { + display: flex; + flex-direction: column; +} + +.bd-grid-view .bd-meta { + font-size: 12px; +} + .bd-description-wrap { flex: 1; + padding: 8px 16px 0 16px; } .bd-addon-list .bd-description { @@ -88,8 +122,7 @@ display: flex; align-items: center; justify-content: space-between; - padding-top: 8px; - border-top: thin solid var(--background-modifier-accent); + padding: 8px 16px 16px 16px; overflow: hidden; } diff --git a/src/ui/draganddrop.js b/src/ui/draganddrop.js new file mode 100644 index 00000000..40097064 --- /dev/null +++ b/src/ui/draganddrop.js @@ -0,0 +1,50 @@ +/* +
+
+
+
Install Addon
+
Installs this plugin automatically
+
+
+*/ + +/* +.bd-drop-container-wrap { + display: none; + justify-content: center; + align-items: center; + background: rgba(0,0,0,0.7); + z-index: 1000; + width: 100%; +} + +.bd-drop-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: #3e82e5; + padding: 20px; + border-radius: 20px; +} + +.bd-drop-icon { + background: url('https://maxcdn.icons8.com/Share/icon/p1em/Very_Basic/plus1600.png'); + background-size: contain; + height: 100px; + width: 100px; + filter: invert(); +} + +.bd-drop-title { + color: white; + font-weight: 600; + font-size: 24px; + margin-top: 8px +} + +.bd-drop-message { + color: white; + margin-top: 8px; +} +*/ \ No newline at end of file diff --git a/src/ui/icons/extension.jsx b/src/ui/icons/extension.jsx new file mode 100644 index 00000000..0f396de0 --- /dev/null +++ b/src/ui/icons/extension.jsx @@ -0,0 +1,11 @@ +import {React} from "modules"; + +export default class Extension extends React.Component { + render() { + const size = this.props.size || "24px"; + return + + + ; + } +} \ No newline at end of file diff --git a/src/ui/icons/theme.jsx b/src/ui/icons/theme.jsx new file mode 100644 index 00000000..f2e9906a --- /dev/null +++ b/src/ui/icons/theme.jsx @@ -0,0 +1,11 @@ +import {React} from "modules"; + +export default class Theme extends React.Component { + render() { + const size = this.props.size || "24px"; + return + + + ; + } +} \ No newline at end of file diff --git a/src/ui/modals.js b/src/ui/modals.js index 56e7b337..23ac42b8 100644 --- a/src/ui/modals.js +++ b/src/ui/modals.js @@ -267,7 +267,7 @@ export default class Modals { const mc = this.ModalComponents; const modal = props => { - return React.createElement(mc.ModalRoot, Object.assign({size: mc.ModalSize.SMALL, className: "bd-addon-modal"}, props), + return React.createElement(mc.ModalRoot, Object.assign({size: mc.ModalSize.MEDIUM, className: "bd-addon-modal"}, props), React.createElement(mc.ModalHeader, {separator: false, className: "bd-addon-modal-header"}, React.createElement(this.FormTitle, {tag: "h4"}, `${name} Settings`), React.createElement(this.FlexElements.Child, {grow: 0}, diff --git a/src/ui/publicservers/menu.js b/src/ui/publicservers/menu.js index ba971c37..fa9add0d 100644 --- a/src/ui/publicservers/menu.js +++ b/src/ui/publicservers/menu.js @@ -16,8 +16,8 @@ const EMPTY_RESULTS = { servers: [], size: 0, total: 0, - page: 0, - numPages: 0 + page: 1, + numPages: 1 }; export default class PublicServers extends React.Component { @@ -166,8 +166,7 @@ export default class PublicServers extends React.Component { return [React.createElement(SettingsTitle, {text: this.title, button: connectButton}), this.state.results.numPages > 1 && this.pagination, content, - this.state.results.numPages > 1 && this.pagination, - this.state.results.numPages > 1 && this.state.query && React.createElement(SettingsTitle, {text: this.title}) + this.state.results.numPages > 1 && this.pagination ]; } @@ -179,6 +178,8 @@ export default class PublicServers extends React.Component {
+
+
; } diff --git a/src/ui/settings.js b/src/ui/settings.js index 082ebcab..6ce98b1d 100644 --- a/src/ui/settings.js +++ b/src/ui/settings.js @@ -78,14 +78,20 @@ export default new class SettingsRenderer { if (collection.disabled) continue; insert({ section: collection.name, - label: collection.name, + label: collection.name.toString(), + className: `bd-${collection.id}-tab`, element: () => this.buildSettingsPanel(collection.id, collection.name, collection.settings, Settings.state[collection.id], Settings.onSettingChange.bind(Settings, collection.id), collection.button ? collection.button : null) }); } for (const panel of Settings.panels.sort((a,b) => a.order > b.order)) { if (panel.clickListener) panel.onClick = (event) => panel.clickListener(thisObject, event, returnValue); + if (!panel.className) panel.className = `bd-${panel.id}-tab`; + if (typeof(panel.label) !== "string") panel.label = panel.label.toString(); insert(panel); } + // for (const tab of returnValue) { + // if (!tab.className) tab.className = `${DOM.escapeID(tab.section).toLowerCase()}-tab`; + // } }); this.forceUpdate(); } diff --git a/src/ui/settings/addoncard.jsx b/src/ui/settings/addoncard.jsx index 2ce49553..1290db6d 100644 --- a/src/ui/settings/addoncard.jsx +++ b/src/ui/settings/addoncard.jsx @@ -11,6 +11,8 @@ import MoneyIcon from "../icons/dollarsign"; import WebIcon from "../icons/globe"; import PatreonIcon from "../icons/patreon"; import SupportIcon from "../icons/support"; +import ExtIcon from "../icons/extension"; +import ThemeIcon from "../icons/theme"; import Modals from "../modals"; import Toasts from "../toasts"; @@ -23,6 +25,11 @@ const LinkIcons = { }; const Tooltip = WebpackModules.getByDisplayName("Tooltip"); +const LayerStack = WebpackModules.getByProps("popLayer"); +const UserStore = WebpackModules.getByProps("getCurrentUser"); +const ChannelStore = WebpackModules.getByProps("getDMFromUserId"); +const PrivateChannelActions = WebpackModules.getByProps("openPrivateChannel"); +const ChannelActions = WebpackModules.getByProps("selectPrivateChannel"); export default class AddonCard extends React.Component { @@ -38,6 +45,7 @@ export default class AddonCard extends React.Component { this.onChange = this.onChange.bind(this); this.reload = this.reload.bind(this); this.showSettings = this.showSettings.bind(this); + this.messageAuthor = this.messageAuthor.bind(this); } showSettings() { @@ -66,15 +74,34 @@ export default class AddonCard extends React.Component { this.forceUpdate(); } + messageAuthor() { + if (!this.props.addon.authorId) return; + if (LayerStack) LayerStack.popLayer(); + if (!UserStore || !ChannelActions || !ChannelStore || !PrivateChannelActions) return; + const selfId = UserStore.getCurrentUser().id; + if (selfId == this.props.addon.authorId) return; + const privateChannelId = ChannelStore.getDMFromUserId(this.props.addon.authorId); + if (privateChannelId) return ChannelActions.selectPrivateChannel(privateChannelId); + PrivateChannelActions.openPrivateChannel(selfId, this.props.addon.authorId); + } + buildTitle(name, version, author) { - const title = Strings.Addons.title.split(/({{[A-Za-z]+}})/); - const nameIndex = title.findIndex(s => s == "{{name}}"); - if (nameIndex) title[nameIndex] = React.createElement("span", {className: "bd-name"}, name); - const versionIndex = title.findIndex(s => s == "{{version}}"); - if (nameIndex) title[versionIndex] = React.createElement("span", {className: "bd-version"}, version); - const authorIndex = title.findIndex(s => s == "{{author}}"); - if (nameIndex) title[authorIndex] = React.createElement("span", {className: "bd-author"}, author); - return title.flat(); + const authorArray = Strings.Addons.byline.split(/({{[A-Za-z]+}})/); + const authorComponent = author.link || author.id + ? {author.name} + : {author.name}; + + const authorIndex = authorArray.findIndex(s => s == "{{author}}"); + if (authorIndex) authorArray[authorIndex] = authorComponent; + + return [ + React.createElement("div", {className: "bd-name"}, name), + React.createElement("div", {className: "bd-meta"}, + React.createElement("span", {className: "bd-version"}, `v${version}`), + ...authorArray + ) + ]; + } buildLink(which) { @@ -139,7 +166,8 @@ export default class AddonCard extends React.Component { return
- {this.buildTitle(name, version, author)} + {this.props.type === "plugin" ? : } +
{this.buildTitle(name, version, {name: author, id: this.props.addon.authorId, link: this.props.addon.authorLink})}
{SimpleMarkdown.parseToReact(description)}
diff --git a/src/ui/settings/addonlist.jsx b/src/ui/settings/addonlist.jsx index 1df6bed1..b9836f71 100644 --- a/src/ui/settings/addonlist.jsx +++ b/src/ui/settings/addonlist.jsx @@ -41,16 +41,16 @@ export default class AddonList extends React.Component { onControlChange(control, value) { const addonlistControls = DataStore.getBDData("addonlistControls") || {}; - if (!addonlistControls[this.props.title.toLowerCase()]) addonlistControls[this.props.title.toLowerCase()] = {}; - addonlistControls[this.props.title.toLowerCase()][control] = value; + if (!addonlistControls[this.props.type]) addonlistControls[this.props.type] = {}; + addonlistControls[this.props.type][control] = value; DataStore.setBDData("addonlistControls", addonlistControls); } getControlState(control, defaultValue) { const addonlistControls = DataStore.getBDData("addonlistControls") || {}; - if (!addonlistControls[this.props.title.toLowerCase()]) return defaultValue; - if (!addonlistControls[this.props.title.toLowerCase()].hasOwnProperty(control)) return defaultValue; - return addonlistControls[this.props.title.toLowerCase()][control]; + if (!addonlistControls[this.props.type]) return defaultValue; + if (!addonlistControls[this.props.type].hasOwnProperty(control)) return defaultValue; + return addonlistControls[this.props.type][control]; } update() { @@ -107,9 +107,9 @@ export default class AddonList extends React.Component { } get emptyImage() { - const message = Strings.Addons.blankSlateMessage.format({link: `https://betterdiscordlibrary.com/${this.props.title.toLowerCase()}`, type: this.props.title}).toString(); - return - + const message = Strings.Addons.blankSlateMessage.format({link: `https://betterdiscordlibrary.com/${this.props.type}`, type: this.props.type}).toString(); + return + ; } @@ -147,7 +147,7 @@ export default class AddonList extends React.Component { const renderedCards = sortedAddons.map(addon => { const hasSettings = addon.instance && typeof(addon.instance.getSettingsPanel) === "function"; const getSettings = hasSettings && addon.instance.getSettingsPanel.bind(addon.instance); - return ; + return ; }); const hasAddonsInstalled = this.props.addonList.length !== 0;