makes new translation button

This commit is contained in:
ChaosExAnima
2026-09-11 18:21:16 +02:00
parent f12789ec33
commit fef41d50f6
4 changed files with 104 additions and 72 deletions

View File

@@ -1,20 +1,16 @@
import type React from 'react';
import { useCallback, useState } from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { CaretRightIcon } from '@phosphor-icons/react';
import { useIdentity } from '@/mastodon/identity_context';
import { languages as preloadedLanguages } from '@/mastodon/initial_state';
import type {
ExpandedStatusShape,
StatusShape,
StatusTranslation,
} from '@/mastodon/models/status';
import { useAppSelector } from '@/mastodon/store';
import { Button } from '../button/redesign';
import { EmojiHTML } from '../emoji/html';
@@ -42,12 +38,6 @@ export const StatusContent: React.FC<
className,
...props
}) => {
const { signedIn } = useIdentity();
const targetLanguages = useAppSelector(
(state) => state.server.translationLanguages.item?.[status.language],
);
const intl = useIntl();
// Determines if a long post should show the read more button.
const [collapsed, setCollapsed] = useState(false);
const onRef = useCallback(
@@ -70,14 +60,6 @@ export const StatusContent: React.FC<
const language = status.translation?.language ?? status.language;
const isCollapsed = !!onReadMore && collapsible && collapsed;
const renderTranslate = !!(
onTranslate &&
!isCollapsed &&
signedIn &&
['public', 'unlisted'].includes(status.visibility) &&
status.search_index?.trim().length &&
targetLanguages?.includes(intl.locale.replace(/[_-].*/, ''))
);
return (
<div
@@ -102,13 +84,6 @@ export const StatusContent: React.FC<
{children}
{renderTranslate && (
<TranslateButton
translation={status.translation}
onTranslate={onTranslate}
/>
)}
{isCollapsed && (
<Button
size='sm'
@@ -123,48 +98,3 @@ export const StatusContent: React.FC<
</div>
);
};
const TranslateButton: React.FC<{
onTranslate: React.MouseEventHandler<HTMLButtonElement>;
translation?: StatusTranslation;
}> = ({ translation, onTranslate }) => {
if (!translation) {
return (
<Button
size='sm'
variant='ghost'
onClick={onTranslate}
className={classes.buttonAlign}
>
<FormattedMessage id='status.translate' defaultMessage='Translate' />
</Button>
);
}
const language = preloadedLanguages?.find(
(lang) => lang[0] === translation.detected_source_language,
);
const languageName = language
? language[1]
: translation.detected_source_language;
const provider = translation.provider;
return (
<div className='translate-button'>
<Button size='sm' variant='ghost' onClick={onTranslate}>
<FormattedMessage
id='status.show_original'
defaultMessage='Show original'
/>
</Button>
<div className='translate-button__meta'>
<FormattedMessage
id='status.translated_from_with'
defaultMessage='Translated from {lang} using {provider}'
values={{ lang: languageName, provider }}
/>
</div>
</div>
);
};

View File

@@ -28,6 +28,7 @@ import { StatusMeta } from './meta';
import { StatusPrepend } from './prepend';
import { StatusRedesignHeader } from './redesign/header';
import classes from './styles.module.scss';
import { TranslateButton } from './translate';
import type { StatusContainerProps, StatusContextType } from './types';
type StatusRedesignProps = Merge<
@@ -185,6 +186,8 @@ export const StatusRedesign: React.FC<StatusRedesignProps> = ({
/>
)}
<TranslateButton status={status} onTranslate={onTranslate} />
{expanded && (
<StatusContent
status={status}

View File

@@ -39,10 +39,23 @@
.content,
.footer,
.root :global(.status__prepend),
.root :global(.content-warning) {
.root :global(.content-warning),
.translate {
grid-column: var(--content-col);
}
.translate {
display: flex;
align-items: center;
}
.translateInfo {
@include mixins.type-label-md;
color: var(--color-text-secondary);
margin-inline-start: auto;
}
.content {
display: flex;
flex-direction: column;

View File

@@ -0,0 +1,86 @@
import { FormattedMessage, useIntl } from 'react-intl';
import { TranslateIcon } from '@phosphor-icons/react';
import { useIdentity } from '@/mastodon/identity_context';
import { languages as preloadedLanguages } from '@/mastodon/initial_state';
import type { AnyStatusShape } from '@/mastodon/models/status';
import { useAppSelector } from '@/mastodon/store';
import { Button } from '../button/redesign';
import classes from './styles.module.scss';
export const TranslateButton: React.FC<{
status: AnyStatusShape;
onTranslate: React.MouseEventHandler<HTMLButtonElement>;
}> = ({ status, onTranslate }) => {
const { signedIn } = useIdentity();
const targetLanguages = useAppSelector(
(state) => state.server.translationLanguages.item?.[status.language],
);
const intl = useIntl();
const { translation } = status;
if (
!signedIn ||
!['public', 'unlisted'].includes(status.visibility) ||
status.search_index?.trim().length === 0 ||
!targetLanguages?.includes(intl.locale.replace(/[_-].*/, ''))
) {
return null;
}
if (!translation || translation.isLoading) {
return (
<div className={classes.translate}>
<Button
size='sm'
color='accent'
variant='ghost'
onClick={onTranslate}
leadingIcon={TranslateIcon}
className={classes.buttonAlign}
loading={translation?.isLoading}
>
<FormattedMessage id='status.translate' defaultMessage='Translate' />
</Button>
</div>
);
}
const language = preloadedLanguages?.find(
(lang) => lang[0] === translation.detected_source_language,
);
const languageName = language
? language[1]
: translation.detected_source_language;
const provider = translation.provider;
return (
<div className={classes.translate}>
<Button
size='sm'
color='accent'
variant='ghost'
onClick={onTranslate}
leadingIcon={TranslateIcon}
className={classes.buttonAlign}
>
<FormattedMessage
id='status.show_original'
defaultMessage='Show original'
/>
</Button>
<div className={classes.translateInfo}>
<FormattedMessage
id='status.translated_from_with'
defaultMessage='Translated from {lang} using {provider}'
values={{ lang: languageName, provider }}
/>
</div>
</div>
);
};