Skip Navigation

kbin-megamod: proposal and proof of concept for integrated collection of kbin scripts with toggle menu

github.com GitHub - aclist/kbin-megamod: Integrated collection of kbin scripts with toggles

Integrated collection of kbin scripts with toggles - GitHub - aclist/kbin-megamod: Integrated collection of kbin scripts with toggles

Recently I had been thinking about using localstorage to elaborate a settings menu that could be used to toggle various scripts that persist browser sessions and would make the process of script management less fiddly and prone to collisions for end-users.

Then @Perry made a subscriptions panel using precisely this sort of logic (a modal leveraging localstorage), so I had a turnkey logic ready to go, and gratefully made use of his modal implementation. Thank you!

kbin-megamod exposes a modal that covers the screen and could be used to fit a large amount of checkboxes (toggles) for different scripts, or any fancy menu-like styling. The idea is that by integrating scripts into a suite, users do not have to manage numerous disparate scripts, updates, and the potential for collisions between them, because they would be tested and integrated a priori into the suite and served from a unified endpoint.

An icon at the top right of the navbar to trigger the modal

The "Megamod Settings" panel exposes a series of toggles that trigger different scripts. This can cover the screen and could fit a large amount of scripts. Tabs or columns could be added as well.

The masthead of the script defines a series of includes (@require) that call a list of scripts sourced from the /mods directory of the same repository. For maintanability purposes, each "feature" integrated into the megamod should be an atomic script sourced here. The calling script itself only manages the menu and storage of settings. For security reasons, scripts should be peer reviewed and audited by contributors and served from a single location, reducing the likelihood of a remote script being changed to execute malicious code, or simply to introduce breaking changes. When the megamod is loaded, @require scripts are fetched and saved in local cache. (Note: when testing, if you wish to update the local cache, bump the version number of the megamod script to force a redownload.)

The business logic follows:

Three arrays and one object are declared and initialized. The first array contains a list of human-readable "labels" for each option in the menu:

/*human readable mod label*/
     const mmLabels = [
            "Add mail icon",
            "Add subs to navbar",
            "Label OP",
            "Profile dropdown",
        ];

The second array contains a human-readable list of descriptions for what each feature does.

    /*human readable mod desc*/
    const mmDescs = [
            "Add mail link to usernames if on kbin.social",
            "Add magazine subscriptions to navbar",
            "Add 'OP' label to thread author",
            "Convert profile page links to dropdown",
            ];

The third array contains the identifiers for the functions, and is used when populating DOM elements with a unique classname. These must be unique.

    /*function identifier, can be same as function name*/
    const mmFuncs = [
        "addMail",
        "initMags",
        "labelOp",
        "dropdownEntry"
        ];

Finally, an object maps these classnames to the actual function entrypoint in the cached script. This is merely used as a workaround for interpolating the classname variables returned from the event listener in order to call the cached function, as explained in the section after this. The entry point name need not be the same as the function identifier (classname) above, but must be unique to prevent collisions. For simplicity, I used entry points with the same name, but they could be anything, such as myEntryPoint.

The entry point logic in the target script must parse an incoming boolean and either enable or disable the logic accordingly. In the case of disabling, this means the script must tear down or hide any DOM elements previously created.

     /*object used for interpolation of function names*/
    /*key MUST be same as mmFuncs array*/
    /*value MUST be literal entry point in the target script, will be passed boolean*/
    /*literal func name need not be identical to key*/
      const funcObj = {
       addMail: addMail,
       initMags: initMags,
       labelOp: labelOp,
       dropdownEntry: dropdownEntry
       };

The toggle logic. The method argument is the name from the mmFuncs array that is returned when the event listener triggers on a checkbox. This is then used as a key in the funcObj and interpolated to the value (the actual script entry point) and a boolean argument is passed.

function applySettings(method) {
        const settings = getSettings();
        if (settings[method] == true) {
            funcObj[method](true);
        } else {
             funcObj[method](false);
        }
    }

The toggle logic in the destination script's entry point may look something like this:

function initMags(toggle){
    if (toggle === false) {
        $('.subs-nav').remove();
    } else {
        createMags();
    }
}

I believe this could be a way forward for curating a collection of scripts, or to use the language from the repository, a "megamod" collection of "mods." It should give end-users a more straightforward means of configuring their experience without having to collect and install disparate scripts, which may be difficult for some. (Of course, power users can continue to pick and choose from standalone scripts if they wish.)

Integrating a new feature is as simple as adding four lines to megamod.user.js

  • The feature label in the first array (mmLabels)
  • The feature description in the second array (mmDescs)
  • The classname in the third array (mmFuncs)
  • The entry point mapping in the final object (funcObj)

That means you only need to write two human-readable sentence fragments and two names, add the script to the /mods directory, and the script will be integrated in the modal. All of the business logic of the called feature is then handled by the cached script. Ensure that the script handles setup and teardown correctly when it is toggled and that the entry points are defined correctly. Also, don't forget to add jQuery to the @requires if necessary and any @grants or other remote content to the masthead of the megamod.user.js so that they are passed through.

