Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

350 lines
20 KiB

/**
* @name PlatformIndicators
* @displayName PlatformIndicators
* @authorId 415849376598982656
* @invite gvA2ree
*/
/*@cc_on
@if (@_jscript)
// Offer to self-install for clueless users that try to run this directly.
var shell = WScript.CreateObject("WScript.Shell");
var fs = new ActiveXObject("Scripting.FileSystemObject");
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins");
var pathSelf = WScript.ScriptFullName;
// Put the user at ease by addressing them in the first person
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
} else if (!fs.FolderExists(pathPlugins)) {
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
// Show the user where to put plugins in the future
shell.Exec("explorer " + pathPlugins);
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
}
WScript.Quit();
@else@*/
module.exports = (() => {
const config = {
info: {
name: "PlatformIndicators",
authors: [
{
name: "Strencher",
discord_id: "415849376598982656",
github_username: "Strencher",
twitter_username: "Strencher3"
}
],
version: "0.0.5",
description: "Adds indicators for every platform that the user is using. Source code availble on the repo in the 'src' folder.",
github: "https://github.com/Strencher/BetterDiscordStuff/blob/master/PlatformIndicators/APlatformIndicators.plugin.js",
github_raw: "https://raw.githubusercontent.com/Strencher/BetterDiscordStuff/master/PlatformIndicators/APlatformIndicators.plugin.js"
},
changelog: [
{
title: "v0.0.5",
type: "fixed",
items: [
"Thanks to @qwert#1441 for fixing the padding issue in chat messages!",
"I still need ideas where to show all of them at one position that is not next to the username... join my Support server => https://discord.gg/gvA2ree to send me ideas!"
]
},
{
title: "v0.0.4",
type: "added",
items: [
"2 Attempt to fix conflicts with BetterRoleColors.",
"It'll probably require you to update 2 times because the filename has changed.",
"Bug fixes... styling fixes..."
]
}
],
defaultConfig: [
{
type: "switch",
name: "Show in MemberList",
note: "Shows the platform indicators in the memberlist",
id: "showInMemberList",
value: true
},
{
type: "switch",
name: "Show next to username",
note: "Shows the platform indicators next the username in messages.",
id: "showOnMessages",
value: true
},
{
type: "switch",
name: "Show in Dmd List",
note: "Shows the platform indicators in the dm list.",
id: "showInDmsList",
value: true
},
{
type: "switch",
name: "Show next to discord tags",
note: "Shows the platform indicators right next to the discord tag.",
id: "showOnTags",
value: true
},
{
type: "switch",
name: "Ignore Bots",
note: "Ignores the status of bots which is always web anyways.",
id: "ignoreBots",
value: true
},
{
type: "category",
name: "icons",
id: "icons",
settings: [
{
type: "switch",
name: "Web Icon",
note: "Show the Web icon.",
id: "web",
value: true
},
{
type: "switch",
name: "Desktop Icon",
note: "Show the Desktop icon.",
id: "desktop",
value: true
},
{
type: "switch",
name: "Mobile Icon",
note: "Show the Mobile icon.",
id: "mobile",
value: true
}
]
}
]
};
//@ts-ignore
const BdApi = window.BdApi;
// @ts-ignore
return !global.ZeresPluginLibrary ? class {
constructor() {
this._config = config;
}
getName() { return config.info.name; }
getAuthor() { return config.info.authors.map(a => a.name).join(", "); }
getDescription() { return config.info.description; }
getVersion() { return config.info.version; }
load() {
BdApi.showConfirmationModal("Library plugin is needed", [`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`], {
confirmText: "Download",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => {
if (error)
return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
});
}
});
}
start() { }
stop() { }
} : (([Plugin, Api]) => {
const plugin = (Plugin, Api) => {
const { Utilities, WebpackModules, PluginUtilities, ReactTools, Patcher, Logger, DiscordModules: { React, UserStatusStore, Dispatcher, DiscordConstants: { ActionTypes } } } = Api;
const Utils = Object.assign(Utilities, {
joinClassNames: (...classNames) => classNames.filter(Boolean).join(" "),
capFirst(text) {
return text[0].toUpperCase() + text.slice(1);
}
});
const DesktopIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_desktop", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }),
React.createElement("path", { fill: "currentColor", d: "M4 2.5C2.897 2.5 2 3.397 2 4.5V15.5C2 16.604 2.897 17.5 4 17.5H11V19.5H7V21.5H17V19.5H13V17.5H20C21.103 17.5 22 16.604 22 15.5V4.5C22 3.397 21.103 2.5 20 2.5H4ZM20 4.5V13.5H4V4.5H20Z" }))));
const WebIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_web", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }),
React.createElement("path", { fill: "currentColor", d: "M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM11 19.93C7.05 19.44 4 16.08 4 12C4 11.38 4.08 10.79 4.21 10.21L9 15V16C9 17.1 9.9 18 11 18V19.93ZM17.9 17.39C17.64 16.58 16.9 16 16 16H15V13C15 12.45 14.55 12 14 12H8V10H10C10.55 10 11 9.55 11 9V7H13C14.1 7 15 6.1 15 5V4.59C17.93 5.78 20 8.65 20 12C20 14.08 19.2 15.97 17.9 17.39Z" }))));
const MobileIcon = React.memo(props => (React.createElement("svg", Object.assign({ className: "PI-icon_mobile", width: "24", height: "24" }, props, { viewBox: "0 0 24 24" }),
React.createElement("g", { fill: "none" },
React.createElement("path", { fill: "currentColor", d: "M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z" })))));
const Icons = {
mobile: MobileIcon,
web: WebIcon,
desktop: DesktopIcon
};
const getClass = (props = [], items = props, exclude = [], selector = false) => {
const module = WebpackModules.getModule(m => m && props.every(prop => m[prop] !== undefined) && exclude.every(e => m[e] == undefined));
if (!module)
return '';
return (selector ? '.' : '') + items.map(item => module[item]).join(selector ? '.' : ' ');
};
const { TooltipContainer: Tooltip } = WebpackModules.getByProps("TooltipContainer");
const StatusModule = WebpackModules.getByProps("Status", "getStatusMask");
const Flux = WebpackModules.getByProps("connectStores");
const MessageTimestamp = WebpackModules.getByProps("MessageTimestamp");
const { Messages } = WebpackModules.getByProps("Messages", "setLocale");
const AuthStore = WebpackModules.getByProps("getId", "getEmail");
let plugin, currentClientStatus;
const StatusIndicators = function StatusIndicators(props) {
if (!props)
return null;
return (React.createElement("div", { className: Utils.joinClassNames("PI-indicatorContainer", "PI-type_" + props.type) }, Object.keys(props).filter(e => plugin.settings.icons[e]).map(e => {
const color = StatusModule.getStatusColor(props[e]);
const Icon = Icons[e];
return React.createElement(Tooltip, { text: Utils.capFirst(e) + ": " + Messages[`STATUS_${(props[e] == "mobile" ? "mobile_online" : props[e]).toUpperCase()}`], position: "top" },
React.createElement(Icon, { style: { color }, width: "18", height: "18" }));
})));
};
return class PlatformIndicators extends Plugin {
constructor() {
super(...arguments);
this.css = `
.PI-indicatorContainer {
display: inline-flex;
}
.PI-indicatorContainer svg {
margin-left: 2px;
}
.header-23xsNx {
display: flex !important;
flex-direction: row !important;
}
.PI-container {
display: flex;
}
`;
this.getSettingsPanel = () => {
return this.buildSettingsPanel().getElement();
};
this.ON_PRESENCE_UPDATE = ({ user, clientStatus }) => {
if (user.id != AuthStore.getId())
return;
currentClientStatus = clientStatus;
UserStatusStore.emitChange();
};
}
getClients(userId) {
const isSelf = userId == AuthStore.getId();
const status = isSelf ? currentClientStatus : UserStatusStore.getState().clientStatuses[userId];
return status !== null && status !== void 0 ? status : {};
}
onStart() {
plugin = this;
PluginUtilities.addStyle(config.info.name, this.css);
Utils.suppressErrors(this.patchMessageHeader.bind(this))();
Utils.suppressErrors(this.patchMemberListItem.bind(this))();
Utils.suppressErrors(this.patchDmList.bind(this))();
Utils.suppressErrors(this.patchDiscordTag.bind(this))();
Dispatcher.subscribe(ActionTypes.PRESENCE_UPDATE, this.ON_PRESENCE_UPDATE);
}
async patchMemberListItem() {
const MemberListItem = WebpackModules.getByDisplayName("MemberListItem");
Patcher.after(MemberListItem.prototype, "renderDecorators", ({ props }, _, returnValue) => {
var _a;
if (!this.settings.showInMemberList)
return;
try {
const tree = (_a = returnValue === null || returnValue === void 0 ? void 0 : returnValue.props) === null || _a === void 0 ? void 0 : _a.children;
if (!Array.isArray(tree) || (this.settings.ignoreBots && props.user.bot))
return;
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(props.user.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "memberList" })));
tree.unshift(React.createElement(FluxWrapper, null));
}
catch (error) {
Logger.error("Error while patching MemberListItem:", error);
}
});
this.forceUpdate(getClass(["member"], ["member"], [], true));
}
patchMessageHeader() {
Patcher.after(MessageTimestamp, "default", (_, [props], returnValue) => {
if (!this.settings.showOnMessages)
return;
try {
const tree = Utils.getNestedProp(returnValue, "props.children.1.props.children");
if (!Array.isArray(tree) || (this.settings.ignoreBots && props.message.author.bot))
return;
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(props.message.author.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "chat" })));
tree.splice(2, 0, React.createElement(FluxWrapper, null));
}
catch (error) {
Logger.error("Error while patching MessageTimestammp:", error);
}
});
}
patchDmList() {
var _a;
const { default: PrivateChannel } = (_a = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "PrivateChannel"; })) !== null && _a !== void 0 ? _a : {};
Patcher.after(PrivateChannel.prototype, "render", (_this, _, ret) => {
const unpatch = Patcher.after(ret.type, "render", (_, __, ret) => {
var _a, _b;
unpatch();
if (!this.settings.showInDmsList)
return;
const tree = Utils.findInReactTree(ret, m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.className) === null || _a === void 0 ? void 0 : _a.indexOf("nameAndDecorators")) > -1; });
if (!tree)
return;
if (!Array.isArray(tree === null || tree === void 0 ? void 0 : tree.children) || (this.settings.ignoreBots && ((_b = (_a = _this.props) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.bot)))
return;
const FluxWrapper = Flux.connectStores([UserStatusStore], () => { var _a, _b; return this.getClients((_b = (_a = _this.props) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id); })(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "dmList" })));
tree.children = [
tree.children,
React.createElement(FluxWrapper, null)
];
});
});
this.forceUpdate(getClass(["privateChannels"], ["privateChannels"], [], true));
}
forceUpdate(selector) {
const nodes = document.querySelectorAll(selector);
if (!nodes.length)
return;
for (const node of nodes) {
const instance = ReactTools.getOwnerInstance(node);
if (!instance)
return;
instance.forceUpdate();
}
}
patchDiscordTag() {
const DiscordTag = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "DiscordTag"; });
const NameTag = WebpackModules.getModule(m => { var _a; return ((_a = m === null || m === void 0 ? void 0 : m.default) === null || _a === void 0 ? void 0 : _a.displayName) === "NameTag"; });
Patcher.after(DiscordTag, "default", (_, [{ user }], ret) => {
ret.props.user = user;
});
Patcher.after(NameTag, "default", (_, [args], ret) => {
if (!this.settings.showOnTags)
return;
const tree = ret === null || ret === void 0 ? void 0 : ret.props;
var { user } = args;
if (!Array.isArray(tree === null || tree === void 0 ? void 0 : tree.children) || (this.settings.ignoreBots && (user === null || user === void 0 ? void 0 : user.bot)))
return;
const FluxWrapper = Flux.connectStores([UserStatusStore], () => this.getClients(user === null || user === void 0 ? void 0 : user.id))(clients => React.createElement(StatusIndicators, Object.assign({}, clients, { type: "discordTag" })));
try {
tree.children.push(React.createElement(FluxWrapper, null));
}
catch (error) {
Logger.error("Failed to inject into NameTag:\n", error);
}
return ret;
});
}
onStop() {
Patcher.unpatchAll();
PluginUtilities.removeStyle(config.info.name);
Dispatcher.unsubscribe(ActionTypes.PRESENCE_UPDATE, this.ON_PRESENCE_UPDATE);
}
};
};
return plugin(Plugin, Api);
//@ts-ignore
})(global.ZeresPluginLibrary.buildPlugin(config));
})();
/*@end@*/