mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-14 12:18:30 +03:00
Let's add speech
This commit is contained in:
parent
33b55c937b
commit
92d6fe7ebe
4 changed files with 93 additions and 30 deletions
|
@ -104,6 +104,7 @@ export const ICONS = {
|
|||
cloud: () => import('@iconify-icons/mingcute/cloud-line'),
|
||||
month: () => import('@iconify-icons/mingcute/calendar-month-line'),
|
||||
media: () => import('@iconify-icons/mingcute/photo-album-line'),
|
||||
speak: () => import('@iconify-icons/mingcute/radar-line'),
|
||||
};
|
||||
|
||||
function Icon({
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useSnapshot } from 'valtio';
|
|||
|
||||
import getTranslateTargetLanguage from '../utils/get-translate-target-language';
|
||||
import localeMatch from '../utils/locale-match';
|
||||
import { speak, supportsTTS } from '../utils/speech';
|
||||
import states from '../utils/states';
|
||||
|
||||
import Icon from './icon';
|
||||
|
@ -51,6 +52,16 @@ export default function MediaAltModal({ alt, lang, onClose }) {
|
|||
<Icon icon="translate" />
|
||||
<span>Translate</span>
|
||||
</MenuItem>
|
||||
{supportsTTS && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
speak(alt, lang);
|
||||
}}
|
||||
>
|
||||
<Icon icon="speak" />
|
||||
<span>Speak</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu2>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -63,6 +63,7 @@ import Media from './media';
|
|||
import { isMediaCaptionLong } from './media';
|
||||
import MenuLink from './menu-link';
|
||||
import RelativeTime from './relative-time';
|
||||
import { speak, supportsTTS } from '../utils/speech';
|
||||
import TranslationBlock from './translation-block';
|
||||
|
||||
const SHOW_COMMENT_COUNT_LIMIT = 280;
|
||||
|
@ -90,6 +91,26 @@ const isIOS =
|
|||
|
||||
const REACTIONS_LIMIT = 80;
|
||||
|
||||
function getPollText(poll) {
|
||||
if (!poll?.options?.length) return '';
|
||||
return `📊:\n${poll.options
|
||||
.map(
|
||||
(option) =>
|
||||
`- ${option.title}${
|
||||
option.votesCount >= 0 ? ` (${option.votesCount})` : ''
|
||||
}`,
|
||||
)
|
||||
.join('\n')}`;
|
||||
}
|
||||
function getPostText(status) {
|
||||
const { spoilerText, content, poll } = status;
|
||||
return (
|
||||
(spoilerText ? `${spoilerText}\n\n` : '') +
|
||||
getHTMLText(content) +
|
||||
getPollText(poll)
|
||||
);
|
||||
}
|
||||
|
||||
function Status({
|
||||
statusID,
|
||||
status,
|
||||
|
@ -782,23 +803,53 @@ function Status({
|
|||
</>
|
||||
)}
|
||||
{enableTranslate ? (
|
||||
<MenuItem
|
||||
disabled={forceTranslate}
|
||||
onClick={() => {
|
||||
setForceTranslate(true);
|
||||
}}
|
||||
>
|
||||
<Icon icon="translate" />
|
||||
<span>Translate</span>
|
||||
</MenuItem>
|
||||
) : (
|
||||
(!language || differentLanguage) && (
|
||||
<MenuLink
|
||||
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
|
||||
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
||||
<MenuItem
|
||||
disabled={forceTranslate}
|
||||
onClick={() => {
|
||||
setForceTranslate(true);
|
||||
}}
|
||||
>
|
||||
<Icon icon="translate" />
|
||||
<span>Translate</span>
|
||||
</MenuLink>
|
||||
</MenuItem>
|
||||
{supportsTTS && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const postText = getPostText(status);
|
||||
if (postText) {
|
||||
speak(postText, language);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon="speak" />
|
||||
<span>Speak</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
(!language || differentLanguage) && (
|
||||
<div class={supportsTTS ? 'menu-horizontal' : ''}>
|
||||
<MenuLink
|
||||
to={`${instance ? `/${instance}` : ''}/s/${id}?translate=1`}
|
||||
>
|
||||
<Icon icon="translate" />
|
||||
<span>Translate</span>
|
||||
</MenuLink>
|
||||
{supportsTTS && (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
const postText = getPostText(status);
|
||||
if (postText) {
|
||||
speak(postText, language);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon icon="speak" />
|
||||
<span>Speak</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{((!isSizeLarge && sameInstance) || enableTranslate) && <MenuDivider />}
|
||||
|
@ -1578,22 +1629,7 @@ function Status({
|
|||
forceTranslate={forceTranslate || inlineTranslate}
|
||||
mini={!isSizeLarge && !withinContext}
|
||||
sourceLanguage={language}
|
||||
text={
|
||||
(spoilerText ? `${spoilerText}\n\n` : '') +
|
||||
getHTMLText(content) +
|
||||
(poll?.options?.length
|
||||
? `\n\nPoll:\n${poll.options
|
||||
.map(
|
||||
(option) =>
|
||||
`- ${option.title}${
|
||||
option.votesCount >= 0
|
||||
? ` (${option.votesCount})`
|
||||
: ''
|
||||
}`,
|
||||
)
|
||||
.join('\n')}`
|
||||
: '')
|
||||
}
|
||||
text={getPostText(status)}
|
||||
/>
|
||||
)}
|
||||
{!spoilerText && sensitive && !!mediaAttachments.length && (
|
||||
|
|
15
src/utils/speech.js
Normal file
15
src/utils/speech.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
export const supportsTTS = 'speechSynthesis' in window;
|
||||
|
||||
export function speak(text, lang) {
|
||||
if (!supportsTTS) return;
|
||||
try {
|
||||
if (speechSynthesis.speaking) {
|
||||
speechSynthesis.cancel();
|
||||
}
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
if (lang) utterance.lang = lang;
|
||||
speechSynthesis.speak(utterance);
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue