Sibbay Github Quick Reply

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

  1. // ==UserScript==
  2. // @name Sibbay Github Quick Reply
  3. // @namespace https://github.com/sibbay-ai/public
  4. // @version 0.13
  5. // @description 小白社区开发者实用工具,快速在issue中插入申请开发/变更deadline等操作
  6. // @author github.com/Yidadaa
  7. // @include https://github.com*
  8. // @run-at document-end
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // 由于github使用pjax加载页面,需要对github全站进行匹配,然后排除掉不需要运行脚本的页面
  17.  
  18. let days = GM_getValue('days') || 14
  19. let size = GM_getValue('size') || 0.1
  20.  
  21. // 工具函数
  22. const $$ = s => Array.from(document.querySelectorAll(s))
  23. const $ = s => document.querySelector(s)
  24.  
  25. function tree2node (root) {
  26. // 生成dom节点
  27. const node = document.createElement(root.type)
  28. // 映射标签属性
  29. root.attrs && Object.keys(root.attrs).forEach(key => { node.setAttribute(key, root.attrs[key]) })
  30. // 映射节点属性
  31. root.props && Object.keys(root.props).forEach(key => { node[key] = root.props[key] })
  32. // 生成子节点
  33. root.children && root.children.forEach(child => { node.appendChild(tree2node(child)) })
  34. return node
  35. }
  36.  
  37. // 检查是否已经标记过ddl
  38. const checkDDL = () => {
  39. return $$('.timeline-comment-group .edit-comment-hide').some(node => {
  40. return /申请开发\ deadline/.test(node.innerText)
  41. })
  42. }
  43.  
  44. // 生成n天后的时间
  45. const nDaysLater = (n) => {
  46. const date = new Date()
  47. const nDaysLaterTime = new Date(date.getTime() + n * 24 * 3600 * 1000)
  48. return `${nDaysLaterTime.getFullYear()}-${nDaysLaterTime.getMonth() + 1}-${nDaysLaterTime.getDate()}`
  49. }
  50.  
  51. // 生成模板
  52. const generateText = () => {
  53. const hasDDL = checkDDL()
  54. let text = `申请开发 deadline: ${nDaysLater(days)} size: ${size}`
  55. if (hasDDL) text = `变更 deadline: ${nDaysLater(days)}`
  56. return text
  57. }
  58.  
  59. // 更新文字
  60. const updateText = () => {
  61. $('#sibbay-text').innerText = generateText()
  62. $('#sibbay-time').innerText = ` - will finish in ${days} days`
  63. $('#sibbay-size').innerText = ` - ${size} size`
  64. }
  65.  
  66. // 确认提交文字
  67. const confirmText = () => {
  68. $('#new_comment_field').value = generateText()
  69. GM_setValue('days', days)
  70. GM_setValue('size', size)
  71. }
  72.  
  73. const buttonWithMenu = {
  74. type: "span",
  75. attrs: {
  76. class: "js-pages-source select-menu js-menu-container js-select-menu js-transitionable",
  77. style: "float: right;"
  78. },
  79. children: [
  80. {
  81. type: "button",
  82. attrs: {
  83. class: "btn mr-1 select-menu-button js-menu-target",
  84. type: 'button',
  85. 'aria-haspopup': true,
  86. 'aria-expanded': false
  87. },
  88. children: [{
  89. type: 'span',
  90. attrs: {
  91. class: 'js-select-button js-pages-source-btn-text',
  92. style: 'margin-right: 5px;'
  93. },
  94. props: {
  95. innerText: checkDDL() ? 'Change deadline' : 'Wanna develop'
  96. }
  97. }]
  98. },
  99. {
  100. type: 'div',
  101. attrs: {
  102. class: 'select-menu-modal-holder',
  103. style: 'margin-top: 35px;'
  104. },
  105. children: [{
  106. type: 'div',
  107. attrs: {
  108. class: 'select-menu-modal js-menu-content',
  109. 'aria-expanded': false
  110. },
  111. children: [{
  112. type: 'div',
  113. attrs: {
  114. class: 'select-menu-list js-navigation-container',
  115. role: 'menu'
  116. },
  117. children: [{
  118. type: 'div',
  119. attrs: {
  120. class: 'select-menu-header js-navigation-enable'
  121. },
  122. children: [{
  123. type: 'span',
  124. props: { innerText: 'Select a time' }
  125. }, {
  126. type: 'span',
  127. attrs: {
  128. id: 'sibbay-time'
  129. },
  130. props: {
  131. innerText: ` - will finish in ${days} days`
  132. }
  133. }]
  134. }, {
  135. type: 'div',
  136. attrs: {
  137. class: 'width-full',
  138. style: 'padding: 10px;'
  139. },
  140. children: [{
  141. type: 'input',
  142. attrs: {
  143. class: 'width-full',
  144. type: 'range',
  145. min: '1',
  146. max: '30',
  147. value: days,
  148. style: 'cursor: pointer'
  149. },
  150. props: {
  151. oninput: e => {
  152. days = e.target.value
  153. updateText()
  154. }
  155. }
  156. }]
  157. }, {
  158. type: 'div',
  159. attrs: {
  160. class: 'select-menu-header js-navigation-enable',
  161. style: `display: ${checkDDL() ? 'none' : 'block'}`
  162. },
  163. children: [{
  164. type: 'span',
  165. props: { innerText: 'Select a size' }
  166. }, {
  167. type: 'span',
  168. attrs: {
  169. id: 'sibbay-size'
  170. },
  171. props: {
  172. innerText: ` - ${size} size`
  173. }
  174. }]
  175. }, {
  176. type: 'div',
  177. attrs: {
  178. class: 'width-full',
  179. style: `padding: 10px; display: ${checkDDL() ? 'none' : 'block'}`
  180. },
  181. children: [{
  182. type: 'input',
  183. attrs: {
  184. class: 'width-full',
  185. type: 'range',
  186. min: '0.1',
  187. max: '3',
  188. step: '0.1',
  189. value: size,
  190. style: 'cursor: pointer'
  191. },
  192. props: {
  193. oninput: e => {
  194. size = e.target.value
  195. updateText()
  196. }
  197. }
  198. }]
  199. }, {
  200. type: 'div',
  201. attrs: {
  202. style: 'padding: 10px; border-top: 1px solid #eee; display: flex; align-items: center;'
  203. },
  204. children: [{
  205. type: 'div',
  206. attrs: {
  207. class: 'width-full',
  208. id: 'sibbay-text',
  209. style: 'font-weight: bold;'
  210. },
  211. props: {
  212. innerText: generateText()
  213. }
  214. }, {
  215. type: 'div',
  216. attrs: {
  217. class: 'btn btn-sm btn-primary js-menu-close'
  218. },
  219. props: {
  220. innerText: 'OK',
  221. onclick: confirmText
  222. }
  223. }]
  224. }]
  225. }]
  226. }]
  227. }
  228. ]
  229. }
  230.  
  231.  
  232. let applyBtn = tree2node(buttonWithMenu)
  233. const appendBtn = () => {
  234. if (!location.href.includes('sibbay-ai')) return
  235. const buttons = document.getElementById('partial-new-comment-form-actions')
  236. if (!buttons) {
  237. return
  238. }
  239. buttons.appendChild(applyBtn)
  240. }
  241.  
  242. document.addEventListener('pjax:complete', appendBtn)
  243. appendBtn()
  244. })();