Add autotranslations to permalink previews (#9540)
* Add autotranslations to permalink previews * Add missing change * Address feedback and fix test * Address feedback * Fix test * Revert "Fix test" This reverts commit 8aa88c33eb9f805ed57be5f0d90412c2657cca8f. * Revert "Address feedback" This reverts commit c0249d3d0b7a326f777234850e6519e0f98ef41c.
This commit is contained in:
parent
b4798e78cc
commit
058b87ee4d
5 changed files with 278 additions and 16 deletions
|
|
@ -136,6 +136,7 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
expect(permalinkPreview.props.author).toBeDefined();
|
||||
expect(permalinkPreview.props.currentUser).toBeDefined();
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(false);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -169,6 +170,7 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
expect(permalinkPreview.props.author).toBeUndefined();
|
||||
expect(permalinkPreview.props.currentUser).toBeDefined();
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(false);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -209,6 +211,7 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
expect(permalinkPreview.props.teammateNameDisplay).toBe('username');
|
||||
expect(permalinkPreview.props.currentUser).toBeDefined();
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(false);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -241,6 +244,7 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
expect(permalinkPreview.props.author).toBeUndefined();
|
||||
expect(permalinkPreview.props.currentUser).toBeDefined();
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(false);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -281,6 +285,7 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
expect(permalinkPreview.props.teammateNameDisplay).toBe('full_name');
|
||||
expect(permalinkPreview.props.currentUser).toBeDefined();
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(false);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -319,6 +324,58 @@ describe('PermalinkPreview Enhanced Component', () => {
|
|||
await waitFor(() => {
|
||||
const permalinkPreview = getByTestId('permalink-preview');
|
||||
expect(permalinkPreview.props.isMilitaryTime).toBe(true);
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass autotranslationsEnabled true when channel has autotranslation enabled', async () => {
|
||||
await operator.handleConfigs({
|
||||
configs: [
|
||||
{id: 'EnableAutoTranslation', value: 'true'},
|
||||
{id: 'RestrictDMAndGMAutotranslation', value: 'false'},
|
||||
],
|
||||
configsToDelete: [],
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
const channelId = 'channel-123';
|
||||
const channel = TestHelper.fakeChannel({id: channelId, team_id: 'team1', autotranslation: true});
|
||||
const myChannel = TestHelper.fakeMyChannel({
|
||||
id: channelId,
|
||||
channel_id: channelId,
|
||||
user_id: 'current-user',
|
||||
autotranslation_disabled: false,
|
||||
});
|
||||
await operator.handleChannel({channels: [channel], prepareRecordsOnly: false});
|
||||
await operator.handleMyChannel({channels: [channel], myChannels: [myChannel], prepareRecordsOnly: false});
|
||||
|
||||
const embedData: PermalinkEmbedData = {
|
||||
post_id: 'post-123',
|
||||
post: TestHelper.fakePost({
|
||||
id: 'post-123',
|
||||
user_id: 'author-user',
|
||||
message: 'Test message',
|
||||
channel_id: channelId,
|
||||
}),
|
||||
team_name: 'test-team',
|
||||
channel_display_name: 'Test Channel',
|
||||
channel_type: 'O',
|
||||
channel_id: channelId,
|
||||
};
|
||||
|
||||
await createPostWithPermalinkEmbed(operator, database, embedData);
|
||||
|
||||
const {getByTestId} = renderWithEverything(
|
||||
<EnhancedPermalinkPreview
|
||||
embedData={embedData}
|
||||
location={Screens.CHANNEL}
|
||||
/>,
|
||||
{database, serverUrl},
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
const permalinkPreview = getByTestId('permalink-preview');
|
||||
expect(permalinkPreview.props.autotranslationsEnabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {of as of$} from 'rxjs';
|
|||
import {map, switchMap, distinctUntilChanged} from 'rxjs/operators';
|
||||
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
import {observeIsChannelAutotranslated} from '@queries/servers/channel';
|
||||
import {observePost} from '@queries/servers/post';
|
||||
import {queryDisplayNamePreferences} from '@queries/servers/preference';
|
||||
import {observeUser, observeTeammateNameDisplay, observeCurrentUser} from '@queries/servers/user';
|
||||
|
|
@ -27,12 +28,17 @@ const enhance = withObservables(['embedData'], ({database, embedData}: WithDatab
|
|||
const post = embedData?.post_id ? observePost(database, embedData.post_id) : of$(undefined);
|
||||
const isOriginPostDeleted = post.pipe(
|
||||
switchMap((p) => {
|
||||
const initialDeleted = Boolean(embedData?.post?.delete_at > 0 || embedData?.post?.state === 'DELETED');
|
||||
const deleteAt = embedData?.post?.delete_at ?? 0;
|
||||
const initialDeleted = Boolean(deleteAt > 0 || embedData?.post?.state === 'DELETED');
|
||||
return of$(p ? p.deleteAt > 0 : initialDeleted);
|
||||
}),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
const channelId = embedData?.post?.channel_id;
|
||||
|
||||
const autotranslationsEnabled = channelId ? observeIsChannelAutotranslated(database, channelId) : of$(false);
|
||||
|
||||
return {
|
||||
teammateNameDisplay,
|
||||
currentUser,
|
||||
|
|
@ -40,6 +46,7 @@ const enhance = withObservables(['embedData'], ({database, embedData}: WithDatab
|
|||
author,
|
||||
post,
|
||||
isOriginPostDeleted,
|
||||
autotranslationsEnabled,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
import {fireEvent} from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import {showPermalink} from '@actions/remote/permalink';
|
||||
import Markdown from '@components/markdown';
|
||||
import TranslateIcon from '@components/post_list/post/header/translate_icon';
|
||||
import {Screens} from '@constants';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
|
|
@ -28,6 +30,14 @@ jest.mocked(Markdown).mockImplementation((props) =>
|
|||
React.createElement('Text', {testID: 'markdown'}, props.value),
|
||||
);
|
||||
|
||||
jest.mock('@components/post_list/post/header/translate_icon', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
jest.mocked(TranslateIcon).mockImplementation(() =>
|
||||
React.createElement(View, {testID: 'translate-icon'}, null),
|
||||
);
|
||||
|
||||
describe('components/post_list/post/body/content/permalink_preview/PermalinkPreview', () => {
|
||||
let database: Database;
|
||||
let operator: ServerDataOperator;
|
||||
|
|
@ -81,7 +91,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
edit_at: 0,
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -118,6 +128,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
isOriginPostDeleted: false,
|
||||
parentLocation: Screens.CHANNEL,
|
||||
parentPostId: 'parent-post-123',
|
||||
autotranslationsEnabled: false,
|
||||
};
|
||||
|
||||
it('should render permalink preview correctly', () => {
|
||||
|
|
@ -141,7 +152,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
...baseProps,
|
||||
embedData: {
|
||||
...baseProps.embedData,
|
||||
post: undefined as unknown as Post,
|
||||
post: undefined,
|
||||
},
|
||||
post: undefined,
|
||||
};
|
||||
|
|
@ -214,7 +225,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
message: 'Test message',
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -262,7 +273,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
message: longMessage,
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -290,7 +301,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
message: '',
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -319,7 +330,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
create_at: 1234567890000,
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -366,7 +377,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
metadata: {
|
||||
files: [fileInfo],
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -412,7 +423,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
metadata: {
|
||||
files: fileInfos,
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -442,7 +453,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
message: 'Post without files',
|
||||
metadata: {
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -473,7 +484,7 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
metadata: {
|
||||
files: [],
|
||||
embeds: [{
|
||||
type: 'opengraph' as PostEmbedType,
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
|
|
@ -492,4 +503,175 @@ describe('components/post_list/post/body/content/permalink_preview/PermalinkPrev
|
|||
expect(queryByTestId('permalink-files-container')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('autotranslationsEnabled', () => {
|
||||
it('should not render TranslateIcon when autotranslationsEnabled is false', () => {
|
||||
const {queryByTestId} = renderPermalinkPreview({
|
||||
...baseProps,
|
||||
autotranslationsEnabled: false,
|
||||
});
|
||||
|
||||
expect(queryByTestId('translate-icon')).toBeNull();
|
||||
});
|
||||
|
||||
it('should render TranslateIcon when autotranslationsEnabled is true', () => {
|
||||
const {getByTestId} = renderPermalinkPreview({
|
||||
...baseProps,
|
||||
autotranslationsEnabled: true,
|
||||
});
|
||||
|
||||
expect(getByTestId('translate-icon')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display translated message when autotranslationsEnabled is true and translation is ready', () => {
|
||||
const originalMessage = 'Original message';
|
||||
const translatedMessage = 'Translated message';
|
||||
const props = {
|
||||
...baseProps,
|
||||
autotranslationsEnabled: true,
|
||||
embedData: {
|
||||
...baseProps.embedData,
|
||||
post: TestHelper.fakePost({
|
||||
id: 'post-123',
|
||||
user_id: 'user-123',
|
||||
message: originalMessage,
|
||||
create_at: 1234567890000,
|
||||
edit_at: 0,
|
||||
metadata: {
|
||||
translations: {
|
||||
en: {
|
||||
state: 'ready',
|
||||
object: {message: translatedMessage},
|
||||
},
|
||||
},
|
||||
embeds: [{
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
description: 'Example Description',
|
||||
},
|
||||
}],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
const {getByText} = renderPermalinkPreview(props);
|
||||
|
||||
expect(getByText(translatedMessage)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display original message when autotranslationsEnabled is true but translation is not ready', () => {
|
||||
const originalMessage = 'Original message only';
|
||||
const props = {
|
||||
...baseProps,
|
||||
autotranslationsEnabled: true,
|
||||
embedData: {
|
||||
...baseProps.embedData,
|
||||
post: TestHelper.fakePost({
|
||||
id: 'post-123',
|
||||
user_id: 'user-123',
|
||||
message: originalMessage,
|
||||
create_at: 1234567890000,
|
||||
edit_at: 0,
|
||||
metadata: {
|
||||
translations: {
|
||||
en: {
|
||||
state: 'processing',
|
||||
object: {message: ''},
|
||||
},
|
||||
},
|
||||
embeds: [{
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
description: 'Example Description',
|
||||
},
|
||||
}],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
const {getByText} = renderPermalinkPreview(props);
|
||||
|
||||
expect(getByText(originalMessage)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display original message when autotranslationsEnabled is false regardless of translation', () => {
|
||||
const originalMessage = 'Original message';
|
||||
const props = {
|
||||
...baseProps,
|
||||
autotranslationsEnabled: false,
|
||||
embedData: {
|
||||
...baseProps.embedData,
|
||||
post: TestHelper.fakePost({
|
||||
id: 'post-123',
|
||||
user_id: 'user-123',
|
||||
message: originalMessage,
|
||||
create_at: 1234567890000,
|
||||
edit_at: 0,
|
||||
metadata: {
|
||||
translations: {
|
||||
en: {
|
||||
state: 'ready',
|
||||
object: {message: 'Translated message'},
|
||||
},
|
||||
},
|
||||
embeds: [{
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
description: 'Example Description',
|
||||
},
|
||||
}],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
const {getByText} = renderPermalinkPreview(props);
|
||||
|
||||
expect(getByText(originalMessage)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not translate custom posts or show TranslateIcon when autotranslationsEnabled is true', () => {
|
||||
const originalMessage = 'Custom message';
|
||||
const translatedMessage = 'Translated custom message';
|
||||
const props = {
|
||||
...baseProps,
|
||||
autotranslationsEnabled: true,
|
||||
embedData: {
|
||||
...baseProps.embedData,
|
||||
post: TestHelper.fakePost({
|
||||
id: 'system-post-123',
|
||||
user_id: 'user-123',
|
||||
type: 'custom_llmbot',
|
||||
message: originalMessage,
|
||||
create_at: 1234567890000,
|
||||
edit_at: 0,
|
||||
metadata: {
|
||||
translations: {
|
||||
en: {
|
||||
state: 'ready',
|
||||
object: {message: translatedMessage},
|
||||
},
|
||||
},
|
||||
embeds: [{
|
||||
type: 'opengraph',
|
||||
url: 'https://example.com',
|
||||
data: {
|
||||
title: 'Example Title',
|
||||
description: 'Example Description',
|
||||
},
|
||||
}],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
const {queryByTestId, getByText} = renderPermalinkPreview(props);
|
||||
expect(queryByTestId('translate-icon')).toBeNull();
|
||||
expect(getByText(originalMessage)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import EditedIndicator from '@components/edited_indicator';
|
|||
import FormattedText from '@components/formatted_text';
|
||||
import FormattedTime from '@components/formatted_time';
|
||||
import Markdown from '@components/markdown';
|
||||
import TranslateIcon from '@components/post_list/post/header/translate_icon';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import {View as ViewConstants} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
|
|
@ -18,6 +19,7 @@ import {useTheme} from '@context/theme';
|
|||
import {useUserLocale} from '@context/user_locale';
|
||||
import {useIsTablet, useWindowDimensions} from '@hooks/device';
|
||||
import {usePreventDoubleTap} from '@hooks/utils';
|
||||
import {getPostTranslatedMessage, getPostTranslation} from '@utils/post';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
import {displayUsername, getUserTimezone} from '@utils/user';
|
||||
|
|
@ -46,6 +48,7 @@ type PermalinkPreviewProps = {
|
|||
location: AvailableScreens;
|
||||
parentLocation?: string;
|
||||
parentPostId?: string;
|
||||
autotranslationsEnabled: boolean;
|
||||
};
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -77,6 +80,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
marginLeft: 12,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
authorName: {
|
||||
color: theme.centerChannelColor,
|
||||
|
|
@ -85,7 +89,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
timestamp: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.64),
|
||||
...typography('Body', 75),
|
||||
marginLeft: 8,
|
||||
},
|
||||
messageContainer: {
|
||||
marginBottom: 8,
|
||||
|
|
@ -130,6 +133,7 @@ const PermalinkPreview = ({
|
|||
location,
|
||||
parentLocation,
|
||||
parentPostId,
|
||||
autotranslationsEnabled,
|
||||
}: PermalinkPreviewProps) => {
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
|
|
@ -170,8 +174,15 @@ const PermalinkPreview = ({
|
|||
channel_type,
|
||||
} = embedData;
|
||||
|
||||
const translation = embedPost ? getPostTranslation(embedPost, locale) : undefined;
|
||||
|
||||
const truncatedMessage = useMemo(() => {
|
||||
const msg = embedPost?.message;
|
||||
let msg = embedPost?.message ?? '';
|
||||
if (autotranslationsEnabled && embedPost?.type === '') {
|
||||
if (translation?.state === 'ready') {
|
||||
msg = getPostTranslatedMessage(msg, translation);
|
||||
}
|
||||
}
|
||||
if (!msg || typeof msg !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -183,7 +194,7 @@ const PermalinkPreview = ({
|
|||
}
|
||||
|
||||
return cleanMessage;
|
||||
}, [embedPost?.message]);
|
||||
}, [autotranslationsEnabled, embedPost?.message, embedPost?.type, translation]);
|
||||
|
||||
const isEdited = useMemo(() => embedData && embedData.post && embedData.post.edit_at > 0, [embedData]);
|
||||
|
||||
|
|
@ -254,9 +265,14 @@ const PermalinkPreview = ({
|
|||
<FormattedTime
|
||||
timezone={getUserTimezone(currentUser)}
|
||||
isMilitaryTime={isMilitaryTime}
|
||||
value={embedPost.create_at}
|
||||
value={embedPost?.create_at ?? 0}
|
||||
style={styles.timestamp}
|
||||
/>
|
||||
{autotranslationsEnabled && embedPost?.type === '' && (
|
||||
<TranslateIcon
|
||||
translationState={translation?.state}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
|
|
|||
2
types/api/posts.d.ts
vendored
2
types/api/posts.d.ts
vendored
|
|
@ -51,7 +51,7 @@ type PostPriority = {
|
|||
|
||||
type PermalinkEmbedData = {
|
||||
post_id: string;
|
||||
post: Post;
|
||||
post?: Post;
|
||||
team_name: string;
|
||||
channel_display_name: string;
|
||||
channel_type: string;
|
||||
|
|
|
|||
Loading…
Reference in a new issue