GitHub Default Issues Filter

Replaces GitHub issue's default filter

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name               GitHub Default Issues Filter
// @name:zh-CN         GitHub Issues 默认过滤器
// @description        Replaces GitHub issue's default filter
// @description:zh-CN  替换 GitHub issues 的默认过滤器
// @version            0.1
// @author             guansss
// @namespace          https://github.com/guansss
// @source             https://github.com/guansss/userscripts
// @supportURL         https://github.com/guansss/userscripts/issues
// @match              *://github.com/*
// @run-at             document-start
// @grant              GM_info
// @license            MIT
// @noframes
// ==/UserScript==

function _script_main() {
    'use strict';

    const ready = new Promise((resolve) => {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => resolve);
        } else {
            resolve();
        }
    });

    function hookMethod(object, method, callback) {
        const original = object[method];

        object[method] = function () {
            callback.apply(this, arguments);
            return original.apply(this, arguments);
        };
    }

    let log;

    setLogger(console.log);

    function setLogger(logger) {
        log = logger.bind(console, `[${GM_info.script.name}]`);
    }

    /**
     * Periodically calls given function until it returns true.
     */
    function repeat(fn, interval = 200) {
        if (fn()) {
            return 0;
        }

        const id = setInterval(() => {
            try {
                fn() && clearInterval(id);
            } catch (e) {
                log(e);
                clearInterval(id);
            }
        }, interval);

        return id;
    }

    /**
     * Periodically calls given function until the return value is truthy.
     * @returns A CancelablePromise that resolves with the function's return value when truthy.
     */
    function until(fn, interval = 0) {
        let cancelled = false;

        const promise = new Promise((resolve, reject) =>
            repeat(() => {
                if (cancelled) {
                    return true;
                }

                try {
                    const result = fn();

                    if (result) {
                        resolve(result);

                        // break the repeat() loop
                        return true;
                    }
                } catch (e) {
                    reject(e);
                    return true;
                }
            }, interval)
        );

        promise.cancel = () => (cancelled = true);

        return promise;
    }

    const urlRegex = /.*issues\/?$/;
    const defaultFilters = 'is:issue is:open ';
    const targetFilters = 'is:issue';

    let currentTask;

    function cancelCurrentTask() {
        currentTask && currentTask.cancel();
    }

    function check(url) {
        if (urlRegex.test(url)) {
            cancelCurrentTask();

            currentTask = until(() => {
                const input = document.getElementById('js-issues-search');

                if (input && input.parentElement && input.parentElement.tagName === 'FORM') {
                    // when navigating from pull requests to issues, the input is persisted until navigation completes,
                    // so we should ensure the input value is as expected
                    if (input.value === defaultFilters) {
                        input.value = targetFilters;
                        input.parentElement.dispatchEvent(new SubmitEvent('submit', { bubbles: true }));

                        return true;
                    }

                    // cancel current task whenever the input is changed by user, usually this won't happen
                    // since the interval is very short, but just in case the user is Shining Finger
                    input.addEventListener('input', cancelCurrentTask, { once: true });
                }
            }, 100);
        }
    }

    const handleStateChange = (data, unused, url) => {
        if (url instanceof URL) {
            url = url.href;
        }

        if (url) {
            check(url);
        }
    };

    hookMethod(history, 'pushState', handleStateChange);
    hookMethod(history, 'replaceState', handleStateChange);

    ready.then(() => check(location.href));
}

_script_main();