2022-09-24 14:25:00 +03:00
import * as Sentry from '@sentry/ember' ;
2022-08-23 13:45:50 +03:00
import Component from '@glimmer/component' ;
import React , { Suspense } from 'react' ;
2022-10-17 11:31:55 +03:00
import ghostPaths from 'ghost-admin/utils/ghost-paths' ;
2022-09-16 17:13:50 +03:00
import { action } from '@ember/object' ;
2022-11-03 14:14:36 +03:00
import { inject } from 'ghost-admin/decorators/inject' ;
2022-08-23 13:45:50 +03:00
class ErrorHandler extends React . Component {
state = {
hasError : false
} ;
static getDerivedStateFromError ( ) {
return { hasError : true } ;
}
render ( ) {
if ( this . state . hasError ) {
return (
< p className = "koenig-react-editor-error" > Loading has failed . Try refreshing the browser ! < / p >
) ;
}
return this . props . children ;
}
}
const fetchKoenig = function ( ) {
let status = 'pending' ;
let response ;
const fetchPackage = async ( ) => {
2022-09-13 20:55:28 +03:00
if ( window [ '@tryghost/koenig-lexical' ] ) {
return window [ '@tryghost/koenig-lexical' ] ;
2022-08-23 13:45:50 +03:00
}
// the manual specification of the protocol in the import template string is
// required to work around ember-auto-import complaining about an unknown dynamic import
// during the build step
const GhostAdmin = window . GhostAdmin || window . Ember . Namespace . NAMESPACES . find ( ns => ns . name === 'ghost-admin' ) ;
2022-10-07 17:24:03 +03:00
const urlTemplate = GhostAdmin . _ _container _ _ . lookup ( 'service:config' ) . editor ? . url ;
const urlVersion = GhostAdmin . _ _container _ _ . lookup ( 'service:config' ) . editor ? . version ;
2022-09-13 13:30:07 +03:00
const url = new URL ( urlTemplate . replace ( '{version}' , urlVersion ) ) ;
2022-08-23 13:45:50 +03:00
if ( url . protocol === 'http:' ) {
await import ( ` http:// ${ url . host } ${ url . pathname } ` ) ;
} else {
await import ( ` https:// ${ url . host } ${ url . pathname } ` ) ;
}
2022-09-13 13:30:07 +03:00
return window [ '@tryghost/koenig-lexical' ] ;
2022-08-23 13:45:50 +03:00
} ;
const suspender = fetchPackage ( ) . then (
( res ) => {
status = 'success' ;
response = res ;
} ,
( err ) => {
status = 'error' ;
response = err ;
}
) ;
const read = ( ) => {
switch ( status ) {
case 'pending' :
throw suspender ;
case 'error' :
throw response ;
default :
return response ;
}
} ;
return { read } ;
} ;
const editorResource = fetchKoenig ( ) ;
const KoenigComposer = ( props ) => {
const { KoenigComposer : _KoenigComposer } = editorResource . read ( ) ;
return < _KoenigComposer { ... props } / > ;
} ;
const KoenigEditor = ( props ) => {
const { KoenigEditor : _KoenigEditor } = editorResource . read ( ) ;
return < _KoenigEditor { ... props } / > ;
} ;
export default class KoenigLexicalEditor extends Component {
2022-11-03 14:14:36 +03:00
@ inject config ;
2022-09-16 17:13:50 +03:00
@ action
onError ( error ) {
// ensure we're still showing errors in development
console . error ( error ) ; // eslint-disable-line
2022-10-07 17:24:03 +03:00
if ( this . config . sentry _dsn ) {
2022-09-16 17:13:50 +03:00
Sentry . captureException ( error , {
tags : {
lexical : true
}
} ) ;
}
// don't rethrow, Lexical will attempt to gracefully recover
}
2022-08-23 13:45:50 +03:00
ReactComponent = ( ) => {
2022-10-17 11:31:55 +03:00
const [ uploadProgressPercentage ] = React . useState ( 0 ) ; // not in use right now, but will need to decide how to handle the percentage state and pass to the Image Cards
// const uploadProgress = (event) => {
// const percentComplete = (event.loaded / event.total) * 100;
// setUploadProgressPercentage(percentComplete);
// };
async function imageUploader ( files ) {
function uploadToUrl ( formData , url ) {
return new Promise ( ( resolve , reject ) => {
const xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'POST' , url ) ;
// xhr.upload.onprogress = (event) => {
// uploadProgress(event);
// };
xhr . onload = ( ) => resolve ( xhr . response ) ;
xhr . onerror = ( ) => reject ( xhr . statusText ) ;
xhr . send ( formData ) ;
} ) ;
}
const formData = new FormData ( ) ;
formData . append ( 'file' , files [ 0 ] ) ;
const url = ` ${ ghostPaths ( ) . apiRoot } /images/upload/ ` ;
const response = await uploadToUrl ( formData , url ) ;
const dataset = JSON . parse ( response ) ;
const imageUrl = dataset ? . images ? . [ 0 ] . url ;
return {
src : imageUrl
} ;
}
2022-08-23 13:45:50 +03:00
return (
< div className = { [ 'koenig-react-editor' , this . args . className ] . filter ( Boolean ) . join ( ' ' ) } >
< ErrorHandler >
< Suspense fallback = { < p className = "koenig-react-editor-loading" > Loading editor ... < / p > } >
2022-11-03 14:14:36 +03:00
< KoenigComposer
initialEditorState = { this . args . lexical }
onError = { this . onError }
2022-10-17 11:31:55 +03:00
imageUploadFunction = { { imageUploader , uploadProgressPercentage } } >
2022-09-13 23:01:53 +03:00
< KoenigEditor onChange = { this . args . onChange } / >
2022-08-23 13:45:50 +03:00
< / K o e n i g C o m p o s e r >
< / S u s p e n s e >
< / E r r o r H a n d l e r >
< / d i v >
) ;
} ;
}