Profile: Support auto translation in channels (#5891)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com> Co-authored-by: Dmitry Kabanov <dmitrykabanovdev@gmail.com>
This commit is contained in:
parent
aea46b1796
commit
9b82953426
@ -94,6 +94,7 @@ export interface GramJsAppConfig extends LimitsConfig {
|
||||
freeze_since_date?: number;
|
||||
freeze_until_date?: number;
|
||||
freeze_appeal_url?: string;
|
||||
channel_autotranslation_level_min?: number;
|
||||
stars_stargift_resale_amount_max?: number;
|
||||
stars_stargift_resale_amount_min?: number;
|
||||
stars_stargift_resale_commission_permille?: number;
|
||||
@ -182,6 +183,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
||||
bandwidthPremiumUploadSpeedup: appConfig.upload_premium_speedup_upload,
|
||||
bandwidthPremiumDownloadSpeedup: appConfig.upload_premium_speedup_download,
|
||||
channelRestrictAdsLevelMin: appConfig.channel_restrict_sponsored_level_min,
|
||||
channelAutoTranslationLevelMin: appConfig.channel_autotranslation_level_min,
|
||||
paidReactionMaxAmount: appConfig.stars_paid_reaction_amount_max,
|
||||
isChannelRevenueWithdrawalEnabled: appConfig.channel_revenue_withdrawal_enabled,
|
||||
isStarsGiftEnabled: appConfig.stars_gifts_enabled,
|
||||
|
||||
@ -119,6 +119,8 @@ function buildApiChatFieldsFromPeerEntity(
|
||||
hasGeo: channel?.hasGeo,
|
||||
subscriptionUntil: channel?.subscriptionUntilDate,
|
||||
paidMessagesStars: paidMessagesStars?.toJSNumber(),
|
||||
level: channel?.level,
|
||||
hasAutoTranslation: channel?.autotranslation,
|
||||
|
||||
...buildApiChatPermissions(peerEntity),
|
||||
...buildApiChatRestrictions(peerEntity),
|
||||
|
||||
@ -2087,3 +2087,16 @@ export async function fetchSponsoredPeer({ query }: { query: string }) {
|
||||
if (!result || result instanceof GramJs.contacts.SponsoredPeersEmpty) return undefined;
|
||||
return buildApiSponsoredPeer(result.peers[0]);
|
||||
}
|
||||
|
||||
export function toggleAutoTranslation({
|
||||
chat, isEnabled,
|
||||
}: {
|
||||
chat: ApiChat; isEnabled: boolean;
|
||||
}) {
|
||||
return invokeRequest(new GramJs.channels.ToggleAutotranslation({
|
||||
channel: buildInputChannel(chat.id, chat.accessHash),
|
||||
enabled: isEnabled,
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,6 +49,8 @@ export interface ApiChat {
|
||||
isForumAsMessages?: true;
|
||||
boostLevel?: number;
|
||||
botVerificationIconId?: string;
|
||||
hasAutoTranslation?: true;
|
||||
level?: number;
|
||||
|
||||
// Calls
|
||||
isCallActive?: boolean;
|
||||
|
||||
@ -235,6 +235,7 @@ export interface ApiAppConfig {
|
||||
bandwidthPremiumUploadSpeedup?: number;
|
||||
bandwidthPremiumDownloadSpeedup?: number;
|
||||
channelRestrictAdsLevelMin?: number;
|
||||
channelAutoTranslationLevelMin?: number;
|
||||
paidReactionMaxAmount?: number;
|
||||
isChannelRevenueWithdrawalEnabled?: boolean;
|
||||
isStarsGiftEnabled?: boolean;
|
||||
|
||||
@ -2007,4 +2007,5 @@
|
||||
"ValueGiftSortByPrice" = "Price";
|
||||
"ValueGiftSortByNumber" = "Number";
|
||||
"ResellGiftsNoFound" = "No gifts found";
|
||||
"ResellGiftsClearFilters" = "Clear Filters";
|
||||
"ResellGiftsClearFilters" = "Clear Filters";
|
||||
"AutomaticTranslation" = "Automatic Translation";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import {
|
||||
memo, useMemo, useRef, useState,
|
||||
memo, useCallback, useMemo, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
@ -123,8 +123,8 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
|
||||
language,
|
||||
detectedChatLanguage,
|
||||
doNotTranslate,
|
||||
onTopicSearch,
|
||||
isAccountFrozen,
|
||||
onTopicSearch,
|
||||
}) => {
|
||||
const {
|
||||
joinChannel,
|
||||
@ -239,7 +239,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
|
||||
handleSearchClick();
|
||||
});
|
||||
|
||||
const getTextWithLanguage = useLastCallback((langKey: string, langCode: string) => {
|
||||
const getTextWithLanguage = useCallback((langKey: string, langCode: string) => {
|
||||
const simplified = langCode.split('-')[0];
|
||||
const translationKey = `TranslateLanguage${simplified.toUpperCase()}`;
|
||||
const name = lang(translationKey);
|
||||
@ -250,7 +250,7 @@ const HeaderActions: FC<OwnProps & StateProps> = ({
|
||||
const translatedNames = new Intl.DisplayNames([language], { type: 'language' });
|
||||
const translatedName = translatedNames.of(langCode)!;
|
||||
return lang(`${langKey}Other`, translatedName);
|
||||
});
|
||||
}, [language, lang]);
|
||||
|
||||
const buttonText = useMemo(() => {
|
||||
if (isTranslating) return lang('ShowOriginalButton');
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
} from '../../global/helpers';
|
||||
import {
|
||||
selectBot,
|
||||
selectCanTranslateChat,
|
||||
selectChat,
|
||||
selectChatFullInfo,
|
||||
selectChatLastMessage,
|
||||
@ -52,6 +53,7 @@ import {
|
||||
selectTabState,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectTranslationLanguage,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll';
|
||||
@ -92,13 +94,13 @@ type OwnProps = {
|
||||
isComments?: boolean;
|
||||
canPost: boolean;
|
||||
isReady: boolean;
|
||||
onScrollDownToggle: BooleanToVoidFunction;
|
||||
onNotchToggle: BooleanToVoidFunction;
|
||||
withBottomShift?: boolean;
|
||||
withDefaultBg: boolean;
|
||||
onIntersectPinnedMessage: OnIntersectPinnedMessage;
|
||||
isContactRequirePremium?: boolean;
|
||||
paidMessagesStars?: number;
|
||||
onScrollDownToggle: BooleanToVoidFunction;
|
||||
onNotchToggle: BooleanToVoidFunction;
|
||||
onIntersectPinnedMessage: OnIntersectPinnedMessage;
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
@ -137,6 +139,9 @@ type StateProps = {
|
||||
isChatProtected?: boolean;
|
||||
hasCustomGreeting?: boolean;
|
||||
isAppConfigLoaded?: boolean;
|
||||
canTranslate?: boolean;
|
||||
translationLanguage?: string;
|
||||
shouldAutoTranslate?: boolean;
|
||||
};
|
||||
|
||||
const MESSAGE_REACTIONS_POLLING_INTERVAL = 20 * 1000;
|
||||
@ -196,16 +201,19 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
areAdsEnabled,
|
||||
channelJoinInfo,
|
||||
isChatProtected,
|
||||
onIntersectPinnedMessage,
|
||||
onScrollDownToggle,
|
||||
onNotchToggle,
|
||||
isAccountFrozen,
|
||||
hasCustomGreeting,
|
||||
isAppConfigLoaded,
|
||||
canTranslate,
|
||||
translationLanguage,
|
||||
shouldAutoTranslate,
|
||||
onIntersectPinnedMessage,
|
||||
onScrollDownToggle,
|
||||
onNotchToggle,
|
||||
}) => {
|
||||
const {
|
||||
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
|
||||
loadMessageViews, loadPeerStoriesByIds, loadFactChecks,
|
||||
loadMessageViews, loadPeerStoriesByIds, loadFactChecks, requestChatTranslation,
|
||||
} = getActions();
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
@ -268,6 +276,12 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
||||
memoFocusingIdRef.current = focusingId;
|
||||
}, [focusingId]);
|
||||
|
||||
// Enable auto translation for the chat if it's available
|
||||
useEffect(() => {
|
||||
if (!shouldAutoTranslate || !canTranslate) return;
|
||||
requestChatTranslation({ chatId, toLanguageCode: translationLanguage });
|
||||
}, [shouldAutoTranslate, canTranslate, translationLanguage, chatId]);
|
||||
|
||||
useNativeCopySelectedMessages(copyMessagesByIds);
|
||||
|
||||
const messageGroups = useMemo(() => {
|
||||
@ -806,6 +820,10 @@ export default memo(withGlobal<OwnProps>(
|
||||
const hasCustomGreeting = Boolean(userFullInfo?.businessIntro);
|
||||
const isAppConfigLoaded = global.isAppConfigLoaded;
|
||||
|
||||
const canTranslate = selectCanTranslateChat(global, chatId) && !chatFullInfo?.isTranslationDisabled;
|
||||
const shouldAutoTranslate = chat?.hasAutoTranslation;
|
||||
const translationLanguage = selectTranslationLanguage(global);
|
||||
|
||||
return {
|
||||
areAdsEnabled,
|
||||
isChatLoaded: true,
|
||||
@ -842,6 +860,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
isAccountFrozen,
|
||||
hasCustomGreeting,
|
||||
isAppConfigLoaded,
|
||||
canTranslate,
|
||||
translationLanguage,
|
||||
shouldAutoTranslate,
|
||||
};
|
||||
},
|
||||
)(MessageList));
|
||||
|
||||
@ -747,7 +747,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const detectedLanguage = useTextLanguage(
|
||||
text?.text,
|
||||
!(areTranslationsEnabled || shouldDetectChatLanguage),
|
||||
!(areTranslationsEnabled && shouldDetectChatLanguage),
|
||||
getIsMessageListReady,
|
||||
);
|
||||
useDetectChatLanguage(message, detectedLanguage, !shouldDetectChatLanguage, getIsMessageListReady);
|
||||
|
||||
@ -30,13 +30,25 @@ export default function useDetectChatLanguage(
|
||||
if (isDisabled || (getIsReady && !getIsReady())) return;
|
||||
|
||||
const isTranslatable = Boolean(message.content.text?.text.length);
|
||||
processMessageMetadata(message.chatId, message.id, isTranslatable, detectedLanguage);
|
||||
processMessageMetadata({
|
||||
chatId: message.chatId,
|
||||
id: message.id,
|
||||
isTranslatable,
|
||||
detectedLanguage,
|
||||
});
|
||||
}, [message, detectedLanguage, isDisabled, getIsReady]);
|
||||
}
|
||||
|
||||
const throttledMakeChatDecision = throttle(makeChatDecision, THROTTLE_DELAY);
|
||||
|
||||
function processMessageMetadata(chatId: string, id: number, isTranslatable: boolean, detectedLanguage?: string) {
|
||||
function processMessageMetadata({
|
||||
chatId, id, isTranslatable, detectedLanguage,
|
||||
}: {
|
||||
chatId: string;
|
||||
id: number;
|
||||
isTranslatable: boolean;
|
||||
detectedLanguage?: string;
|
||||
}) {
|
||||
const chatStats = CHAT_STATS.get(chatId) || new LimitedMap<number, MessageMetadata>(MESSAGES_LIMIT);
|
||||
|
||||
const previousMetadata = chatStats.get(id);
|
||||
@ -90,7 +102,7 @@ function makeChatDecision(chatId: string) {
|
||||
}
|
||||
|
||||
const translatableRatio = translatableCount / messagesChecked;
|
||||
const detectableRatio = detectableCount / messagesChecked;
|
||||
const detectableRatio = detectableCount / translatableCount;
|
||||
|
||||
if (translatableRatio < MIN_TRANSLATABLE_RATIO || detectableRatio < MIN_DETECTABLE_RATIO) {
|
||||
return;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import {
|
||||
memo, useCallback, useEffect, useMemo, useState,
|
||||
memo, useEffect, useMemo, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
@ -11,12 +11,13 @@ import type {
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
import { ManagementProgress, ManagementScreens } from '../../../types';
|
||||
|
||||
import { getChatAvatarHash, getHasAdminRight, isChatPublic } from '../../../global/helpers';
|
||||
import { getChatAvatarHash, getHasAdminRight, isChatChannel, isChatPublic } from '../../../global/helpers';
|
||||
import { selectChat, selectChatFullInfo, selectTabState } from '../../../global/selectors';
|
||||
import { formatInteger } from '../../../util/textFormat';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
@ -27,6 +28,7 @@ import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
import InputText from '../../ui/InputText';
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import Spinner from '../../ui/Spinner';
|
||||
import Switcher from '../../ui/Switcher';
|
||||
import TextArea from '../../ui/TextArea';
|
||||
|
||||
import './Management.scss';
|
||||
@ -46,6 +48,8 @@ type StateProps = {
|
||||
canInvite?: boolean;
|
||||
exportedInvites?: ApiExportedInvite[];
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
hasAutoTranslation?: boolean;
|
||||
canToggleAutoTranslation?: boolean;
|
||||
};
|
||||
|
||||
const CHANNEL_TITLE_EMPTY = 'Channel title can\'t be empty';
|
||||
@ -63,6 +67,8 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
availableReactions,
|
||||
onScreenSelect,
|
||||
onClose,
|
||||
hasAutoTranslation,
|
||||
canToggleAutoTranslation,
|
||||
}) => {
|
||||
const {
|
||||
updateChat,
|
||||
@ -72,6 +78,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
openChat,
|
||||
loadExportedChatInvites,
|
||||
loadChatJoinRequests,
|
||||
toggleAutoTranslation,
|
||||
} = getActions();
|
||||
|
||||
const currentTitle = chat?.title || '';
|
||||
@ -88,6 +95,12 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||
const lang = useOldLang();
|
||||
|
||||
const hasAutoTranslationAvailable = chat && isChatChannel(chat);
|
||||
|
||||
const handleAutoTranslationChange = useLastCallback(() => {
|
||||
toggleAutoTranslation({ chatId, isEnabled: !hasAutoTranslation });
|
||||
});
|
||||
|
||||
useHistoryBack({
|
||||
isActive,
|
||||
onBack: onClose,
|
||||
@ -112,46 +125,46 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
}, [chatFullInfo?.adminMembersById]);
|
||||
const removedUsersCount = chatFullInfo?.kickedMembers?.length || 0;
|
||||
|
||||
const handleClickEditType = useCallback(() => {
|
||||
const handleClickEditType = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.ChatPrivacyType);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleClickDiscussion = useCallback(() => {
|
||||
const handleClickDiscussion = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.Discussion);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleClickReactions = useCallback(() => {
|
||||
const handleClickReactions = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.Reactions);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleClickAdministrators = useCallback(() => {
|
||||
const handleClickAdministrators = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.ChatAdministrators);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleClickInvites = useCallback(() => {
|
||||
const handleClickInvites = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.Invites);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleClickRequests = useCallback(() => {
|
||||
const handleClickRequests = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.JoinRequests);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleSetPhoto = useCallback((file: File) => {
|
||||
const handleSetPhoto = useLastCallback((file: File) => {
|
||||
setPhoto(file);
|
||||
setIsProfileFieldsTouched(true);
|
||||
}, []);
|
||||
});
|
||||
|
||||
const handleTitleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const handleTitleChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value);
|
||||
setIsProfileFieldsTouched(true);
|
||||
}, []);
|
||||
});
|
||||
|
||||
const handleAboutChange = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const handleAboutChange = useLastCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setAbout(e.target.value);
|
||||
setIsProfileFieldsTouched(true);
|
||||
}, []);
|
||||
});
|
||||
|
||||
const handleUpdateChannel = useCallback(() => {
|
||||
const handleUpdateChannel = useLastCallback(() => {
|
||||
const trimmedTitle = title.trim();
|
||||
const trimmedAbout = about.trim();
|
||||
|
||||
@ -166,17 +179,17 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
about: trimmedAbout,
|
||||
photo,
|
||||
});
|
||||
}, [about, chatId, photo, title, updateChat]);
|
||||
});
|
||||
|
||||
const handleClickSubscribers = useCallback(() => {
|
||||
const handleClickSubscribers = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.ChannelSubscribers);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleRemovedUsersClick = useCallback(() => {
|
||||
const handleRemovedUsersClick = useLastCallback(() => {
|
||||
onScreenSelect(ManagementScreens.ChannelRemovedUsers);
|
||||
}, [onScreenSelect]);
|
||||
});
|
||||
|
||||
const handleDeleteChannel = useCallback(() => {
|
||||
const handleDeleteChannel = useLastCallback(() => {
|
||||
if (chat.isCreator) {
|
||||
deleteChannel({ chatId: chat.id });
|
||||
} else {
|
||||
@ -186,7 +199,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
closeDeleteDialog();
|
||||
closeManagement();
|
||||
openChat({ id: undefined });
|
||||
}, [chat.isCreator, chat.id, closeDeleteDialog, closeManagement, leaveChannel, deleteChannel, openChat]);
|
||||
});
|
||||
|
||||
const chatReactionsDescription = useMemo(() => {
|
||||
if (!chatFullInfo?.enabledReactions) {
|
||||
@ -290,6 +303,22 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
{chatReactionsDescription}
|
||||
</span>
|
||||
</ListItem>
|
||||
{hasAutoTranslationAvailable && (
|
||||
<ListItem
|
||||
icon="language"
|
||||
narrow
|
||||
ripple
|
||||
disabled={!canToggleAutoTranslation}
|
||||
onClick={handleAutoTranslationChange}
|
||||
>
|
||||
<span>{lang('AutomaticTranslation')}</span>
|
||||
<Switcher
|
||||
id="auto-translation"
|
||||
label={lang('AutomaticTranslation')}
|
||||
checked={hasAutoTranslation}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
</div>
|
||||
<div className="section">
|
||||
<ListItem
|
||||
@ -353,7 +382,11 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { management } = selectTabState(global);
|
||||
const { progress } = management;
|
||||
const { invites } = management.byChatId[chatId] || {};
|
||||
|
||||
const minLevelToToggleAutoTranslation = global.appConfig?.channelAutoTranslationLevelMin;
|
||||
const hasAutoTranslation = chat?.hasAutoTranslation;
|
||||
const chatBoostLevel = chat?.level;
|
||||
const canToggleAutoTranslation = chatBoostLevel && minLevelToToggleAutoTranslation
|
||||
? chatBoostLevel >= minLevelToToggleAutoTranslation : false;
|
||||
return {
|
||||
chat,
|
||||
chatFullInfo: selectChatFullInfo(global, chatId),
|
||||
@ -362,6 +395,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
canInvite: getHasAdminRight(chat, 'inviteUsers'),
|
||||
exportedInvites: invites,
|
||||
availableReactions: global.reactions.availableReactions,
|
||||
hasAutoTranslation,
|
||||
canToggleAutoTranslation,
|
||||
};
|
||||
},
|
||||
(global, { chatId }) => {
|
||||
|
||||
@ -15,7 +15,7 @@ import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||
@ -154,14 +154,14 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
|
||||
if (isChatPublic(linkedGroup)) {
|
||||
return renderText(
|
||||
`Do you want to make **${linkedGroup.title}** the discussion board for **${chat!.title}**?`,
|
||||
`Do you want to make **${linkedGroup.title}** the discussion board for **${chat?.title}**?`,
|
||||
['br', 'simple_markdown'],
|
||||
);
|
||||
}
|
||||
|
||||
return renderText(
|
||||
// eslint-disable-next-line @stylistic/max-len
|
||||
`Do you want to make **${linkedGroup.title}** the discussion board for **${chat!.title}**?\n\nAnyone from the channel will be able to see messages in this group.`,
|
||||
`Do you want to make **${linkedGroup.title}** the discussion board for **${chat?.title}**?\n\nAnyone from the channel will be able to see messages in this group.`,
|
||||
['br', 'simple_markdown'],
|
||||
);
|
||||
}
|
||||
@ -169,12 +169,14 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
function renderLinkedGroup() {
|
||||
return (
|
||||
<div>
|
||||
<ListItem
|
||||
className="chat-item-clickable"
|
||||
inactive
|
||||
>
|
||||
<GroupChatInfo chatId={linkedChat!.id} />
|
||||
</ListItem>
|
||||
{linkedChat && (
|
||||
<ListItem
|
||||
className="chat-item-clickable"
|
||||
inactive
|
||||
>
|
||||
<GroupChatInfo chatId={linkedChat.id} />
|
||||
</ListItem>
|
||||
)}
|
||||
<ListItem
|
||||
icon="delete"
|
||||
ripple
|
||||
@ -188,7 +190,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
onClose={closeConfirmUnlinkGroupDialog}
|
||||
header={renderUnlinkGroupHeader()}
|
||||
textParts={renderText(
|
||||
lang(isChannel ? 'DiscussionUnlinkChannelAlert' : 'DiscussionUnlinkGroupAlert', linkedChat!.title),
|
||||
lang(isChannel ? 'DiscussionUnlinkChannelAlert' : 'DiscussionUnlinkGroupAlert', linkedChat?.title),
|
||||
['br', 'simple_markdown'],
|
||||
)}
|
||||
confirmLabel={lang(isChannel ? 'DiscussionUnlinkGroup' : 'DiscussionUnlinkChannel')}
|
||||
|
||||
@ -134,3 +134,7 @@
|
||||
right: 0.5rem;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.boostInfo {
|
||||
margin: 0 -1rem;
|
||||
}
|
||||
|
||||
@ -226,8 +226,7 @@ const BoostStatistics = ({
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
className="chat-item-clickable"
|
||||
|
||||
className={buildClassName(styles.boostInfo, 'chat-item-clickable')}
|
||||
onClick={() => handleBoosterClick(boost.userId)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -2984,6 +2984,19 @@ addActionHandler('updatePaidMessagesPrice', async (global, actions, payload): Pr
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('toggleAutoTranslation', async (global, actions, payload): Promise<void> => {
|
||||
const { chatId, isEnabled } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
const result = await callApi('toggleAutoTranslation', { chat, isEnabled });
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
global = updateChat(global, chatId, { hasAutoTranslation: isEnabled || undefined });
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('resolveBusinessChatLink', async (global, actions, payload): Promise<void> => {
|
||||
const { slug, tabId = getCurrentTabId() } = payload;
|
||||
const result = await callApi('resolveBusinessChatLink', { slug });
|
||||
|
||||
@ -283,6 +283,9 @@ export function selectShouldDetectChatLanguage<T extends GlobalState>(
|
||||
) {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return false;
|
||||
|
||||
if (chat.hasAutoTranslation) return true;
|
||||
|
||||
const { canTranslateChats } = global.settings.byKey;
|
||||
|
||||
const isPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
@ -1081,6 +1081,10 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
paidMessagesStars: number;
|
||||
} & WithTabId;
|
||||
toggleAutoTranslation: {
|
||||
chatId: string;
|
||||
isEnabled: boolean;
|
||||
} & WithTabId;
|
||||
|
||||
updateChat: {
|
||||
chatId: string;
|
||||
|
||||
6
src/lib/gramjs/tl/api.d.ts
vendored
6
src/lib/gramjs/tl/api.d.ts
vendored
@ -421,7 +421,7 @@ namespace Api {
|
||||
export type TypeAccessPointRule = AccessPointRule;
|
||||
export type TypeTlsClientHello = TlsClientHello;
|
||||
export type TypeTlsBlock = TlsBlockString | TlsBlockRandom | TlsBlockZero | TlsBlockDomain | TlsBlockGrease | TlsBlockScope;
|
||||
|
||||
|
||||
|
||||
export namespace storage {
|
||||
export type TypeFileType = storage.FileUnknown | storage.FilePartial | storage.FileJpeg | storage.FileGif | storage.FilePng | storage.FilePdf | storage.FileMp3 | storage.FileMov | storage.FileMp4 | storage.FileWebp;
|
||||
@ -17724,7 +17724,7 @@ namespace Api {
|
||||
|
||||
static fromReader(reader: Reader): TlsBlockScope;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export namespace storage {
|
||||
export class FileUnknown extends VirtualClass<void> {
|
||||
@ -22053,7 +22053,7 @@ namespace Api {
|
||||
}>, Api.TypeDestroySessionRes> {
|
||||
sessionId: long;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export namespace auth {
|
||||
export class SendCode extends Request<Partial<{
|
||||
|
||||
@ -1727,6 +1727,7 @@ channels.toggleViewForumAsMessages#9738bb15 channel:InputChannel enabled:Bool =
|
||||
channels.getChannelRecommendations#25a71742 flags:# channel:flags.0?InputChannel = messages.Chats;
|
||||
channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
channels.updatePaidMessagesPrice#fc84653f channel:InputChannel send_paid_messages_stars:long = Updates;
|
||||
channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Updates;
|
||||
bots.setBotInfo#10cf3123 flags:# bot:flags.2?InputUser lang_code:string name:flags.3?string about:flags.0?string description:flags.1?string = Bool;
|
||||
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
|
||||
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
|
||||
@ -1834,4 +1835,4 @@ premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:
|
||||
premium.getMyBoosts#be77b4a = premium.MyBoosts;
|
||||
premium.applyBoost#6b7da746 flags:# slots:flags.0?Vector<int> peer:InputPeer = premium.MyBoosts;
|
||||
premium.getBoostsStatus#42f1f61 peer:InputPeer = premium.BoostsStatus;
|
||||
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;`;
|
||||
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;`;
|
||||
|
||||
@ -280,6 +280,7 @@
|
||||
"channels.searchPosts",
|
||||
"channels.reportSpam",
|
||||
"channels.updatePaidMessagesPrice",
|
||||
"channels.toggleAutotranslation",
|
||||
"bots.getBotRecommendations",
|
||||
"bots.canSendMessage",
|
||||
"bots.allowSendMessage",
|
||||
|
||||
10
src/types/language.d.ts
vendored
10
src/types/language.d.ts
vendored
@ -1530,6 +1530,7 @@ export interface LangPair {
|
||||
'ValueGiftSortByNumber': undefined;
|
||||
'ResellGiftsNoFound': undefined;
|
||||
'ResellGiftsClearFilters': undefined;
|
||||
'AutomaticTranslation': undefined;
|
||||
}
|
||||
|
||||
export interface LangPairWithVariables<V = LangVariable> {
|
||||
@ -1773,6 +1774,9 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'VoipPeerIncompatible': {
|
||||
'user': V;
|
||||
};
|
||||
'NewDiscussionChatTitle': {
|
||||
'name': V;
|
||||
};
|
||||
'LastSeenTodayAt': {
|
||||
'time': V;
|
||||
};
|
||||
@ -2170,6 +2174,9 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'from': V;
|
||||
'title': V;
|
||||
};
|
||||
'ActionCreatedChatYou': {
|
||||
'title': V;
|
||||
};
|
||||
'ActionPaymentDone': {
|
||||
'amount': V;
|
||||
'user': V;
|
||||
@ -2488,9 +2495,6 @@ export interface LangPairWithVariables<V = LangVariable> {
|
||||
'ComposerTitleForwardFrom': {
|
||||
'users': V;
|
||||
};
|
||||
'NewDiscussionChatTitle': {
|
||||
'name': V;
|
||||
}
|
||||
}
|
||||
|
||||
export interface LangPairPlural {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user