this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make custom emoji picker work for poll fields

+116 -102
+1
src/components/compose-poll.jsx
··· 42 42 lang={lang} 43 43 spellCheck="true" 44 44 dir="auto" 45 + data-allow-custom-emoji="true" 45 46 onInput={(e) => { 46 47 const { value } = e.target; 47 48 options[i] = value;
+73 -60
src/components/compose.jsx
··· 184 184 textareaRef.current?.focus(); 185 185 }, 300); 186 186 }; 187 + const insertTextAtCursor = ({ targetElement, text }) => { 188 + if (!targetElement) return; 189 + 190 + const { selectionStart, selectionEnd, value } = targetElement; 191 + let textBeforeInsert = value.slice(0, selectionStart); 192 + 193 + // Remove zero-width space from end of text 194 + textBeforeInsert = textBeforeInsert.replace(/\u200B$/, ''); 195 + 196 + const spaceBeforeInsert = textBeforeInsert 197 + ? /[\s\t\n\r]$/.test(textBeforeInsert) 198 + ? '' 199 + : ' ' 200 + : ''; 201 + 202 + const textAfterInsert = value.slice(selectionEnd); 203 + const spaceAfterInsert = /^[\s\t\n\r]/.test(textAfterInsert) ? '' : ' '; 204 + 205 + const newText = 206 + textBeforeInsert + 207 + spaceBeforeInsert + 208 + text + 209 + spaceAfterInsert + 210 + textAfterInsert; 211 + 212 + targetElement.value = newText; 213 + targetElement.selectionStart = targetElement.selectionEnd = 214 + selectionEnd + text.length + spaceAfterInsert.length; 215 + targetElement.focus(); 216 + targetElement.dispatchEvent(new Event('input')); 217 + }; 218 + 219 + const lastFocusedEmojiFieldRef = useRef(null); 220 + const composeContainerRef = useRef(null); 221 + useEffect(() => { 222 + const handleFocus = (e) => { 223 + const target = e.target; 224 + if (target.hasAttribute('data-allow-custom-emoji')) { 225 + lastFocusedEmojiFieldRef.current = target; 226 + } 227 + }; 228 + 229 + const composeContainer = composeContainerRef.current; 230 + if (composeContainer) { 231 + composeContainer.addEventListener('focusin', handleFocus); 232 + } 233 + 234 + return () => { 235 + if (composeContainer) { 236 + composeContainer.removeEventListener('focusin', handleFocus); 237 + } 238 + }; 239 + }, []); 187 240 188 241 useEffect(() => { 189 242 if (replyToStatus) { ··· 693 746 }; 694 747 695 748 return ( 696 - <div id="compose-container-outer"> 749 + <div id="compose-container-outer" ref={composeContainerRef}> 697 750 <div id="compose-container" class={standalone ? 'standalone' : ''}> 698 751 <div class="compose-top"> 699 752 {currentAccountInfo?.avatarStatic && ( ··· 1180 1233 </div> 1181 1234 <Textarea 1182 1235 ref={textareaRef} 1236 + data-allow-custom-emoji="true" 1183 1237 placeholder={ 1184 1238 replyToStatus 1185 1239 ? t`Post your reply` ··· 1208 1262 onTrigger={(action) => { 1209 1263 if (action?.name === 'custom-emojis') { 1210 1264 setShowEmoji2Picker({ 1265 + targetElement: lastFocusedEmojiFieldRef, 1211 1266 defaultSearchTerm: action?.defaultSearchTerm || null, 1212 1267 }); 1213 1268 } else if (action?.name === 'mention') { ··· 1372 1427 </MenuItem> 1373 1428 <MenuItem 1374 1429 onClick={() => { 1375 - setShowEmoji2Picker(true); 1430 + setShowEmoji2Picker({ 1431 + targetElement: lastFocusedEmojiFieldRef, 1432 + }); 1376 1433 }} 1377 1434 > 1378 1435 <Icon icon="emoji2" />{' '} ··· 1453 1510 class="toolbar-button" 1454 1511 disabled={uiState === 'loading'} 1455 1512 onClick={() => { 1456 - setShowEmoji2Picker(true); 1513 + setShowEmoji2Picker({ 1514 + targetElement: lastFocusedEmojiFieldRef, 1515 + }); 1457 1516 }} 1458 1517 > 1459 1518 <Icon icon="emoji2" alt={_(ADD_LABELS.customEmoji)} /> ··· 1588 1647 defaultSearchTerm={showMentionPicker?.defaultSearchTerm} 1589 1648 onSelect={(socialAddress) => { 1590 1649 const textarea = textareaRef.current; 1591 - if (!textarea) return; 1592 - const { selectionStart, selectionEnd } = textarea; 1593 - const text = textarea.value; 1594 - let textBeforeMention = text.slice(0, selectionStart); 1595 - // Remove zero-width space from end of text 1596 - textBeforeMention = textBeforeMention.replace(/\u200B$/, ''); 1597 - const spaceBeforeMention = textBeforeMention 1598 - ? /[\s\t\n\r]$/.test(textBeforeMention) 1599 - ? '' 1600 - : ' ' 1601 - : ''; 1602 - const textAfterMention = text.slice(selectionEnd); 1603 - const spaceAfterMention = /^[\s\t\n\r]/.test(textAfterMention) 1604 - ? '' 1605 - : ' '; 1606 - const newText = 1607 - textBeforeMention + 1608 - spaceBeforeMention + 1609 - '@' + 1610 - socialAddress + 1611 - spaceAfterMention + 1612 - textAfterMention; 1613 - textarea.value = newText; 1614 - textarea.selectionStart = textarea.selectionEnd = 1615 - selectionEnd + 1616 - 1 + 1617 - socialAddress.length + 1618 - spaceAfterMention.length; 1619 - textarea.focus(); 1620 - textarea.dispatchEvent(new Event('input')); 1650 + if (textarea) { 1651 + insertTextAtCursor({ 1652 + targetElement: textarea, 1653 + text: '@' + socialAddress, 1654 + }); 1655 + } 1621 1656 }} 1622 1657 /> 1623 1658 </Modal> ··· 1636 1671 }} 1637 1672 defaultSearchTerm={showEmoji2Picker?.defaultSearchTerm} 1638 1673 onSelect={(emojiShortcode) => { 1639 - const textarea = textareaRef.current; 1640 - if (!textarea) return; 1641 - const { selectionStart, selectionEnd } = textarea; 1642 - const text = textarea.value; 1643 - let textBeforeEmoji = text.slice(0, selectionStart); 1644 - // Remove zero-width space from end of text 1645 - textBeforeEmoji = textBeforeEmoji.replace(/\u200B$/, ''); 1646 - const spaceBeforeEmoji = textBeforeEmoji 1647 - ? /[\s\t\n\r]$/.test(textBeforeEmoji) 1648 - ? '' 1649 - : ' ' 1650 - : ''; 1651 - const textAfterEmoji = text.slice(selectionEnd); 1652 - const spaceAfterEmoji = /^[\s\t\n\r]/.test(textAfterEmoji) 1653 - ? '' 1654 - : ' '; 1655 - const newText = 1656 - textBeforeEmoji + 1657 - spaceBeforeEmoji + 1658 - emojiShortcode + 1659 - spaceAfterEmoji + 1660 - textAfterEmoji; 1661 - textarea.value = newText; 1662 - textarea.selectionStart = textarea.selectionEnd = 1663 - selectionEnd + emojiShortcode.length + spaceAfterEmoji.length; 1664 - textarea.focus(); 1665 - textarea.dispatchEvent(new Event('input')); 1674 + const targetElement = 1675 + showEmoji2Picker?.targetElement?.current || textareaRef.current; 1676 + if (targetElement) { 1677 + insertTextAtCursor({ targetElement, text: emojiShortcode }); 1678 + } 1666 1679 }} 1667 1680 /> 1668 1681 </Modal>
+42 -42
src/locales/en.po
··· 248 248 249 249 #: src/components/account-sheet.jsx:38 250 250 #: src/components/add-remove-lists-sheet.jsx:45 251 - #: src/components/compose.jsx:779 251 + #: src/components/compose.jsx:832 252 252 #: src/components/custom-emojis-modal.jsx:234 253 253 #: src/components/drafts.jsx:57 254 254 #: src/components/edit-profile-sheet.jsx:87 ··· 358 358 msgid "Choice {0}" 359 359 msgstr "Choice {0}" 360 360 361 - #: src/components/compose-poll.jsx:60 361 + #: src/components/compose-poll.jsx:61 362 362 #: src/components/media-attachment.jsx:300 363 363 #: src/components/shortcuts-settings.jsx:726 364 364 #: src/pages/catchup.jsx:1081 ··· 366 366 msgid "Remove" 367 367 msgstr "" 368 368 369 - #: src/components/compose-poll.jsx:88 369 + #: src/components/compose-poll.jsx:89 370 370 msgid "Multiple choices" 371 371 msgstr "" 372 372 373 - #: src/components/compose-poll.jsx:91 373 + #: src/components/compose-poll.jsx:92 374 374 msgid "Duration" 375 375 msgstr "" 376 376 377 - #: src/components/compose-poll.jsx:122 377 + #: src/components/compose-poll.jsx:123 378 378 msgid "Remove poll" 379 379 msgstr "" 380 380 ··· 408 408 msgid "Schedule post" 409 409 msgstr "Schedule post" 410 410 411 - #: src/components/compose.jsx:304 411 + #: src/components/compose.jsx:357 412 412 msgid "You have unsaved changes. Discard this post?" 413 413 msgstr "You have unsaved changes. Discard this post?" 414 414 415 415 #. placeholder {0}: unsupportedFiles.length 416 416 #. placeholder {1}: unsupportedFiles[0].name 417 417 #. placeholder {2}: lf.format( unsupportedFiles.map((f) => f.name), ) 418 - #: src/components/compose.jsx:542 418 + #: src/components/compose.jsx:595 419 419 msgid "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}" 420 420 msgstr "{0, plural, one {File {1} is not supported.} other {Files {2} are not supported.}}" 421 421 422 - #: src/components/compose.jsx:552 423 - #: src/components/compose.jsx:570 424 - #: src/components/compose.jsx:1682 422 + #: src/components/compose.jsx:605 423 + #: src/components/compose.jsx:623 424 + #: src/components/compose.jsx:1695 425 425 #: src/components/file-picker-input.jsx:38 426 426 msgid "{maxMediaAttachments, plural, one {You can only attach up to 1 file.} other {You can only attach up to # files.}}" 427 427 msgstr "" 428 428 429 - #: src/components/compose.jsx:760 429 + #: src/components/compose.jsx:813 430 430 msgid "Pop out" 431 431 msgstr "Pop out" 432 432 433 - #: src/components/compose.jsx:767 433 + #: src/components/compose.jsx:820 434 434 msgid "Minimize" 435 435 msgstr "Minimize" 436 436 437 - #: src/components/compose.jsx:803 437 + #: src/components/compose.jsx:856 438 438 msgid "Looks like you closed the parent window." 439 439 msgstr "Looks like you closed the parent window." 440 440 441 - #: src/components/compose.jsx:810 441 + #: src/components/compose.jsx:863 442 442 msgid "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later." 443 443 msgstr "Looks like you already have a compose field open in the parent window and currently publishing. Please wait for it to be done and try again later." 444 444 445 - #: src/components/compose.jsx:815 445 + #: src/components/compose.jsx:868 446 446 msgid "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?" 447 447 msgstr "Looks like you already have a compose field open in the parent window. Popping in this window will discard the changes you made in the parent window. Continue?" 448 448 449 - #: src/components/compose.jsx:858 449 + #: src/components/compose.jsx:911 450 450 msgid "Pop in" 451 451 msgstr "Pop in" 452 452 453 453 #. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username 454 454 #. placeholder {1}: rtf.format(-replyToStatusMonthsAgo, 'month') 455 - #: src/components/compose.jsx:868 455 + #: src/components/compose.jsx:921 456 456 msgid "Replying to @{0}’s post (<0>{1}</0>)" 457 457 msgstr "" 458 458 459 459 #. placeholder {0}: replyToStatus.account.acct || replyToStatus.account.username 460 - #: src/components/compose.jsx:878 460 + #: src/components/compose.jsx:931 461 461 msgid "Replying to @{0}’s post" 462 462 msgstr "" 463 463 464 - #: src/components/compose.jsx:891 464 + #: src/components/compose.jsx:944 465 465 msgid "Editing source post" 466 466 msgstr "" 467 467 468 - #: src/components/compose.jsx:944 468 + #: src/components/compose.jsx:997 469 469 msgid "Poll must have at least 2 options" 470 470 msgstr "Poll must have at least 2 options" 471 471 472 - #: src/components/compose.jsx:948 472 + #: src/components/compose.jsx:1001 473 473 msgid "Some poll choices are empty" 474 474 msgstr "Some poll choices are empty" 475 475 476 - #: src/components/compose.jsx:961 476 + #: src/components/compose.jsx:1014 477 477 msgid "Some media have no descriptions. Continue?" 478 478 msgstr "Some media have no descriptions. Continue?" 479 479 480 - #: src/components/compose.jsx:1013 480 + #: src/components/compose.jsx:1066 481 481 msgid "Attachment #{i} failed" 482 482 msgstr "Attachment #{i} failed" 483 483 484 - #: src/components/compose.jsx:1109 484 + #: src/components/compose.jsx:1162 485 485 #: src/components/status.jsx:2103 486 486 #: src/components/timeline.jsx:1015 487 487 msgid "Content warning" 488 488 msgstr "" 489 489 490 - #: src/components/compose.jsx:1125 490 + #: src/components/compose.jsx:1178 491 491 msgid "Content warning or sensitive media" 492 492 msgstr "Content warning or sensitive media" 493 493 494 - #: src/components/compose.jsx:1161 494 + #: src/components/compose.jsx:1214 495 495 #: src/components/status.jsx:87 496 496 #: src/pages/settings.jsx:318 497 497 msgid "Public" 498 498 msgstr "" 499 499 500 - #: src/components/compose.jsx:1166 500 + #: src/components/compose.jsx:1219 501 501 #: src/components/nav-menu.jsx:349 502 502 #: src/components/shortcuts-settings.jsx:165 503 503 #: src/components/status.jsx:88 504 504 msgid "Local" 505 505 msgstr "" 506 506 507 - #: src/components/compose.jsx:1170 507 + #: src/components/compose.jsx:1223 508 508 #: src/components/status.jsx:89 509 509 #: src/pages/settings.jsx:321 510 510 msgid "Unlisted" 511 511 msgstr "" 512 512 513 - #: src/components/compose.jsx:1173 513 + #: src/components/compose.jsx:1226 514 514 #: src/components/status.jsx:90 515 515 #: src/pages/settings.jsx:324 516 516 msgid "Followers only" 517 517 msgstr "" 518 518 519 - #: src/components/compose.jsx:1176 519 + #: src/components/compose.jsx:1229 520 520 #: src/components/status.jsx:91 521 521 #: src/components/status.jsx:1983 522 522 msgid "Private mention" 523 523 msgstr "" 524 524 525 - #: src/components/compose.jsx:1185 525 + #: src/components/compose.jsx:1239 526 526 msgid "Post your reply" 527 527 msgstr "Post your reply" 528 528 529 - #: src/components/compose.jsx:1187 529 + #: src/components/compose.jsx:1241 530 530 msgid "Edit your post" 531 531 msgstr "Edit your post" 532 532 533 - #: src/components/compose.jsx:1188 533 + #: src/components/compose.jsx:1242 534 534 msgid "What are you doing?" 535 535 msgstr "What are you doing?" 536 536 537 - #: src/components/compose.jsx:1267 537 + #: src/components/compose.jsx:1322 538 538 msgid "Mark media as sensitive" 539 539 msgstr "" 540 540 541 - #: src/components/compose.jsx:1304 541 + #: src/components/compose.jsx:1359 542 542 msgid "Posting on <0/>" 543 543 msgstr "Posting on <0/>" 544 544 545 - #: src/components/compose.jsx:1335 545 + #: src/components/compose.jsx:1390 546 546 #: src/components/mention-modal.jsx:220 547 547 #: src/components/shortcuts-settings.jsx:715 548 548 #: src/pages/list.jsx:388 549 549 msgid "Add" 550 550 msgstr "" 551 551 552 - #: src/components/compose.jsx:1563 552 + #: src/components/compose.jsx:1622 553 553 msgid "Schedule" 554 554 msgstr "Schedule" 555 555 556 - #: src/components/compose.jsx:1565 556 + #: src/components/compose.jsx:1624 557 557 #: src/components/keyboard-shortcuts-help.jsx:155 558 558 #: src/components/status.jsx:965 559 559 #: src/components/status.jsx:1751 ··· 562 562 msgid "Reply" 563 563 msgstr "" 564 564 565 - #: src/components/compose.jsx:1567 565 + #: src/components/compose.jsx:1626 566 566 msgid "Update" 567 567 msgstr "Update" 568 568 569 - #: src/components/compose.jsx:1568 569 + #: src/components/compose.jsx:1627 570 570 msgctxt "Submit button in composer" 571 571 msgid "Post" 572 572 msgstr "Post" 573 573 574 - #: src/components/compose.jsx:1694 574 + #: src/components/compose.jsx:1707 575 575 msgid "Downloading GIF…" 576 576 msgstr "Downloading GIF…" 577 577 578 - #: src/components/compose.jsx:1722 578 + #: src/components/compose.jsx:1735 579 579 msgid "Failed to download GIF" 580 580 msgstr "Failed to download GIF" 581 581