From 9c4d80052fd9d9b0f7b63c4e956bdfcc4afb5cb6 Mon Sep 17 00:00:00 2001 From: ZecD Date: Fri, 18 Oct 2024 15:47:46 +0200 Subject: [PATCH 1/4] WIP UseTranslationHook --- .../tools/hooks/useTranslationTools.tsx | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/pages/Video/components/tools/hooks/useTranslationTools.tsx diff --git a/src/pages/Video/components/tools/hooks/useTranslationTools.tsx b/src/pages/Video/components/tools/hooks/useTranslationTools.tsx new file mode 100644 index 000000000..68b97a32d --- /dev/null +++ b/src/pages/Video/components/tools/hooks/useTranslationTools.tsx @@ -0,0 +1,44 @@ +import { useParams } from 'react-router-dom'; +import { FEATURE_FLAG_AI_TRANSLATION } from 'src/constants'; +import { useFeatureFlag } from 'src/hooks/useFeatureFlag'; +import { + useGetVideosByVidQuery, + useGetVideosByVidTranslationQuery, +} from 'src/features/api'; +import { usePreferredLanguage } from '../usePreferredLanguage'; +import { useToolsContext } from '../context/ToolsContext'; + +export const useTranslationTools = () => { + const { videoId } = useParams(); + const { hasFeatureFlag } = useFeatureFlag(); + const hasAIFeatureFlag = hasFeatureFlag(FEATURE_FLAG_AI_TRANSLATION); + const preferredLanguage = usePreferredLanguage(); + const { isOpen, setIsOpen, language, setLanguage } = useToolsContext(); + + const { data: translation, isLoading: isLoadingTranslation } = + useGetVideosByVidTranslationQuery( + { + vid: videoId || '', + ...(language && { lang: language }), + }, + { + skip: !hasAIFeatureFlag || !language || !preferredLanguage, + } + ); + const { data: video } = useGetVideosByVidQuery({ + vid: videoId || '', + }); + + const canTranslate = + !!preferredLanguage && + video && + video.language.localeCompare(preferredLanguage) !== 0 && + (!translation || + translation.language.localeCompare(preferredLanguage) !== 0); + const isTranslating = + translation && + translation.language === preferredLanguage && + translation.processing === 1; + + const isProcessing = isLoadingTranslation || translation?.processing === 1; +}; From e825569b70467afb8855e1aeb26fbbd7c03b5f88 Mon Sep 17 00:00:00 2001 From: ZecD Date: Fri, 18 Oct 2024 17:07:01 +0200 Subject: [PATCH 2/4] feat: Add translation progress bar label - Added a new translation progress bar label in the Italian translation file. - Updated the TranslationLoader component to display the translation progress bar label using the translation hook. Refactor: Remove unnecessary code and fix flex-direction - Removed an unnecessary console.log statement in the Transcript Header component. - Fixed the flex-direction issue in the Transcript Header component. fix: Fix loading issue in TranslationLoader component - Fixed a loading issue in the TranslationLoader component. feat: Change behavior of translation buttons - Implemented changes to the behavior of translation buttons. Refactor: Update useTranslationTools hook - Updated the useTranslationTools hook to handle quick translation and preferred language. - Added logic to determine if quick translation is available and if the video is already translated in the preferred language. --- src/locales/it/translation.json | 1 + .../Transcript/TranslationLoader.tsx | 28 +--- .../Video/components/Transcript/index.tsx | 31 +--- .../tools/hooks/useTranslationTools.tsx | 93 ++++++++---- src/pages/Video/components/tools/index.tsx | 136 ++++++++---------- 5 files changed, 135 insertions(+), 154 deletions(-) diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index 06e57237c..6251eb15d 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -970,6 +970,7 @@ "__TOOLS_TRANSLATE_TOAST_LANGUAGE_SUCCESS_MESSAGE": "", "__TOOLS_TRANSLATE_TOAST_SUCCESS_MESSAGE": "", "__TOOLS_TRANSLATE_TOGGLE_TEXT": "", + "__TOOLS_TRANSLATE_PROGRESS_BAR_LABEL": "Traduzione in corso...", "__UX_CAMPAIGN_PAGE_NAVIGATION_DASHBOARD_TOOLTIP": "Dashboard", "__UX_CAMPAIGN_PAGE_NAVIGATION_INSIGHTS_TOOLTIP": "Temi e scoperte", "__UX_CAMPAIGN_PAGE_NAVIGATION_VIDEO_LIST_TOOLTIP": "Lista Video", diff --git a/src/pages/Video/components/Transcript/TranslationLoader.tsx b/src/pages/Video/components/Transcript/TranslationLoader.tsx index 36db11bdc..2fa131ebf 100644 --- a/src/pages/Video/components/Transcript/TranslationLoader.tsx +++ b/src/pages/Video/components/Transcript/TranslationLoader.tsx @@ -1,34 +1,18 @@ import { Skeleton, SM } from '@appquality/unguess-design-system'; -import { useParams } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; -import { useFeatureFlag } from 'src/hooks/useFeatureFlag'; -import { useGetVideosByVidTranslationQuery } from 'src/features/api'; import styled from 'styled-components'; -import { FEATURE_FLAG_AI_TRANSLATION } from 'src/constants'; -import { useToolsContext } from '../tools/context/ToolsContext'; -import { usePreferredLanguage } from '../tools/usePreferredLanguage'; +import { useTranslation } from 'react-i18next'; +import { useTranslationTools } from '../tools/hooks/useTranslationTools'; const LoaderWrapper = styled.div` margin-top: ${appTheme.space.xs}; `; export const TranslationLoader = () => { - const { videoId } = useParams(); - const { language } = useToolsContext(); - const { hasFeatureFlag } = useFeatureFlag(); - const hasAIFeatureFlag = hasFeatureFlag(FEATURE_FLAG_AI_TRANSLATION); - const preferredLanguage = usePreferredLanguage(); - const { data } = useGetVideosByVidTranslationQuery( - { - vid: videoId || '', - ...(language && { lang: language }), - }, - { - skip: !hasAIFeatureFlag || !language || !preferredLanguage, - } - ); + const { isProcessing } = useTranslationTools(); + const { t } = useTranslation(); - if (!data?.processing) return null; + if (!isProcessing) return null; return ( @@ -37,7 +21,7 @@ export const TranslationLoader = () => { color={appTheme.palette.grey[700]} style={{ marginBottom: appTheme.space.xxs }} > - Translation in progress.... + {t('__TOOLS_TRANSLATE_PROGRESS_BAR_LABEL')} void; }) => { - const { language } = useToolsContext(); - - const { hasFeatureFlag } = useFeatureFlag(); - const hasAIFeatureFlag = hasFeatureFlag(FEATURE_FLAG_AI_TRANSLATION); - const handleAddObservation = useAddObservation({ videoId: videoId || '' }); - const { data: content, speakers } = useContent(videoId || ''); - const { data: observations } = useObservations(videoId || ''); - - const { data: translation } = useGetVideosByVidTranslationQuery( - { - vid: videoId || '', - ...(language && { lang: language }), - }, - { - skip: !hasAIFeatureFlag || !language, - } - ); + const { data: translationData } = useTranslationTools(); const editor = TranscriptComponent.useEditor( { currentTime: currentTime * 1000, onSetCurrentTime: (time) => setCurrentTime(time), content, - translations: translation?.sentences, + translations: translationData.translation?.sentences, themeExtension: TranscriptTheme, observations, // @ts-ignore numberOfSpeakers: speakers, }, - [observations, translation?.sentences] + [observations, translationData.translation?.sentences] ); return ( diff --git a/src/pages/Video/components/tools/hooks/useTranslationTools.tsx b/src/pages/Video/components/tools/hooks/useTranslationTools.tsx index 68b97a32d..36dfbff68 100644 --- a/src/pages/Video/components/tools/hooks/useTranslationTools.tsx +++ b/src/pages/Video/components/tools/hooks/useTranslationTools.tsx @@ -2,43 +2,82 @@ import { useParams } from 'react-router-dom'; import { FEATURE_FLAG_AI_TRANSLATION } from 'src/constants'; import { useFeatureFlag } from 'src/hooks/useFeatureFlag'; import { + GetVideosByVidTranslationApiResponse, useGetVideosByVidQuery, useGetVideosByVidTranslationQuery, } from 'src/features/api'; +import { useEffect, useState } from 'react'; import { usePreferredLanguage } from '../usePreferredLanguage'; import { useToolsContext } from '../context/ToolsContext'; export const useTranslationTools = () => { const { videoId } = useParams(); + const [data, setData] = useState<{ + hasQuickTranslate?: boolean; + preferredLanguage?: string; + translation?: GetVideosByVidTranslationApiResponse; + canQuickTranslate?: boolean; + }>({ + hasQuickTranslate: false, + canQuickTranslate: true, + }); + const { hasFeatureFlag } = useFeatureFlag(); const hasAIFeatureFlag = hasFeatureFlag(FEATURE_FLAG_AI_TRANSLATION); const preferredLanguage = usePreferredLanguage(); - const { isOpen, setIsOpen, language, setLanguage } = useToolsContext(); - - const { data: translation, isLoading: isLoadingTranslation } = - useGetVideosByVidTranslationQuery( - { - vid: videoId || '', - ...(language && { lang: language }), - }, - { - skip: !hasAIFeatureFlag || !language || !preferredLanguage, - } - ); - const { data: video } = useGetVideosByVidQuery({ - vid: videoId || '', - }); + const { language } = useToolsContext(); + + const { + data: video, + isLoading: isLoadingVideo, + isError: isErrorVideo, + } = useGetVideosByVidQuery( + { + vid: videoId || '', + }, + { + skip: !videoId, + } + ); + + const { + data: translation, + isLoading: isLoadingTranslation, + isError: isErrorTranslation, + } = useGetVideosByVidTranslationQuery( + { + vid: videoId || '', + ...(language && { lang: language }), + }, + { + skip: !hasAIFeatureFlag || !videoId || !language, + } + ); + + useEffect(() => { + if (video) { + const hasQuickTranslate = + !!preferredLanguage && // Exists a preferred language + video.language.localeCompare(preferredLanguage) !== 0; // Video language is different from preferred language + + const isPrefTranslated = !!( + preferredLanguage && + translation?.language.localeCompare(preferredLanguage) === 0 + ); + + setData({ + hasQuickTranslate, + preferredLanguage, + canQuickTranslate: hasQuickTranslate && !isPrefTranslated, + translation, + }); + } + }, [video, translation, preferredLanguage]); - const canTranslate = - !!preferredLanguage && - video && - video.language.localeCompare(preferredLanguage) !== 0 && - (!translation || - translation.language.localeCompare(preferredLanguage) !== 0); - const isTranslating = - translation && - translation.language === preferredLanguage && - translation.processing === 1; - - const isProcessing = isLoadingTranslation || translation?.processing === 1; + return { + isError: !hasAIFeatureFlag || isErrorVideo || isErrorTranslation, + isProcessing: + isLoadingVideo || isLoadingTranslation || translation?.processing === 1, + data, + }; }; diff --git a/src/pages/Video/components/tools/index.tsx b/src/pages/Video/components/tools/index.tsx index 460ed3380..eb9457467 100644 --- a/src/pages/Video/components/tools/index.tsx +++ b/src/pages/Video/components/tools/index.tsx @@ -11,78 +11,40 @@ import { ReactComponent as SettingsIcon } from '@zendeskgarden/svg-icons/src/16/ import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router-dom'; import { appTheme } from 'src/app/theme'; -import { FEATURE_FLAG_AI_TRANSLATION } from 'src/constants'; -import { - useGetVideosByVidQuery, - useGetVideosByVidTranslationQuery, - usePostVideosByVidTranslationMutation, -} from 'src/features/api'; -import { useFeatureFlag } from 'src/hooks/useFeatureFlag'; +import { usePostVideosByVidTranslationMutation } from 'src/features/api'; import { ToolsTranslate } from './ToolsTranslate'; import { useToolsContext } from './context/ToolsContext'; -import { usePreferredLanguage } from './usePreferredLanguage'; +import { useTranslationTools } from './hooks/useTranslationTools'; export const Tools = () => { const { t } = useTranslation(); const { videoId } = useParams(); - const { isOpen, setIsOpen, language, setLanguage } = useToolsContext(); - const { hasFeatureFlag } = useFeatureFlag(); - const hasAIFeatureFlag = hasFeatureFlag(FEATURE_FLAG_AI_TRANSLATION); - const { addToast } = useToast(); - const [requestTranslation, { isLoading: isLoadingRequestTranslation }] = - usePostVideosByVidTranslationMutation(); - - const preferredLanguage = usePreferredLanguage(); - - const { data: translation, isLoading: isLoadingTranslation } = - useGetVideosByVidTranslationQuery( - { - vid: videoId || '', - ...(language && { lang: language }), - }, - { - skip: !hasAIFeatureFlag || !language || !preferredLanguage, - } - ); + const { isError, isProcessing, data } = useTranslationTools(); + const { isOpen, setIsOpen, setLanguage } = useToolsContext(); + const [requestTranslation] = usePostVideosByVidTranslationMutation(); - const { data: video } = useGetVideosByVidQuery({ - vid: videoId || '', - }); + if (isError) return null; - if (!hasAIFeatureFlag || isLoadingTranslation) return null; - const canTranslate = - !!preferredLanguage && - video && - video.language.localeCompare(preferredLanguage) !== 0 && - (!translation || - translation.language.localeCompare(preferredLanguage) !== 0); - const isTranslating = - translation && - translation.language === preferredLanguage && - translation.processing === 1; + if (data.hasQuickTranslate && data.preferredLanguage) { + // 2 buttons + return ( +
+ + {isOpen && ( + )} - - {isOpen && ( - - )} - {(preferredLanguage && - video?.language.localeCompare(preferredLanguage) !== 0) || - isTranslating ? ( { setIsOpen(!isOpen); @@ -136,7 +89,32 @@ export const Tools = () => { > - ) : null} +
+ ); + } + + // 1 button + return ( +
+ + {isOpen && ( + + )}
); }; From f84ddde136047248edab113455b0bcfe56e3d828 Mon Sep 17 00:00:00 2001 From: ZecD Date: Fri, 18 Oct 2024 17:07:29 +0200 Subject: [PATCH 3/4] feat: Add translation progress bar label --- src/locales/en/translation.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index dc4a90af6..36f683ab2 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -940,6 +940,7 @@ "__TOOLS_TRANSLATE_TOAST_LANGUAGE_SUCCESS_MESSAGE": "Preferred language updated", "__TOOLS_TRANSLATE_TOAST_SUCCESS_MESSAGE": "Translation completed", "__TOOLS_TRANSLATE_TOGGLE_TEXT": "Set as preferred language", + "__TOOLS_TRANSLATE_PROGRESS_BAR_LABEL": "Translation in progress...", "__UX_CAMPAIGN_PAGE_NAVIGATION_DASHBOARD_TOOLTIP": "Dashboard", "__UX_CAMPAIGN_PAGE_NAVIGATION_INSIGHTS_TOOLTIP": "Topics & insights", "__UX_CAMPAIGN_PAGE_NAVIGATION_VIDEO_LIST_TOOLTIP": "Playlist", From d4601df83f40d6c3340cdfd48006c1275e7bb3eb Mon Sep 17 00:00:00 2001 From: "Luca Cannarozzo (@cannarocks)" Date: Mon, 21 Oct 2024 16:38:46 +0200 Subject: [PATCH 4/4] Refactor language selection logic in ToolsTranslate component --- .../Video/components/tools/ToolsTranslate.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pages/Video/components/tools/ToolsTranslate.tsx b/src/pages/Video/components/tools/ToolsTranslate.tsx index 04183aad0..29282234f 100644 --- a/src/pages/Video/components/tools/ToolsTranslate.tsx +++ b/src/pages/Video/components/tools/ToolsTranslate.tsx @@ -62,13 +62,9 @@ const ToolsTranslate = ({ currentLanguage }: { currentLanguage?: string }) => { (preference) => preference?.name === 'translations_language' ); - // Remove videoLanguage and current translation language from allowedLanguages - const filteredLanguages = allowedLanguages.filter( - (lang) => lang !== videoLanguage && lang !== currentLanguage - ); - useEffect(() => { - if (languagePreference?.value) { + const lang = languagePreference?.value; + if (lang && lang !== videoLanguage && lang !== currentLanguage) { setInternalLanguage(languagePreference.value); } }, [languagePreference]); @@ -101,13 +97,16 @@ const ToolsTranslate = ({ currentLanguage }: { currentLanguage?: string }) => { label={t('__TOOLS_TRANSLATE_LANGUAGE_DROPDOWN_LABEL')} startIcon={} placeholder={t('__TOOLS_TRANSLATE_LANGUAGE_DROPDOWN_PLACEHOLDER')} + onSelect={(value) => setInternalLanguage(value)} + selectionValue={internalLanguage} + inputValue={getLanguageNameByFullTag(internalLanguage) ?? ''} > - {filteredLanguages.map((lang) => ( + {allowedLanguages.map((lang) => ( ))}