import './quick-mention.css'; import React from 'dom-chef'; import {$, elementExists} from 'select-dom'; import {ReplyIcon} from '@primer/octicons-react'; import * as pageDetect from 'github-url-detection'; import * as textFieldEdit from 'text-field-edit'; import delegate, {DelegateEvent} from 'delegate-it'; import {wrap} from '../helpers/dom-utils.js'; import features from '../feature-manager.js'; import {getUsername, isArchivedRepoAsync} from '../github-helpers/index.js'; import observe from '../helpers/selector-observer.js'; function prefixUserMention(userMention: string): string { // The alt may or may not have it #4859 return '@' + userMention.replace('@', '').replace(/\[bot]$/, ''); } function mentionUser({delegateTarget: button}: DelegateEvent): void { const userMention = button.parentElement!.querySelector('img')!.alt; const newComment = $('textarea#new_comment_field')!; newComment.focus(); // If the new comment field has selected text, don’t replace it newComment.selectionStart = newComment.selectionEnd; // If the cursor is preceded by a space (or is at place 0), don't add a space before the mention const precedingCharacter = newComment.value.slice(newComment.selectionStart - 1, newComment.selectionStart); const spacer = /\s|^$/.test(precedingCharacter) ? '' : ' '; // The space after closes the autocomplete box and places the cursor where the user would start typing textFieldEdit.insert(newComment, `${spacer}${prefixUserMention(userMention)} `); } const debug = false; function add(avatar: HTMLElement): void { if (debug) { avatar.style.border = 'solid 5px black'; } const timelineItem = avatar.closest([ // Regular comments '.js-comment-container', // Reviews '.js-comment', ])!; if (debug) { timelineItem.style.border = 'solid 5px red'; } if ( // TODO: Rewrite with :has() // Exclude events that aren't tall enough, like hidden comments or reviews without comments !elementExists('.unminimized-comment, .js-comment-container', timelineItem) ) { return; } if (debug) { timelineItem.style.border = 'solid 5px green'; } // Wrap avatars next to review events so the inserted button doesn't break the layout #4844 if (avatar.classList.contains('TimelineItem-avatar')) { avatar.classList.remove('TimelineItem-avatar'); wrap(avatar,
); } const userMention = $('img', avatar)!.alt; avatar.classList.add('rgh-quick-mention'); avatar.after( , ); } async function init(signal: AbortSignal): Promise