this repo has no description
0
fork

Configure Feed

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

Let's add speech

+93 -30
+1
src/components/icon.jsx
··· 104 104 cloud: () => import('@iconify-icons/mingcute/cloud-line'), 105 105 month: () => import('@iconify-icons/mingcute/calendar-month-line'), 106 106 media: () => import('@iconify-icons/mingcute/photo-album-line'), 107 + speak: () => import('@iconify-icons/mingcute/radar-line'), 107 108 }; 108 109 109 110 function Icon({
+11
src/components/media-alt-modal.jsx
··· 4 4 5 5 import getTranslateTargetLanguage from '../utils/get-translate-target-language'; 6 6 import localeMatch from '../utils/locale-match'; 7 + import { speak, supportsTTS } from '../utils/speech'; 7 8 import states from '../utils/states'; 8 9 9 10 import Icon from './icon'; ··· 51 52 <Icon icon="translate" /> 52 53 <span>Translate</span> 53 54 </MenuItem> 55 + {supportsTTS && ( 56 + <MenuItem 57 + onClick={() => { 58 + speak(alt, lang); 59 + }} 60 + > 61 + <Icon icon="speak" /> 62 + <span>Speak</span> 63 + </MenuItem> 64 + )} 54 65 </Menu2> 55 66 </div> 56 67 </header>
+66 -30
src/components/status.jsx
··· 63 63 import { isMediaCaptionLong } from './media'; 64 64 import MenuLink from './menu-link'; 65 65 import RelativeTime from './relative-time'; 66 + import { speak, supportsTTS } from '../utils/speech'; 66 67 import TranslationBlock from './translation-block'; 67 68 68 69 const SHOW_COMMENT_COUNT_LIMIT = 280; ··· 89 90 /iPad|iPhone|iPod/.test(navigator.userAgent); 90 91 91 92 const REACTIONS_LIMIT = 80; 93 + 94 + function getPollText(poll) { 95 + if (!poll?.options?.length) return ''; 96 + return `📊:\n${poll.options 97 + .map( 98 + (option) => 99 + `- ${option.title}${ 100 + option.votesCount >= 0 ? ` (${option.votesCount})` : '' 101 + }`, 102 + ) 103 + .join('\n')}`; 104 + } 105 + function getPostText(status) { 106 + const { spoilerText, content, poll } = status; 107 + return ( 108 + (spoilerText ? `${spoilerText}\n\n` : '') + 109 + getHTMLText(content) + 110 + getPollText(poll) 111 + ); 112 + } 92 113 93 114 function Status({ 94 115 statusID, ··· 782 803 </> 783 804 )} 784 805 {enableTranslate ? ( 785 - <MenuItem 786 - disabled={forceTranslate} 787 - onClick={() => { 788 - setForceTranslate(true); 789 - }} 790 - > 791 - <Icon icon="translate" /> 792 - <span>Translate</span> 793 - </MenuItem> 794 - ) : ( 795 - (!language || differentLanguage) && ( 796 - <MenuLink 797 - to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`} 806 + <div class={supportsTTS ? 'menu-horizontal' : ''}> 807 + <MenuItem 808 + disabled={forceTranslate} 809 + onClick={() => { 810 + setForceTranslate(true); 811 + }} 798 812 > 799 813 <Icon icon="translate" /> 800 814 <span>Translate</span> 801 - </MenuLink> 815 + </MenuItem> 816 + {supportsTTS && ( 817 + <MenuItem 818 + onClick={() => { 819 + const postText = getPostText(status); 820 + if (postText) { 821 + speak(postText, language); 822 + } 823 + }} 824 + > 825 + <Icon icon="speak" /> 826 + <span>Speak</span> 827 + </MenuItem> 828 + )} 829 + </div> 830 + ) : ( 831 + (!language || differentLanguage) && ( 832 + <div class={supportsTTS ? 'menu-horizontal' : ''}> 833 + <MenuLink 834 + to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`} 835 + > 836 + <Icon icon="translate" /> 837 + <span>Translate</span> 838 + </MenuLink> 839 + {supportsTTS && ( 840 + <MenuItem 841 + onClick={() => { 842 + const postText = getPostText(status); 843 + if (postText) { 844 + speak(postText, language); 845 + } 846 + }} 847 + > 848 + <Icon icon="speak" /> 849 + <span>Speak</span> 850 + </MenuItem> 851 + )} 852 + </div> 802 853 ) 803 854 )} 804 855 {((!isSizeLarge && sameInstance) || enableTranslate) && <MenuDivider />} ··· 1578 1629 forceTranslate={forceTranslate || inlineTranslate} 1579 1630 mini={!isSizeLarge && !withinContext} 1580 1631 sourceLanguage={language} 1581 - text={ 1582 - (spoilerText ? `${spoilerText}\n\n` : '') + 1583 - getHTMLText(content) + 1584 - (poll?.options?.length 1585 - ? `\n\nPoll:\n${poll.options 1586 - .map( 1587 - (option) => 1588 - `- ${option.title}${ 1589 - option.votesCount >= 0 1590 - ? ` (${option.votesCount})` 1591 - : '' 1592 - }`, 1593 - ) 1594 - .join('\n')}` 1595 - : '') 1596 - } 1632 + text={getPostText(status)} 1597 1633 /> 1598 1634 )} 1599 1635 {!spoilerText && sensitive && !!mediaAttachments.length && (
+15
src/utils/speech.js
··· 1 + export const supportsTTS = 'speechSynthesis' in window; 2 + 3 + export function speak(text, lang) { 4 + if (!supportsTTS) return; 5 + try { 6 + if (speechSynthesis.speaking) { 7 + speechSynthesis.cancel(); 8 + } 9 + const utterance = new SpeechSynthesisUtterance(text); 10 + if (lang) utterance.lang = lang; 11 + speechSynthesis.speak(utterance); 12 + } catch (e) { 13 + alert(e); 14 + } 15 + }