Sibbay Github Quick Reply

小白社区开发者实用工具,快速在issue中插入申请开发/变更deadline等操作

// ==UserScript==
// @name         Sibbay Github Quick Reply
// @namespace    https://github.com/sibbay-ai/public
// @version      0.13
// @description  小白社区开发者实用工具,快速在issue中插入申请开发/变更deadline等操作
// @author       github.com/Yidadaa
// @include      https://github.com*
// @run-at       document-end
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function() {
    'use strict';

    // 由于github使用pjax加载页面,需要对github全站进行匹配,然后排除掉不需要运行脚本的页面

    let days = GM_getValue('days') || 14
    let size = GM_getValue('size') || 0.1

    // 工具函数
    const $$ = s => Array.from(document.querySelectorAll(s))
    const $ = s => document.querySelector(s)

    function tree2node (root) {
        // 生成dom节点
        const node = document.createElement(root.type)
        // 映射标签属性
        root.attrs && Object.keys(root.attrs).forEach(key => { node.setAttribute(key, root.attrs[key]) })
        // 映射节点属性
        root.props && Object.keys(root.props).forEach(key => { node[key] = root.props[key] })
        // 生成子节点
        root.children && root.children.forEach(child => { node.appendChild(tree2node(child)) })
        return node
    }

    // 检查是否已经标记过ddl
    const checkDDL = () => {
        return $$('.timeline-comment-group .edit-comment-hide').some(node => {
            return /申请开发\ deadline/.test(node.innerText)
        })
    }

    // 生成n天后的时间
    const nDaysLater = (n) => {
        const date = new Date()
        const nDaysLaterTime = new Date(date.getTime() + n * 24 * 3600 * 1000)
        return `${nDaysLaterTime.getFullYear()}-${nDaysLaterTime.getMonth() + 1}-${nDaysLaterTime.getDate()}`
    }

    // 生成模板
    const generateText = () => {
        const hasDDL = checkDDL()
        let text = `申请开发 deadline: ${nDaysLater(days)} size: ${size}`
        if (hasDDL) text = `变更 deadline: ${nDaysLater(days)}`
        return text
    }

    // 更新文字
    const updateText = () => {
        $('#sibbay-text').innerText = generateText()
        $('#sibbay-time').innerText = ` - will finish in ${days} days`
        $('#sibbay-size').innerText = ` - ${size} size`
    }

    // 确认提交文字
    const confirmText = () => {
        $('#new_comment_field').value = generateText()
        GM_setValue('days', days)
        GM_setValue('size', size)
    }

    const buttonWithMenu = {
        type: "span",
        attrs: {
            class: "js-pages-source select-menu js-menu-container js-select-menu js-transitionable",
            style: "float: right;"
        },
        children: [
            {
                type: "button",
                attrs: {
                    class: "btn mr-1 select-menu-button js-menu-target",
                    type: 'button',
                    'aria-haspopup': true,
                    'aria-expanded': false
                },
                children: [{
                    type: 'span',
                    attrs: {
                        class: 'js-select-button js-pages-source-btn-text',
                        style: 'margin-right: 5px;'
                    },
                    props: {
                        innerText: checkDDL() ? 'Change deadline' : 'Wanna develop'
                    }
                }]
            },
            {
                type: 'div',
                attrs: {
                    class: 'select-menu-modal-holder',
                    style: 'margin-top: 35px;'
                },
                children: [{
                    type: 'div',
                    attrs: {
                        class: 'select-menu-modal js-menu-content',
                        'aria-expanded': false
                    },
                    children: [{
                        type: 'div',
                        attrs: {
                            class: 'select-menu-list js-navigation-container',
                            role: 'menu'
                        },
                        children: [{
                            type: 'div',
                            attrs: {
                                class: 'select-menu-header js-navigation-enable'
                            },
                            children: [{
                                type: 'span',
                                props: { innerText: 'Select a time' }
                            }, {
                                type: 'span',
                                attrs: {
                                    id: 'sibbay-time'
                                },
                                props: {
                                    innerText: ` - will finish in ${days} days`
                                }
                            }]
                        }, {
                            type: 'div',
                            attrs: {
                                class: 'width-full',
                                style: 'padding: 10px;'
                            },
                            children: [{
                                type: 'input',
                                attrs: {
                                    class: 'width-full',
                                    type: 'range',
                                    min: '1',
                                    max: '30',
                                    value: days,
                                    style: 'cursor: pointer'
                                },
                                props: {
                                    oninput: e => {
                                        days = e.target.value
                                        updateText()
                                    }
                                }
                            }]
                        }, {
                            type: 'div',
                            attrs: {
                                class: 'select-menu-header js-navigation-enable',
                                style: `display: ${checkDDL() ? 'none' : 'block'}`
                            },
                            children: [{
                                type: 'span',
                                props: { innerText: 'Select a size' }
                            }, {
                                type: 'span',
                                attrs: {
                                    id: 'sibbay-size'
                                },
                                props: {
                                    innerText: ` - ${size} size`
                                }
                            }]
                        }, {
                            type: 'div',
                            attrs: {
                                class: 'width-full',
                                style: `padding: 10px; display: ${checkDDL() ? 'none' : 'block'}`
                            },
                            children: [{
                                type: 'input',
                                attrs: {
                                    class: 'width-full',
                                    type: 'range',
                                    min: '0.1',
                                    max: '3',
                                    step: '0.1',
                                    value: size,
                                    style: 'cursor: pointer'
                                },
                                props: {
                                    oninput: e => {
                                        size = e.target.value
                                        updateText()
                                    }
                                }
                            }]
                        }, {
                            type: 'div',
                            attrs: {
                                style: 'padding: 10px; border-top: 1px solid #eee; display: flex; align-items: center;'
                            },
                            children: [{
                                type: 'div',
                                attrs: {
                                    class: 'width-full',
                                    id: 'sibbay-text',
                                    style: 'font-weight: bold;'
                                },
                                props: {
                                    innerText: generateText()
                                }
                            }, {
                                type: 'div',
                                attrs: {
                                    class: 'btn btn-sm btn-primary js-menu-close'
                                },
                                props: {
                                    innerText: 'OK',
                                    onclick: confirmText
                                }
                            }]
                        }]
                    }]
                }]
            }
        ]
    }


    let applyBtn = tree2node(buttonWithMenu)
    const appendBtn = () => {
        if (!location.href.includes('sibbay-ai')) return
        const buttons = document.getElementById('partial-new-comment-form-actions')
        if (!buttons) {
            return
        }
        buttons.appendChild(applyBtn)
    }

    document.addEventListener('pjax:complete', appendBtn)
    appendBtn()
})();