import React from "react" ;
import ReactDOMServer from "react-dom/server" ;
import Select from "react-select" ;
import CreatableSelect from "react-select/creatable" ;
import { gAppRef } from "./index.js" ;
import { ImageFileUploader } from "./FileUploader.js" ;
import { makeOptionalLink , unloadCreatableSelect , pluralString , applyUpdatedVals } from "./utils.js" ;
const axios = require ( "axios" ) ;
// --------------------------------------------------------------------
export class PublicationSearchResult extends React . Component
{
render ( ) {
const publ = gAppRef . caches . publishers [ this . props . data . publ _id ] ;
let tags = [ ] ;
if ( this . props . data . pub _tags )
this . props . data . pub _tags . map ( t => tags . push ( < div key = { t } className = "tag" > { t } < / d i v > ) ) ;
return ( < div className = "search-result publication"
ref = { r => gAppRef . setTestAttribute ( r , "pub_id" , this . props . data . pub _id ) }
>
< div className = "name" > { makeOptionalLink ( this . _makeDisplayName ( ) , this . props . data . pub _url ) }
{ publ && < span className = "publisher" > ( { publ . publ _name } ) < / s p a n > }
< img src = "/images/edit.png" className = "edit" onClick = { this . onEditPublication . bind ( this ) } alt = "Edit this publication." / >
< img src = "/images/delete.png" className = "delete" onClick = { this . onDeletePublication . bind ( this ) } alt = "Delete this publication." / >
< / d i v >
< div className = "description" dangerouslySetInnerHTML = { { _ _html : this . props . data . pub _description } } / >
{ tags . length > 0 && < div className = "tags" > < label > Tags : < / l a b e l > { t a g s } < / d i v > }
< / d i v > ) ;
}
static onNewPublication ( notify ) {
PublicationSearchResult . _doEditPublication ( { } , ( newVals , refs ) => {
axios . post ( gAppRef . makeFlaskUrl ( "/publication/create" , { list : 1 } ) , newVals )
. then ( resp => {
// update the caches
gAppRef . caches . publications = resp . data . publications ;
gAppRef . caches . tags = resp . data . tags ;
// unload any updated values
applyUpdatedVals ( newVals , newVals , resp . data . updated , refs ) ;
// update the UI with the new details
notify ( resp . data . pub _id , newVals ) ;
if ( resp . data . warnings )
gAppRef . showWarnings ( "The new publication was created OK." , resp . data . warnings ) ;
else
gAppRef . showInfoToast ( < div > The new publication was created OK . < / d i v > ) ;
gAppRef . closeModalForm ( ) ;
} )
. catch ( err => {
gAppRef . showErrorMsg ( < div > Couldn ' t create the publication : < div className = "monospace" > { err . toString ( ) } < / d i v > < / d i v > ) ;
} ) ;
} ) ;
}
onEditPublication ( ) {
PublicationSearchResult . _doEditPublication ( this . props . data , ( newVals , refs ) => {
// send the updated details to the server
newVals . pub _id = this . props . data . pub _id ;
axios . post ( gAppRef . makeFlaskUrl ( "/publication/update" , { list : 1 } ) , newVals )
. then ( resp => {
// update the caches
gAppRef . caches . publications = resp . data . publications ;
gAppRef . caches . tags = resp . data . tags ;
// update the UI with the new details
applyUpdatedVals ( this . props . data , newVals , resp . data . updated , refs ) ;
this . forceUpdate ( ) ;
if ( resp . data . warnings )
gAppRef . showWarnings ( "The publication was updated OK." , resp . data . warnings ) ;
else
gAppRef . showInfoToast ( < div > The publication was updated OK . < / d i v > ) ;
gAppRef . closeModalForm ( ) ;
} )
. catch ( err => {
gAppRef . showErrorMsg ( < div > Couldn ' t update the publication : < div className = "monospace" > { err . toString ( ) } < / d i v > < / d i v > ) ;
} ) ;
} ) ;
}
static _doEditPublication ( vals , notify ) {
let refs = { } ;
// initialize the image
let imageFilename = null , imageData = null ;
let imageRef = null , uploadImageRef = null , removeImageRef = null ;
let imageUrl = gAppRef . makeFlaskUrl ( "/images/publication/" + vals . pub _id ) ;
imageUrl += "?foo=" + Math . random ( ) ; // FUDGE! To bypass the cache :-/
let onMissingImage = ( evt ) => {
imageRef . src = "/images/placeholder.png" ;
removeImageRef . style . display = "none" ;
} ;
let onUploadImage = ( evt ) => {
if ( evt === null && ! gAppRef . isFakeUploads ( ) ) {
// nb: the publication image was clicked - trigger an upload request
uploadImageRef . click ( ) ;
return ;
}
let fileUploader = new ImageFileUploader ( ) ;
fileUploader . getFile ( evt , imageRef , removeImageRef , ( fname , data ) => {
imageFilename = fname ;
imageData = data ;
} ) ;
} ;
let onRemoveImage = ( ) => {
imageData = "{remove}" ;
imageRef . src = "/images/placeholder.png" ;
removeImageRef . style . display = "none" ;
} ;
// initialize the publishers
let publishers = [ { value : null , label : < i > ( none ) < / i > } ] ;
let currPubl = 0 ;
for ( let p of Object . entries ( gAppRef . caches . publishers ) ) {
publishers . push ( {
value : p [ 1 ] . publ _id ,
label : < span dangerouslySetInnerHTML = { { _ _html : p [ 1 ] . publ _name } } / >
} ) ;
if ( p [ 1 ] . publ _id === vals . publ _id )
currPubl = publishers . length - 1 ;
}
publishers . sort ( ( lhs , rhs ) => {
return ReactDOMServer . renderToStaticMarkup ( lhs . label ) . localeCompare ( ReactDOMServer . renderToStaticMarkup ( rhs . label ) ) ;
} ) ;
// initialize the tags
const tags = gAppRef . makeTagLists ( vals . pub _tags ) ;
// prepare the form content
/* eslint-disable jsx-a11y/img-redundant-alt */
const content = < div >
< div className = "row image" >
< img src = { imageUrl } className = "image" onError = { onMissingImage } onClick = { ( ) => onUploadImage ( null ) } ref = { r => imageRef = r } alt = "Click to upload an image for this publication." / >
< img src = "/images/delete.png" className = "remove-image" onClick = { onRemoveImage } ref = { r => removeImageRef = r } alt = "Remove the publication's image." / >
< input type = "file" accept = "image/*" onChange = { onUploadImage } style = { { display : "none" } } ref = { r => uploadImageRef = r } / >
< / d i v >
< div className = "row name" > < label > Name : < / l a b e l >
< input type = "text" defaultValue = { vals . pub _name } ref = { ( r ) => refs . pub _name = r } / >
< / d i v >
< div className = "row edition" > < label > Edition : < / l a b e l >
< input type = "text" defaultValue = { vals . pub _edition } ref = { ( r ) => refs . pub _edition = r } / >
< / d i v >
< div className = "row publisher" > < label > Publisher : < / l a b e l >
< Select className = "react-select" classNamePrefix = "react-select" options = { publishers } isSearchable = { true }
defaultValue = { publishers [ currPubl ] }
ref = { ( r ) => refs . publ _id = r }
/ >
< / d i v >
< div className = "row tags" > < label > Tags : < / l a b e l >
< CreatableSelect className = "react-select" classNamePrefix = "react-select" options = { tags [ 1 ] } isMulti
defaultValue = { tags [ 0 ] }
ref = { ( r ) => refs . pub _tags = r }
/ >
< / d i v >
< div className = "row description" > < label > Description : < / l a b e l >
< textarea defaultValue = { vals . pub _description } ref = { ( r ) => refs . pub _description = r } / >
< / d i v >
< div className = "row url" > < label > Web : < / l a b e l >
< input type = "text" defaultValue = { vals . pub _url } ref = { ( r ) => refs . pub _url = r } / >
< / d i v >
< / d i v > ;
const buttons = {
OK : ( ) => {
// unload the new values
let newVals = { } ;
for ( let r in refs ) {
if ( r === "publ_id" )
newVals [ r ] = refs [ r ] . state . value && refs [ r ] . state . value . value ;
else if ( r === "pub_tags" ) {
let vals = unloadCreatableSelect ( refs [ r ] ) ;
newVals [ r ] = vals . map ( v => v . label ) ;
} else
newVals [ r ] = refs [ r ] . value . trim ( ) ;
}
if ( imageData ) {
newVals . imageData = imageData ;
newVals . imageFilename = imageFilename ;
}
if ( newVals . pub _name === "" ) {
gAppRef . showErrorMsg ( < div > Please specify the publication ' s name . < / d i v > ) ;
return ;
}
// notify the caller about the new details
notify ( newVals , refs ) ;
} ,
Cancel : ( ) => { gAppRef . closeModalForm ( ) ; } ,
} ;
const isNew = Object . keys ( vals ) . length === 0 ;
gAppRef . showModalForm ( isNew ? "New publication" : "Edit publication" , content , buttons ) ;
}
onDeletePublication ( ) {
let doDelete = ( nArticles ) => {
// confirm the operation
let warning ;
if ( typeof nArticles === "number" ) {
if ( nArticles === 0 )
warning = < div > No articles will be deleted . < / d i v > ;
else
warning = < div > { pluralString ( nArticles , "associated article" ) + " will also be deleted." } < / d i v > ;
} else {
warning = ( < div > < img className = "icon" src = "/images/error.png" alt = "Error." / >
WARNING : Couldn ' t check if any associated articles will be deleted :
< div className = "monospace" > { nArticles . toString ( ) } < / d i v >
< / d i v > ) ;
}
const content = ( < div >
Delete this publication ?
< div style = { { margin : "0.5em 0 0.5em 2em" , fontStyle : "italic" } } dangerouslySetInnerHTML = { { _ _html : this . _makeDisplayName ( ) } } / >
{ warning }
< / d i v > ) ;
gAppRef . ask ( content , "ask" , {
"OK" : ( ) => {
// delete the publication on the server
axios . get ( gAppRef . makeFlaskUrl ( "/publication/delete/" + this . props . data . pub _id , { list : 1 } ) )
. then ( resp => {
// update the caches
gAppRef . caches . publications = resp . data . publications ;
gAppRef . caches . tags = resp . data . tags ;
// update the UI
this . props . onDelete ( "pub_id" , this . props . data . pub _id ) ;
resp . data . deleteArticles . forEach ( article _id => {
this . props . onDelete ( "article_id" , article _id ) ;
} ) ;
if ( resp . data . warnings )
gAppRef . showWarnings ( "The publication was deleted." , resp . data . warnings ) ;
else
gAppRef . showInfoToast ( < div > The publication was deleted . < / d i v > ) ;
} )
. catch ( err => {
gAppRef . showErrorToast ( < div > Couldn ' t delete the publication : < div className = "monospace" > { err . toString ( ) } < / d i v > < / d i v > ) ;
} ) ;
} ,
"Cancel" : null ,
} ) ;
}
// get the publication details
axios . get ( gAppRef . makeFlaskUrl ( "/publication/" + this . props . data . pub _id ) )
. then ( resp => {
doDelete ( resp . data . nArticles ) ;
} )
. catch ( err => {
doDelete ( err ) ;
} ) ;
}
_makeDisplayName ( ) {
if ( this . props . data . pub _edition )
return this . props . data . pub _name + " (" + this . props . data . pub _edition + ")" ;
else
return this . props . data . pub _name ;
}
}