Advertisement
magnetos

platform

Jul 7th, 2023
711
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2. * @name PlatformIndicators
  3. * @displayName PlatformIndicators
  4. * @authorId 415849376598982656
  5. * @invite gvA2ree
  6. * @version 1.4.2
  7. */
  8. /*@cc_on
  9. @if (@_jscript)
  10.      
  11.     // Offer to self-install for clueless users that try to run this directly.
  12.     var shell = WScript.CreateObject("WScript.Shell");
  13.     var fs = new ActiveXObject("Scripting.FileSystemObject");
  14.     var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins");
  15.     var pathSelf = WScript.ScriptFullName;
  16.     // Put the user at ease by addressing them in the first person
  17.     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);
  18.     if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
  19.         shell.Popup("I"m in the correct folder already.", 0, "I"m already installed", 0x40);
  20.     } else if (!fs.FolderExists(pathPlugins)) {
  21.         shell.Popup("I can"t find the BetterDiscord plugins folder.\nAre you sure it"s even installed?", 0, "Can"t install myself", 0x10);
  22.     } else if (shell.Popup("Should I copy myself to BetterDiscord"s plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
  23.         fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
  24.         // Show the user where to put plugins in the future
  25.         shell.Exec("explorer " + pathPlugins);
  26.         shell.Popup("I"m installed!", 0, "Successfully installed", 0x40);
  27.     }
  28.     WScript.Quit();
  29. @else@*/
  30.  
  31. module.exports = (() => {
  32.     const config = {
  33.         info: {
  34.             name: "PlatformIndicators",
  35.             authors: [
  36.                 {
  37.                     name: "Strencher",
  38.                     discord_id: "415849376598982656",
  39.                     github_username: "Strencher",
  40.                     twitter_username: "Strencher3"
  41.                 }
  42.             ],
  43.             version: "1.4.2",
  44.             description: "Adds indicators for every platform that the user is using. Source code available on the repo in the 'src' folder.",
  45.             github: "https://github.com/Strencher/BetterDiscordStuff/blob/master/PlatformIndicators/APlatformIndicators.plugin.js",
  46.             github_raw: "https://raw.githubusercontent.com/Strencher/BetterDiscordStuff/master/PlatformIndicators/APlatformIndicators.plugin.js"
  47.         },
  48.         changelog: [
  49.             {
  50.                 title: "v1.4.2",
  51.                 type: "fixed",
  52.                 items: [
  53.                     "Fixed indicators not showing in user popout for new usernames.",
  54.                 ]
  55.             },
  56.         ],
  57.         defaultConfig: [
  58.             {
  59.                 type: "switch",
  60.                 name: "Show in MemberList",
  61.                 note: "Shows the platform indicators in the memberlist",
  62.                 id: "showInMemberList",
  63.                 value: true
  64.             },
  65.             {
  66.                 type: "switch",
  67.                 name: "Show next to username",
  68.                 note: "Shows the platform indicators next the username in messages.",
  69.                 id: "showInChat",
  70.                 value: true
  71.             },
  72.             {
  73.                 type: "switch",
  74.                 name: "Show in DMs List",
  75.                 note: "Shows the platform indicators in the dm list.",
  76.                 id: "showInDmsList",
  77.                 value: true
  78.             },
  79.             {
  80.                 type: "switch",
  81.                 name: "Show next to discord tags",
  82.                 note: "Shows the platform indicators right next to the discord tag.",
  83.                 id: "showInTags",
  84.                 value: true
  85.             },
  86.             {
  87.                 type: "switch",
  88.                 name: "Ignore Bots",
  89.                 note: "Ignores the status of bots which is always web anyways.",
  90.                 id: "ignoreBots",
  91.                 value: true
  92.             },
  93.             {
  94.                 type: "category",
  95.                 name: "icons",
  96.                 id: "icons",
  97.                 settings: [
  98.                     {
  99.                         type: "switch",
  100.                         name: "Web Icon",
  101.                         note: "Show the Web icon.",
  102.                         id: "web",
  103.                         value: true
  104.                     },
  105.                     {
  106.                         type: "switch",
  107.                         name: "Desktop Icon",
  108.                         note: "Show the Desktop icon.",
  109.                         id: "desktop",
  110.                         value: true
  111.                     },
  112.                     {
  113.                         type: "switch",
  114.                         name: "Mobile Icon",
  115.                         note: "Show the Mobile icon.",
  116.                         id: "mobile",
  117.                         value: true
  118.                     },
  119.                     {
  120.                         type: "switch",
  121.                         name: "Embedded Icon",
  122.                         note: "Show the Embedded icon.",
  123.                         id: "embedded",
  124.                         value: true
  125.                     }
  126.                 ]
  127.             }
  128.         ]
  129.     };
  130.    
  131.     return !global.ZeresPluginLibrary ? class {
  132.         constructor() {
  133.             this._config = config;
  134.         }
  135.         getName() {return config.info.name;}
  136.         getAuthor() {return config.info.authors.map(a => a.name).join(", ");}
  137.         getDescription() {return config.info.description;}
  138.         getVersion() {return config.info.version;}
  139.         load() {
  140.             BdApi.showConfirmationModal("Library plugin is needed", [`The library plugin needed for ${config.info.name} is missing. Please click Download Now to install it.`], {
  141.                 confirmText: "Download",
  142.                 cancelText: "Cancel",
  143.                 onConfirm: () => {
  144.                     require("request").get("https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", async (error, response, body) => {
  145.                         if (error)
  146.                             return require("electron").shell.openExternal("https://betterdiscord.net/ghdl?url=https://raw.githubusercontent.com/rauenzi/BDPluginLibrary/master/release/0PluginLibrary.plugin.js");
  147.                         await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
  148.                     });
  149.                 }
  150.             });
  151.         }
  152.         start() {}
  153.         stop() {}
  154.     } : (([Plugin, Api]) => {
  155.         const plugin = (Plugin, Api) => {
  156.             const {DiscordClasses, DOMTools, Utilities, WebpackModules, PluginUtilities, ReactTools, DiscordModules: {LocaleManager: {Messages}, UserStatusStore, UserStore}} = Api;
  157.             const Dispatcher = WebpackModules.getByProps("dispatch", "register");
  158.             const LocalActivityStore = WebpackModules.getByProps("getCustomStatusActivity");
  159.             const Flux = Object.assign({}, WebpackModules.getByProps("Store", "connectStores"), WebpackModules.getByProps("useStateFromStores"));
  160.             const SessionsStore = WebpackModules.getByProps("getSessions", "_dispatchToken");
  161.             const friendsRowClasses = WebpackModules.getByProps("hovered", "discriminator");
  162.  
  163.             const {DOM, Webpack, Webpack: {Filters}} = BdApi;
  164.             const [ChatHeader, NameTag, MemberListItem, DirectMessage, NewUserName, {LayerClassName = ""} = {}] = Webpack.getBulk.apply(null, [
  165.                 Filters.byProps("replyAvatar", "sizeEmoji"),
  166.                 Filters.byProps("bot", "nameTag"),
  167.                 Filters.byProps("wrappedName", "nameAndDecorators"),
  168.                 Filters.combine(Filters.byProps("wrappedName", "nameAndDecorators"), m => !m.container),
  169.                 Filters.byProps("discrimBase", "userTagUsernameBase"),
  170.                 Filters.byProps("LayerClassName")
  171.             ].map(fn => ({filter: fn})));
  172.  
  173.             class StringUtils {
  174.                 static upperFirst(string) {return string.charAt(0).toUpperCase() + string.slice(1);}
  175.                 static getStatusText(key, status) {
  176.                     return this.upperFirst(key) + ": " + Messages[`STATUS_${(status == "mobile" ? "mobile_online" : status).toUpperCase()}`];
  177.                 }
  178.             }
  179.  
  180.             const Settings = new class Settings extends Flux.Store {
  181.                 constructor() {super(Dispatcher, {});}
  182.                 _settings = PluginUtilities.loadSettings(config.info.name, {});
  183.  
  184.                 get(key, def) {
  185.                     return this._settings[key] ?? def;
  186.                 }
  187.  
  188.                 set(key, value) {
  189.                     this._settings[key] = value;
  190.                     this.emitChange();
  191.                 }
  192.             };
  193.  
  194.             const StoreWatcher = {
  195.                 _stores: [Settings, UserStatusStore, UserStore, SessionsStore],
  196.                 _listeners: new Set,
  197.                 onChange(callback) {
  198.                     this._listeners.add(callback);
  199.                 },
  200.                 offChange(callback) {
  201.                     this._listeners.add(callback);
  202.                 },
  203.                 _alertListeners() {
  204.                     StoreWatcher._listeners.forEach(l => l());
  205.                 },
  206.                 _init() {
  207.                     this._stores.forEach(store => store.addChangeListener(this._alertListeners));
  208.                 },
  209.                 _stop() {
  210.                     this._stores.forEach(store => store.addChangeListener(this._alertListeners));
  211.                 }
  212.             };
  213.  
  214.             const StatusColors = new Proxy({
  215.                 dnd: "#ED4245",
  216.                 idle: "#FAA81A",
  217.                 online: "#3BA55D",
  218.                 streaming: "#593695",
  219.                 offline: "#747F8D"
  220.             }, {
  221.                 get(target, key) {
  222.                     return target[key] ?? target.offline;
  223.                 }
  224.             });
  225.  
  226.             const isStreaming = () => LocalActivityStore.getActivities().some(e => e.type === 1);
  227.  
  228.             const getReactProps = (el, filter = _ => _) => {
  229.                 const instance = ReactTools.getReactInstance(el);
  230.  
  231.                 for (let current = instance.return, i = 0; i > 10000 || current !== null; current = current?.return, i++) {
  232.                     if (current?.pendingProps && filter(current.pendingProps)) return current.pendingProps;
  233.                 }
  234.  
  235.                 return null;
  236.             };
  237.  
  238.             // Taken from SolidJS' template function.
  239.             function template(html, check, isSVG) {
  240.                 const t = document.createElement("template");
  241.                 t.innerHTML = html;
  242.                 let node = t.content.firstChild;
  243.                 if (isSVG)
  244.                   node = node.firstChild;
  245.                 return node;
  246.               }
  247.  
  248.             const createElement = (type, props, ...children) => {
  249.                 if (typeof type === "function") return type({...props, children: [].concat()})
  250.  
  251.                 const node = document.createElement(type);
  252.  
  253.                 for (const key of Object.keys(props)) {
  254.                     if (key.indexOf("on") === 0) node.addEventListener(key.slice(2).toLowerCase(), props[key]);
  255.                     else if (key === "children") {
  256.                         node.append(...(Array.isArray(props[key]) ? props[key] : [].concat(props[key])));
  257.                     } else {
  258.                         node.setAttribute(key === "className" ? "class" : key, props[key]);
  259.                     }
  260.                 }
  261.  
  262.                 if (children.length) node.append(...children);
  263.  
  264.                 return node;
  265.             };
  266.  
  267.             class Tooltip {
  268.                 containerClassName = Utilities.className("PI-tooltip", ...["tooltip", "tooltipTop", "tooltipPrimary"].map(c => DiscordClasses.Tooltips?.[c]?.value));
  269.                 pointerClassName = DiscordClasses.Tooltips?.tooltipPointer?.value;
  270.                 contentClassName = DiscordClasses.Tooltips?.tooltipContent?.value;
  271.  
  272.                 constructor(target, {text, spacing}) {
  273.                     this.target = target;
  274.                     this.ref = null;
  275.                     this.text = text;
  276.                     this.spacing = spacing;
  277.                     this.tooltip = createElement("div", {
  278.                         className: this.containerClassName,
  279.                         style: "visibility: hidden;",
  280.                         children: [
  281.                             createElement("div", {className: this.pointerClassName, style: "left: calc(50% + 0px)"}),
  282.                             createElement("div", {className: this.contentClassName}, text)
  283.                         ]
  284.                     });
  285.  
  286.                     target.addEventListener("mouseenter", () => {
  287.                         this.show();    
  288.                     });
  289.  
  290.                     target.addEventListener("mouseleave", () => {
  291.                         this.hide();
  292.                     });
  293.  
  294.                     this.tooltip._unmount = DOM.onRemoved(target, () => this.hide());
  295.                 }
  296.  
  297.                 get container() {return document.querySelector(`.${LayerClassName} ~ .${LayerClassName}`);}
  298.  
  299.                 checkOffset(x, y) {
  300.                     if (y < 0) {
  301.                         y = 0;
  302.                     } else if (y > window.innerHeight) {
  303.                         y = window.innerHeight;
  304.                     }
  305.            
  306.                     if (x > window.innerWidth) {
  307.                         x = window.innerWidth;
  308.                     } else if (x < 0) {
  309.                         x = 0;
  310.                     }
  311.            
  312.                     return {x, y};
  313.                 }
  314.  
  315.                 show() {
  316.                     const tooltip = this.ref = this.tooltip.cloneNode(true);
  317.                     this.container.appendChild(tooltip);
  318.  
  319.                     const targetRect = this.target.getBoundingClientRect();
  320.                     const tooltipRect = tooltip.getBoundingClientRect();
  321.  
  322.                     let top = (targetRect.y - tooltipRect.height) - this.spacing;
  323.                     let left = targetRect.x + (targetRect.width / 2) - (tooltipRect.width / 2);    
  324.  
  325.                     const position = this.checkOffset(left, top);
  326.  
  327.                     tooltip.style = `top: ${position.y}px; left: ${position.x}px;`;
  328.                 }
  329.  
  330.                 hide() {
  331.                     this.ref?.remove();
  332.                 }
  333.             }
  334.  
  335.             class StatusIndicators {
  336.                 constructor(target, userId, type) {
  337.                     this.userId = userId;
  338.                     this.type = type;
  339.                     this.ref = null;
  340.                     this.target = target;
  341.                     this._destroyed = false;
  342.  
  343.                     target._patched = true;
  344.  
  345.                     this.container = createElement("div", {
  346.                         "data-id": userId,
  347.                         className: Utilities.className("PI-indicatorContainer", "PI-type_" + type),
  348.                     });
  349.  
  350.                     this._stopObserver = DOM.onRemoved(target, () => this.unmount());
  351.  
  352.                     StoreWatcher.onChange(this.handleChange);
  353.                 }
  354.  
  355.                 unmount() {
  356.                     this.ref?.remove();
  357.                     this._stopObserver?.();
  358.                     this._destroyed = true;
  359.                     StoreWatcher.offChange(this.handleChange);
  360.                     this.target._patched = false;
  361.                 }
  362.  
  363.                 mount() {
  364.                     if (this._destroyed) return false;
  365.  
  366.                     const res = this.render();
  367.                     if (!res) this.ref?.remove();
  368.                     else {
  369.                         if (this.ref) {
  370.                             this.ref.replaceWith(res);
  371.                         } else {
  372.                             this.target.appendChild(res);
  373.                         }
  374.                        
  375.                         this.ref = res;
  376.                     }
  377.                 }
  378.  
  379.                 handleChange = () => {
  380.                     if (this._destroyed) return false;
  381.  
  382.                     if (this.state && _.isEqual(this.state, this.getState())) return;
  383.  
  384.                     this.mount();
  385.                 }
  386.  
  387.                 getState() {
  388.                     const user = UserStore.getUser(this.userId);
  389.                     return {
  390.                         iconStates: Settings.get("icons", {}),
  391.                         shouldShow: (() => {
  392.                             const shownInArea = Settings.get("showIn" + this.type, true);
  393.                             const isBot = Settings.get("ignoreBots", true) && (user?.bot ?? false);
  394.        
  395.                             return shownInArea && !isBot;
  396.                         })(),
  397.                         clients: (() => {
  398.                             if (user?.id === UserStore.getCurrentUser()?.id) return SessionsStore.getSession() ? {
  399.                                 [SessionsStore.getSession().clientInfo.client]: isStreaming() ? "streaming" : SessionsStore.getSession().status
  400.                             } : {};
  401.          
  402.                             return UserStatusStore.getState().clientStatuses[user?.id] ?? {};
  403.                         })(),
  404.                         user
  405.                     };
  406.                 }
  407.  
  408.                 render() {
  409.                     const container = this.container.cloneNode(true);
  410.                     const state = this.state = this.getState();
  411.  
  412.                     if (!Object.keys(state.clients).length || !state.shouldShow) return null;
  413.  
  414.                     container._unmount = this.unmount.bind(this);
  415.  
  416.                     container.append(...Object.entries(state.clients)
  417.                         .filter(([key]) => (state.iconStates[key] ?? true) && Icons[key] != null)
  418.                         .map(([key, status]) => {
  419.                             const Icon = Icons[key];
  420.                             return Icon({
  421.                                 text: StringUtils.getStatusText(key, status),
  422.                                 style: `color: ${StatusColors[status]};`,
  423.                                 width: 18,
  424.                                 height: 18,
  425.                                 "data-status": status
  426.                             });
  427.                         })
  428.                     );
  429.  
  430.                     return container;
  431.                 }
  432.             }
  433.  
  434.             const createIcon = ((Icon, defaultProps) => props => {
  435.                 const element = Icon.cloneNode(true);
  436.  
  437.                 if (props.text) {
  438.                     new Tooltip(element, {
  439.                         text: props.text,
  440.                         spacing: 8
  441.                     });
  442.                 }
  443.  
  444.                 for (const prop in Object.assign({}, defaultProps, props)) {
  445.                     if (prop === "text") continue;
  446.                     element.setAttribute(prop, props[prop]);
  447.                 }
  448.  
  449.                 return element;
  450.             });
  451.  
  452.             const Icons = {
  453.                 mobile: createIcon(
  454.                     template(`<svg class="PI-icon_mobile" width="24" height="24" transform="scale(0.9)" viewBox="0 -2.5 32 44"><path fill="currentColor" d="M 2.882812 0.246094 C 1.941406 0.550781 0.519531 2.007812 0.230469 2.953125 C 0.0585938 3.542969 0 7.234375 0 17.652344 L 0 31.554688 L 0.5 32.558594 C 1.117188 33.769531 2.152344 34.5625 3.519531 34.847656 C 4.210938 35 7.078125 35.058594 12.597656 35 C 20.441406 34.941406 20.691406 34.925781 21.441406 34.527344 C 22.347656 34.054688 23.078125 33.3125 23.578125 32.386719 C 23.921875 31.761719 23.941406 30.964844 24 18.085938 C 24.039062 8.503906 24 4.167969 23.847656 3.464844 C 23.558594 2.121094 22.75 1.097656 21.519531 0.492188 L 20.5 0 L 12.039062 0.0195312 C 6.402344 0.0390625 3.328125 0.113281 2.882812 0.246094 Z M 20.382812 14.582031 L 20.382812 22.917969 L 3.652344 22.917969 L 3.652344 6.25 L 20.382812 6.25 Z M 13.789062 27.539062 C 14.5 28.296875 14.597656 29.035156 14.132812 29.925781 C 13.308594 31.496094 10.671875 31.421875 9.902344 29.8125 C 9.539062 29.054688 9.539062 28.730469 9.902344 28.011719 C 10.691406 26.535156 12.632812 26.308594 13.789062 27.539062 Z M 13.789062 27.539062 "></path></svg>`)
  455.                 ),
  456.                 web: createIcon(
  457.                     template(`<svg class="PI-icon_web" width="24" height="24" viewBox="0 -2.5 28 28"><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"></path></svg>`)
  458.                 ),
  459.                 desktop: createIcon(
  460.                     template(`<svg class="PI-icon_desktop" width="24" height="24" viewBox="0 -2.5 28 28"><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"></path></svg>`)
  461.                 ),
  462.                 embedded: createIcon(
  463.                     template(`<svg class="PI-icon_embedded" width="24" height="24" viewBox="0 -2.5 28 28"><path fill="currentColor" d="M5.79335761,5 L18.2066424,5 C19.7805584,5 21.0868816,6.21634264 21.1990185,7.78625885 L21.8575059,17.0050826 C21.9307825,18.0309548 21.1585512,18.9219909 20.132679,18.9952675 C20.088523,18.9984215 20.0442685,19 20,19 C18.8245863,19 17.8000084,18.2000338 17.5149287,17.059715 L17,15 L7,15 L6.48507125,17.059715 C6.19999155,18.2000338 5.1754137,19 4,19 C2.97151413,19 2.13776159,18.1662475 2.13776159,17.1377616 C2.13776159,17.0934931 2.1393401,17.0492386 2.1424941,17.0050826 L2.80098151,7.78625885 C2.91311838,6.21634264 4.21944161,5 5.79335761,5 Z M14.5,10 C15.3284271,10 16,9.32842712 16,8.5 C16,7.67157288 15.3284271,7 14.5,7 C13.6715729,7 13,7.67157288 13,8.5 C13,9.32842712 13.6715729,10 14.5,10 Z M18.5,13 C19.3284271,13 20,12.3284271 20,11.5 C20,10.6715729 19.3284271,10 18.5,10 C17.6715729,10 17,10.6715729 17,11.5 C17,12.3284271 17.6715729,13 18.5,13 Z M6,9 L4,9 L4,11 L6,11 L6,13 L8,13 L8,11 L10,11 L10,9 L8,9 L8,7 L6,7 L6,9 Z"></path></svg>`)
  464.                 )
  465.             };
  466.  
  467.             const ElementInjections = {
  468.                 [ChatHeader?.headerText ?? "unknown"]: elements => {
  469.                     for (const el of elements) {
  470.                         if (el.getElementsByClassName("PI-indicatorContainer").length || el._patched) continue;
  471.  
  472.                         const user = getReactProps(el.parentElement, e => e?.message)?.message?.author;
  473.  
  474.                         if (user) {
  475.                             new StatusIndicators(el, user.id, "Chat").mount();
  476.                         }
  477.                     }
  478.                 },
  479.                 ...Object.fromEntries([NameTag?.nameTag, NewUserName?.userTagWithNickname]
  480.                     .filter(Boolean)
  481.                     .map(className => [
  482.                         className,
  483.                         elements => {
  484.                             for (const el of elements) {
  485.                                 if (el.getElementsByClassName("PI-indicatorContainer").length || el._patched) continue;
  486.        
  487.                                 const user = getReactProps(el, e => e?.user)?.user;
  488.                                 if (user) {
  489.                                     new StatusIndicators(el, user.id, "Tags").mount();
  490.                                 }
  491.                             }
  492.                         }
  493.                     ])
  494.                 ),
  495.                 ...Object.fromEntries([MemberListItem?.nameAndDecorators, DirectMessage?.nameAndDecorators]
  496.                     .filter(Boolean)
  497.                     .map(className => [
  498.                         className,
  499.                         elements => {
  500.                             for (const el of elements) {
  501.                                 if (el.getElementsByClassName("PI-indicatorContainer").length || el._patched) continue;
  502.                                
  503.                                 const user = getReactProps(el, e => e?.user)?.user;
  504.  
  505.                                 if (user) {
  506.                                     new StatusIndicators(el, user.id, "MemberList").mount();
  507.                                 }
  508.                             }
  509.                         }
  510.                     ])
  511.                 )
  512.             };
  513.  
  514.             return class PlatformIndicators extends Plugin {
  515.                 getSettingsPanel() {
  516.                     const panel = this.buildSettingsPanel();
  517.  
  518.                     // Very dirty
  519.                     panel.addListener(() => {
  520.                         Settings._settings = {...this.settings};
  521.                         Settings.emitChange();
  522.                     });
  523.  
  524.                     return panel.getElement();
  525.                 }
  526.  
  527.                 css = /*css*/`
  528.                     .PI-tooltip {
  529.                         position: fixed;
  530.                     }
  531.  
  532.                     .PI-indicatorContainer {
  533.                         display: inline-flex;
  534.                         vertical-align: bottom;
  535.                         margin-bottom: 2px;
  536.                         margin-left: 5px;
  537.                     }
  538.  
  539.                     .PI-indicatorContainer svg {
  540.                         margin-left: -2px;
  541.                     }
  542.  
  543.                     .PI-indicatorContainer div:first-child svg {
  544.                         margin-left: 2px;
  545.                     }
  546.  
  547.                     .PI-container {
  548.                         display: flex;
  549.                     }
  550.  
  551.                     .PI-icon_mobile {
  552.                         position: relative;
  553.                         top: 1px;
  554.                     }
  555.  
  556.                     .PI-indicatorContainer.PI-type_Chat {
  557.                         margin-right: -6px;
  558.                         vertical-align: top;
  559.                     }
  560.  
  561.                     .${friendsRowClasses.userInfo} .PI-indicatorContainer > div {display: inline-flex;}
  562.  
  563.                     .${friendsRowClasses.userInfo} .${friendsRowClasses.discriminator} {
  564.                         display: none;
  565.                         visibility: visible;
  566.                     }
  567.  
  568.                     .${friendsRowClasses.hovered} .${friendsRowClasses.discriminator} {display: block;}
  569.                 `;
  570.  
  571.                 onStart() {
  572.                     PluginUtilities.addStyle(config.info.name, this.css);
  573.                     StoreWatcher._init();
  574.  
  575.                     for (const className in ElementInjections) {
  576.                         const elements = Array.from(document.body.getElementsByClassName(className));
  577.  
  578.                         if (elements.length) {
  579.                             ElementInjections[className](elements);
  580.                         }
  581.                     }
  582.                 }
  583.  
  584.                 observer({addedNodes}) {
  585.                     for (const added of addedNodes) {
  586.                         if (added.nodeType === Node.TEXT_NODE) continue;
  587.  
  588.                         for (const className in ElementInjections) {
  589.                             const elements = Array.from(added.getElementsByClassName(className));
  590.  
  591.                             if (elements.length) {
  592.                                 ElementInjections[className](elements);
  593.                             }
  594.                         }
  595.                     }
  596.                 }
  597.  
  598.                 onStop() {
  599.                     StoreWatcher._stop();
  600.                     StoreWatcher._listeners.clear();
  601.                     PluginUtilities.removeStyle(config.info.name);
  602.                     document.querySelectorAll(".PI-indicatorContainer").forEach(el => el._unmount?.());
  603.                     document.querySelectorAll(".PI-tooltip").forEach(n => (n?._unmount?.(), n.remove()));
  604.                 }
  605.             };
  606.         };
  607.         return plugin(Plugin, Api);
  608.         //@ts-ignore
  609.     })(global.ZeresPluginLibrary.buildPlugin(config));
  610. })();
  611. /*@end@*/
  612.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement