import pizza from 'react-pizza' import React from 'react' import uniq from 'lodash.uniq' import isEq from 'lodash.isequal' import { EventEmitter } from 'events' import { Checkbox, Form, InputNumber, notification, Popconfirm, Popover, Tooltip } from 'antd' import { css } from '@emotion/css' import setIntervalCheck from 'interval-check' import domify from 'domify' import { isNotIssueReady, isNotReady, useToken } from '../shared/utils' import JiraApiBrowser from '../shared/jira-api-browser' import { UserAddOutlined, LoadingOutlined } from '@ant-design/icons' import UserSuggest from '../component/user-suggest' import useForceUpdate from '../shared/hooks/use-forceupdate' import { reloadDetailView } from '../shared/jira-helper' const getSubKeys = () => { const table = document.querySelector('#issuetable, #subtasks .ghx-container .aui') if (!table) { return [] } return uniq( [].slice.apply(table.querySelectorAll('.ghx-key, [data-issue-key]')).map((node) => { return node.getAttribute('data-issue-key') || node.textContent.trim() }) ) } const SubtasksAssigneeComponent: React.FC<{ disabled?: boolean; subtaskKeys: any[] }> = function ({ disabled, subtaskKeys }) { const [token] = useToken() const jiraApi = React.useMemo( () => new JiraApiBrowser({ password: token }), [token] ) const [loading, setLoading] = React.useState(false) const [user, setUser] = React.useState(undefined) React.useEffect(() => { jiraApi.queryIssue(JIRA.Issue.getIssueKey()).then((res: any) => { let user = null if (res.data.fields) { if (res.data.fields.assignee) { user = res.data.fields.assignee.name } } setUser(user || JIRA?.Users?.LoggedInUser?.userName()) }) }, [JIRA.Issue.getIssueKey()]) if (!token || isNotReady() || isNotIssueReady()) { return null } return ( } okButtonProps={{ loading, disabled }} onConfirm={async () => { const issueKey = JIRA.Issue.getIssueKey() const subKeys = subtaskKeys if (subKeys.length) { setLoading(true) await jiraApi.setAssignees( subKeys.map((issueIdOrKey) => ({ issueIdOrKey, assignee: { name: user || null } })) ) reloadDetailView() setLoading(false) notification.success({ message: '子任务更新经办人成功' }) } }} > {loading ? ( ) : ( )} ) } const handleClick = () => { subtasksAssigneeRender() } let globalDispose = null export default function subtasksAssigneeRender() { const backlogContainer = document.querySelector('#ghx-rabid') if (backlogContainer) { backlogContainer.removeEventListener('click', handleClick) backlogContainer.addEventListener('click', handleClick) } if (globalDispose) { globalDispose() } const dispose = setIntervalCheck( null, () => { const allSubtaskKeys = [] let selectedSubtaskKeys = [] const connector = new EventEmitter() const connector2 = new EventEmitter() const container = document.querySelector(`#view-subtasks_heading .ops`) || document.querySelector(`#subtasks_heading .ops`) const tbody = document.querySelector(`#subtasks table tbody`) || document.querySelector(`table#issuetable tbody`) if (container) { if (container.querySelector('.jira-extension-subtasks-assignee')) { return false } if (!getSubKeys().length) { return false } const elem = document.createElement('li') elem.classList.add('jira-extension-subtasks-assignee') container.prepend(elem) pizza(() => { const [subtaskKeys, setSubtaskKeys] = React.useState(selectedSubtaskKeys.slice()) React.useEffect(() => { const handle = (keys) => { setSubtaskKeys(keys) } connector.addListener('next', handle) return () => { connector.removeListener('next', handle) } }, []) return })(elem, {}) const td = domify(``) tbody.querySelectorAll('tr').forEach((tr) => { const issueKey = tr.getAttribute('data-issuekey') || tr.getAttribute('data-issue-key') allSubtaskKeys.push(issueKey) selectedSubtaskKeys.push(issueKey) const newTd = td.cloneNode(true) pizza(() => { const [update] = useForceUpdate() React.useEffect(() => { const handle = (checked) => { if (!checked) { selectedSubtaskKeys = [] } else { selectedSubtaskKeys = allSubtaskKeys.slice() } connector.emit('next', selectedSubtaskKeys.slice()) update() } connector2.addListener('next', handle) return () => { connector2.removeListener('next', handle) } }, [update]) return ( { if (evt.target.checked) { const i = selectedSubtaskKeys.indexOf(issueKey) i >= 0 && selectedSubtaskKeys.splice(i, 1) selectedSubtaskKeys.push(issueKey) } else { const i = selectedSubtaskKeys.indexOf(issueKey) i >= 0 && selectedSubtaskKeys.splice(i, 1) } connector.emit('next', selectedSubtaskKeys.slice()) update() }} /> ) })(newTd, {}) tr.prepend(newTd) }) const elem1 = document.createElement('li') elem1.classList.add('jira-extension-subtasks-assignee-checkout') container.prepend(elem1) pizza(() => { const [checked, setChecked] = React.useState(true) const [subtaskKeys, setSubtaskKeys] = React.useState(selectedSubtaskKeys.slice()) React.useEffect(() => { const handle = (keys) => { setSubtaskKeys(keys) setChecked(!!allSubtaskKeys.length && isEq(uniq(allSubtaskKeys).sort(), uniq(keys).sort())) } connector.addListener('next', handle) return () => { connector.removeListener('next', handle) } }, []) return ( { setChecked(e.target.checked) connector2.emit('next', e.target.checked) }} /> ) })(elem1, {}) connector.emit('next', selectedSubtaskKeys.slice()) } return false }, 500 ) globalDispose = dispose }