⏲️ Better GitHub status time picker

Freely pick when your GitHub status clears!

What does this solve?

GitHub only provides a few options to automatically clear one’s status in the future:

Default GH status clear screenshot

This can be annoying if for example, one wants to set a “busy” status for a couple weeks in a row, or even until a later day in the week.

The tool provided below replaces the limited options with a date and a time picker, which provide complete flexibility in how to set the date and time when that GitHub status will be cleared.

Better GH status clear screenshot

Going on a long weekend, and want to be marked away until your return on Tuesday morning, 9am? You can do so now!

How to use?

There are two ways to use this tool:

  1. A userscript.
  2. A bookmarklet.

Userscript

What is a userscript?

According to Wikipedia’s page on Userscript:

A userscript (or user script) is a program, usually written in JavaScript, for modifying web pages to augment browsing.

You can run userscripts in your browser using a userscript manager, such as Violentmonkey.

Install

Add the userscript to your userscript manager. (For example, Violentmonkey will automatically come up when you click the link.)

Better GH status clear (userscript)

Use

Once installed and if enabled, the userscript will automatically fire up and change GitHub’s status modal when it pops up!


Bookmarklet

What is a bookmarklet?

According to Wikipedia’s page on Bookmarklet:

A bookmarklet is a bookmark stored in a web browser that contains JavaScript commands that add new features to the browser.

Install

You can install the bookmarklet by adding it to your browser’s bookmarks. The simplest way to do so is to drag’n’drop the button below to your bookmarks bar.

Better GH status clear (bookmarklet)

⬆️ Drag and drop the button above to your bookmarks!

Use

After opening the “Set status” modal window on GitHub, click on the bookmarklet in your browser to replace the date/time picker.

You can now select freely the date and time at which to clear your GitHub status!

Video

Source code

You can check the bookmarklet’s uncompressed source code below:


(() => {

class BetterGitHubStatusClear {
  static DEBUG_MODE = false;

  static get debug() {
    if (this.DEBUG_MODE) {
      return console.log.bind(window.console, "[BGHSC]");
    } else {
      return function() {};
    };
  };

  static run() {
    this.debug("Triggered.");

    const select = this.getOriginalClearStatusSelect();

    if (!select) {
      this.debug('Could not find the "clear status" select on the page. Aborting...');
      return;
    };

    if (select.classList.contains("bghsc")) {
      this.debug('Job already done. Aborting...');
      return;
    };

    select.classList.add("bghsc");
    if (!this.DEBUG_MODE) {
      select.parentElement.hidden = true;
    } else {
      this.debug('Not hiding the select\'s parent since DEBUG_MODE is on.');
    };

    var previousDateString = "";
    var previousTimeString = "";

    var previousDate = null;
    if (select.value != " ") {
      previousDate = new Date(select.value);
      previousDate = new Date(
        previousDate.getTime() - previousDate.getTimezoneOffset() * 60 * 1000
      );
      [previousDateString, previousTimeString] = previousDate.toISOString().split("T", 2);
      previousTimeString = previousTimeString.split(":", 3).slice(0, 2).join(":");
    };
    this.debug("Current clear date/time: " + previousDate);

    const html = `\
      <div>
        <input type="date" class="form-control" name="clear-date" value="${previousDateString}">
        <input type="time" class="form-control" name="clear-time" value="${previousTimeString}">
      </div>`;

    select.parentElement.parentElement.insertAdjacentHTML("beforeend", html);

    const that = this;
    const updateSelect = function(e) {
      that.debug("Updating select with new date...");
      const container = select.parentElement.parentElement;
      const dateString = container.querySelector("[name='clear-date']").value;
      const timeString = container.querySelector("[name='clear-time']").value;

      select.childNodes.forEach((option) => {
        if (option?.text?.startsWith("Custom")) {
          option.remove();
        };
      });

      if (dateString == "") {
        select.value = " ";
        that.debug("Erased clear date.");
      } else {
        const dateTime = new Date(`${dateString} ${timeString}`).toISOString();
        select.options.add(new Option(`Custom (${dateTime})`, dateTime, true, true));
        select.value = dateTime;
        that.debug(`Set clear date to ${dateTime}.`);
      };
    };

    select.parentElement.parentElement.querySelector("[name='clear-date']").addEventListener("input", updateSelect);
    select.parentElement.parentElement.querySelector("[name='clear-time']").addEventListener("input", updateSelect);

    this.debug("Finished.");
  };

  static ensureContext() {
    if (document.location.host !== "github.com") {
      alert("This bookmarklet only works on GitHub.");
      return false;
    };

    if (!this.getOriginalClearStatusSelect()) {
      alert("Please open the 'Set status' modal first.");
      return false;
    };

    return true;
  };

  static getOriginalClearStatusSelect() {
    const xpath = "//div[contains(., 'Edit status')]//select[@id='expires_at']";
    return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
  };
}


if (!BetterGitHubStatusClear.ensureContext()) return;
BetterGitHubStatusClear.run();

})();