Going forward, we should look into externalizing this into a manifest and preparing the arrays/objects programmatically, rather than inline.

I would like to ask for your contributions and suggestions. Below I am including callouts to users I saw had posted scripts here recently (from the first few pages). If you wish to integrate your script, please make a PR. Sorry if I missed anyone. The main area that will require some testing will be ensuring that the scripts "play nicely" with each other and that there is no reduplication of efforts or collisions. I believe most scripts are being MIT licensed, so they could be integrated anyway with proper attribution, but obviously having the script authors integrate their own code would be preferable.

The main thing you would have to refactor is add some teardown logic to your script so that when toggled to the off state, it reverts all of its changes and restores the elements to their prior state.

@raltsm4k, @minnieo, @blobcat, @artillect, @0rito, @SirPsychoMantis, @CodingAndCoffee

Postscript: as for the megamod script itself, I have not thoroughly cleaned everything yet. I have not fully integrated the "add dropdown" functionality from my other script, but merely added it as the last checkbox for testing purposes, so the cosmetic teardown is not working correctly when disabling it. Otherwise, the other toggles are working correctly and act as a minimum viable prototype. You can test enabling/disabling these features and should see the effects live.

Lastly, in case you are wondering, "won't these features be added to kbin upstream eventually?" If you are already a script author, I think you know the answer to this, but to clarify for other interested parties: there will always be a need for third-party modifications on any site, no matter how mature, as users may have wildly different needs. Changing things on the client-side is non-invasive and can be prototyped easily, as well as takes the strain on upstream developers off of UI concerns and lets them focus on more important issues. Finally, client-side scripts could also serve as a reference for designs used in production code, so these efforts are not wasted.

19

You're viewing a single thread.

19 comments
  • I still think the proper place for userscript settings is in the sidebar with the built in settings as is done by the Kbin enhancement script - we already have the site preferences split into two places (sidebar and profile settings page), adding a third one to hunt through feels unnecessarily complicated.
    At the very least, the icon/link to open them should live there.

    • I see, thank you for the criticism. I hadn't used that one before. Don't you think this unnecessarily buries the link? What if the user has disabled sidebars altogether or this settings menu changes upstream? Wouldn't it be better to decouple the link from this area? In any event, this is more about a framework for integrally managing mods; the physical location of widgets could always be changed.

      • You can't remove the sidebar normally, so it would have to be a custom userscript, and doing that would also remove the Magazine information box, subscribe and block buttons, list of moderators and thread info. Maybe a script that allows it to be collapsed when not needed could be useful but I don't see anyone just ripping it out completely.

        But mainly thing is about keeping relevant settings grouped together. Lets say you add "Show thumbnails on left" option, it won't do anything unless the sidebar Threads -> Show Thumbnails is turned on first, so the logical place for that custom option would be in that same place. Same for any theme stuff, or anything to do with the top bar - the rest of the options are already in the sidebar.
        So maybe the correct answer is actually more fragmentation where the framework would allow adding the option either to the new floating window if it's something major with a bunch of customization, or have it embedded into the already existing sidebar settings if it's better suited to be there? I dunno.

        • I'm of two minds about it. On the one hand, the options may be semantically related, like you said. On the other hand, intermingling "official" options with third-party options somewhat obfuscates which ones are hacks/experimental and which ones are vanilla, so intentionally separating them cordons them off from each other.

          My other thought was that the sidebar settings are more likely to be the "set it and forget it" top-level type, while a debug modal might include more frequently-used tweaks that the user reaches for on a regular basis. (Not saying the ones added into the existing POC are such, just thinking ahead. Such as filters and the like.)

          Another thing is that (in theory), third-party options could supersede sidebar options, so you don't necessarily need to flip a toggle in the sidebar if a third-party script enforces the same check (e.g. user wants thumbnails, we better enable thumbnails first regardless of what the sidebar says). This may be trending towards bad design, however; depends on what we consider the responsibility of a third-party script to be (brute force or not).

          I tend to agree with you that in cases like these, the ¿Por qué no los dos? school of thought works well. You could simply expose a link to the widget in both the navbar and sidebar or let the user set where they want it. I think it might be a bit messy to have all those options inline in the sidebar (might get rather long), but maybe an icon in the sidebar opening a sub-menu or opening this modal? There are a million ways to implement this, could be nested tabs or whatever. My gut instinct tells me the settings sidebar is going to get its own dedicated page in the future, but I may be wrong.

          • I'm just one user, but for what it's worth, I would rather have my "mod" options separate from the sites's settings, it makes it clear which ones are "official" and which ones are on my end. RES did it this way and I think it worked well. I'm not saying RES is any sort of model to follow, but it's been developed for a long time so there is precedent that others had the same idea.

        • And here I am putting together a simple script to allow collapsing of those pesky sidebars, lol! However, doesn't RES also handle settings in a similar was as is here? I think they open a new page entirely now, but it's still a dedicated button, no? At the very least it would be familiar to users of RES.

19 comments