import { useEffect , useState } from 'react' ; import { KeyWithTranslationsModel , LanguageModel } from '@tolgee/core' ; import { ComponentDependencies } from './KeyDialog' ; import { sleep } from '../tools/sleep' ; import { createProvider } from '../tools/createProvider' ; export interface ScreenshotInterface { id : number ; filename : string ; fileUrl : string ; createdAt ? : string ; justUploaded : boolean ; } type FormTranslations = { [ key ] : string ; } ; interface TranslationInterface { text ? : string ; } type DialogProps = { input : string ; defaultValue : string ; open : boolean ; onClose : ( ) => void ; dependencies : ComponentDependencies ; } ; const responseToTranslationData = ( data ) : FormTranslations => { const translations = { } ; if ( data ) { Object . entries ( data ) . forEach ( ( [ language , translation ] ) => ( translations [ language ] = translation . text ) ) ; } return translations ; } ; type State = { type : 'ON_INPUT_CHANGE' ; payload : { key : string ; value : string ; } ; } | { type : 'ON_SELECTED_LANGUAGES_CHANGE' ; payload : { languages : Set < string > ; } ; } | { type : 'LOAD_TRANSLATIONS' ; payload : { reinitialize ? : boolean ; languages : Set < string > ; } ; } | { type : 'HANDLE_UPLOAD_IMAGES' ; payload : { files : File [ ] ; } ; } | { type : 'HANDLE_TAKE_SCREENSHOT' ; } | { type : 'HANDLE_REMOVE_SCREENSHOT' ; payload : { id : number ; } ; } | { type : 'ON_SAVE' ; } | { type : 'ON_CLOSE' ; } | { type : 'SET_USE_BROWSER_WINDOW' ; payload : boolean ; } | { type : 'SET_CONTAINER' ; payload : Element ; } ; export const [ DialogProvider , useDialogDispatch , useDialogContext ] = createProvider ( ( props ) => { const [ loading , setLoading ] = useState < boolean > ( true ) ; const [ saving , setSaving ] = useState < boolean > ( false ) ; const [ success , setSuccess ] = useState < boolean > ( false ) ; const [ error , setError ] = useState < string > ( null ) ; const [ takingScreenshot , setTakingScreenshot ] = useState < boolean > ( false ) ; const [ translations , setTranslations ] = useState < KeyWithTranslationsModel > ( null ) ; const [ translationsForm , setTranslationsForm ] = useState < FormTranslations > ( null ) ; const [ translationsFormTouched , setTranslationsFormTouched ] = useState ( false ) ; const coreService = props . dependencies . coreService ; const properties = props . dependencies . properties ; const linkToPlatform = properties . projectId !== undefined ? `${ properties . config . apiUrl }/projects/${ properties . projectId }/translations/single?key=${ props . input }` : undefined ; const translationService = props . dependencies . translationService ; const screenshotService = props . dependencies . screenshotService ; const [ container , setContainer ] = useState ( undefined as Element | undefined ) ; const [ useBrowserWindow , setUseBrowserWindow ] = useState ( false ) ; const [ screenshots , setScreenshots ] = useState < ScreenshotInterface [ ] > ( [ ] ) ; const [ screenshotsUploading , setScreenshotsUploading ] = useState ( false ) ; const dispatch = async ( action ) => { switch ( action . type ) { case 'ON_INPUT_CHANGE' : setSuccess ( false ) ; setTranslationsFormTouched ( true ) ; setTranslationsForm ( { ... translationsForm , [ action . payload . key ] : action . payload . value , } ) ; break ; case 'LOAD_TRANSLATIONS' : { const { reinitialize = true , languages } = action . payload ; translationService . getTranslationsOfKey ( props . input , languages ) . then ( ( [ result , languages ] ) => { setTranslations ( result || { keyId : undefined , translations : { } , screenshots : [ ] , keyName : props . input , keyTags : [ ] , screenshotCount : 0 , } ) ; if ( ! selectedLanguages ?. size ) { setSelectedLanguages ( new Set ( languages ) ) ; } const translationsData = responseToTranslationData ( result ?. translations ) ; if ( ! translationsForm || reinitialize ) { setTranslationsForm ( translationsData ) ; setScreenshots ( result ?. screenshots ?. map ( ( sc ) => ( { ... sc , justUploaded : false , } ) ) || [ ] ) ; } else { const result = { } ; languages . forEach ( ( lng ) => { result [ lng ] = translationsForm [ lng ] || translationsData [ lng ] ; } ) ; setTranslationsForm ( result ) ; } setLoading ( false ) ; } ) ; break ; } case 'HANDLE_UPLOAD_IMAGES' : setScreenshotsUploading ( true ) ; await Promise . all ( action . payload . files . map ( uploadImage ) ) ; setScreenshotsUploading ( false ) ; break ; case 'HANDLE_TAKE_SCREENSHOT' : { setTakingScreenshot ( true ) ; const data = await props . dependencies . pluginManager . takeScreenshot ( { key : props . input , translations : translationsForm , } ) ; setTakingScreenshot ( false ) ; setScreenshotsUploading ( true ) ; const blob = await fetch ( data ) . then ( ( r ) => r . blob ( ) ) ; await uploadImage ( blob ) ; setScreenshotsUploading ( false ) ; break ; } case 'HANDLE_REMOVE_SCREENSHOT' : { const { id } = action . payload ; const screenshot = screenshots . find ( ( sc ) => sc . id === id ) ; if ( screenshot . justUploaded ) { screenshotService . deleteImages ( [ screenshot . id ] ) ; } setScreenshots ( screenshots . filter ( ( sc ) => sc . id !== id ) ) ; break ; } case 'ON_SAVE' : { setSaving ( true ) ; try { if ( translations . keyId === undefined ) { await translationService . createKey ( { name : props . input , translations : translationsForm , screenshotUploadedImageIds : screenshots . map ( ( sc ) => sc . id ) , } ) ; } else { await translationService . updateKeyComplex ( translations . keyId , { name : props . input , translations : translationsForm , screenshotIdsToDelete : getRemovedScreenshots ( ) , screenshotUploadedImageIds : getJustUploadedScreenshots ( ) , } ) ; } setSuccess ( true ) ; await sleep ( 200 ) ; setError ( null ) ; props . onClose ( ) ; } catch ( e ) { setError ( 'Unexpected error occurred :(' ) ; console . error ( e ) ; } finally { setSaving ( false ) ; } break ; } case 'ON_CLOSE' : { props . onClose ( ) ; setUseBrowserWindow ( false ) ; const uploadedScreenshots = getJustUploadedScreenshots ( ) ; if ( uploadedScreenshots . length ) { screenshotService . deleteImages ( uploadedScreenshots ) ; } setScreenshots ( [ ] ) ; break ; } case 'ON_SELECTED_LANGUAGES_CHANGE' : { const { languages } = action . payload ; if ( languages . size ) { setSelectedLanguages ( languages ) ; properties . preferredLanguages = languages ; dispatch ( { type : 'LOAD_TRANSLATIONS' , payload : { languages , reinitialize : false } , } ) ; } break ; } case 'SET_CONTAINER' : setContainer ( action . payload ) ; break ; case 'SET_USE_BROWSER_WINDOW' : setUseBrowserWindow ( action . payload ) ; break ; } } ; useEffect ( ( ) => { const onKeyDown = ( e ) => { if ( e . key === 'Escape' ) { dispatch ( { type : 'ON_CLOSE' } ) ; } } ; if ( ! useBrowserWindow ) { window . addEventListener ( 'keydown' , onKeyDown ) ; return ( ) => { window . removeEventListener ( 'keydown' , onKeyDown ) ; } ; } } , [ useBrowserWindow ] ) ; useEffect ( ( ) => { if ( props . open ) { setLoading ( true ) ; setSuccess ( false ) ; setError ( null ) ; setTranslationsFormTouched ( false ) ; dispatch ( { type : 'LOAD_TRANSLATIONS' , payload : { languages : properties . preferredLanguages } , } ) ; if ( availableLanguages === undefined ) { coreService . getLanguagesFull ( ) . then ( ( l ) => { setAvailableLanguages ( l ) ; } ) ; } } } , [ props . open , useBrowserWindow , props . input ] ) ; const getJustUploadedScreenshots = ( ) => { return screenshots . filter ( ( sc ) => sc . justUploaded ) . map ( ( sc ) => sc . id ) ; } ; const getRemovedScreenshots = ( ) => { return translations . screenshots ?. map ( ( sc ) => sc . id ) . filter ( ( scId ) => ! screenshots . find ( ( sc ) => sc . id === scId ) ) ; } ; const uploadImage = async ( file ) => { await screenshotService . uploadImage ( file ) . then ( ( data ) => setScreenshots ( ( screenshots ) => [ ... screenshots , { ... data , justUploaded : true } , ] ) ) . catch ( ( e ) => { setError ( e . code || e . message ) ; } ) ; } ; const formDisabled = loading || ( translations . keyId ? ! coreService . isAuthorizedTo ( 'translations.edit' ) : ! coreService . isAuthorizedTo ( 'keys.edit' ) ) ; const [ availableLanguages , setAvailableLanguages ] = useState < LanguageModel [ ] > ( undefined ) ; const [ selectedLanguages , setSelectedLanguages ] = useState ( properties . preferredLanguages || new Set ( [ properties . currentLanguage ] ) ) ; useEffect ( ( ) => { if ( props . defaultValue && availableLanguages && selectedLanguages && translationsForm ) { const baseLanguageDefinition = availableLanguages . find ( ( l ) => l . base ) ; if ( baseLanguageDefinition && selectedLanguages . has ( baseLanguageDefinition . tag ) && ! translationsFormTouched ) { const wasBaseTranslationProvided = translations . translations [ baseLanguageDefinition . tag ] !== undefined ; if ( ! translationsForm [ baseLanguageDefinition . tag ] && ! wasBaseTranslationProvided ) { setTranslationsForm ( { ... translationsForm , [ baseLanguageDefinition . tag ] : props . defaultValue , } ) ; } } } } , [ availableLanguages , translationsForm , selectedLanguages , props . defaultValue , ] ) ; const contextValue = { input : props . input , dependencies : props . dependencies , open : props . open , loading , saving , success , error , availableLanguages , selectedLanguages , formDisabled , translations , translationsForm , container , useBrowserWindow , pluginAvailable : props . dependencies . pluginManager . handshakeSucceed , takingScreenshot , screenshotsUploading , screenshots , linkToPlatform , } ; return [ contextValue , dispatch ] ; } ) ;