import React from "react" ;
import Select from "react-select" ;
import CreatableSelect from "react-select/creatable" ;
import { NEW _ARTICLE _PUB _PRIORITY _CUTOFF } from "./constants.js" ;
import { PublicationSearchResult } from "./PublicationSearchResult.js" ;
import { gAppRef } from "./index.js" ;
import { ImageFileUploader } from "./FileUploader.js" ;
import { makeScenarioDisplayName , parseScenarioDisplayName , sortSelectableOptions , unloadCreatableSelect } from "./utils.js" ;
// --------------------------------------------------------------------
export class ArticleSearchResult2
{
static _doEditArticle ( vals , notify ) {
// initialize
let refs = { } ;
const isNew = Object . keys ( vals ) . length === 0 ;
// initialize the image
let imageFilename = null , imageData = null ;
let imageRef = null , uploadImageRef = null , removeImageRef = null ;
let imageUrl = gAppRef . makeFlaskUrl ( "/images/article/" + vals . article _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 article 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 publications
let publications = [ { value : null , label : < i > ( none ) < / i > } ] ;
let mostRecentPub = null ;
for ( let p of Object . entries ( gAppRef . caches . publications ) ) {
const pub _display _name = PublicationSearchResult . makeDisplayName ( p [ 1 ] ) ;
const pub = {
value : p [ 1 ] . pub _id ,
label : < span dangerouslySetInnerHTML = { { _ _html : pub _display _name } } / > ,
} ;
publications . push ( pub ) ;
if ( mostRecentPub === null || p [ 1 ] . time _created > mostRecentPub [ 1 ] )
mostRecentPub = [ pub , p [ 1 ] . time _created ] ;
}
sortSelectableOptions ( publications ) ;
if ( isNew && mostRecentPub ) {
// NOTE: If the user is creating a new article, we check for the most recently-created publication
// and put that at the the top of list. This makes things easier in the most common use-case:
// the user has received a new magazine and is entering all the articles from it.
const now = new Date ( ) / 1000 | 0 ;
const delta = now - mostRecentPub [ 1 ] ; // nb: we ignore server/client time zones
if ( delta <= NEW _ARTICLE _PUB _PRIORITY _CUTOFF ) {
publications = publications . filter ( p => p !== mostRecentPub [ 0 ] ) ;
publications . splice ( 1 , 0 , mostRecentPub [ 0 ] ) ;
}
}
let currPub = publications [ 0 ] ;
for ( let i = 1 ; i < publications . length ; ++ i ) {
if ( publications [ i ] . value === vals . pub _id ) {
currPub = publications [ i ] ;
break ;
}
}
// initialize the authors
let allAuthors = [ ] ;
for ( let a of Object . entries ( gAppRef . caches . authors ) )
allAuthors . push ( { value : a [ 1 ] . author _id , label : a [ 1 ] . author _name } ) ;
allAuthors . sort ( ( lhs , rhs ) => { return lhs . label . localeCompare ( rhs . label ) ; } ) ;
let currAuthors = [ ] ;
if ( vals . article _authors ) {
currAuthors = vals . article _authors . map ( a => {
return { value : a , label : gAppRef . caches . authors [ a ] . author _name }
} ) ;
}
// initialize the scenarios
let allScenarios = [ ] ;
for ( let s of Object . entries ( gAppRef . caches . scenarios ) )
allScenarios . push ( { value : s [ 1 ] . scenario _id , label : makeScenarioDisplayName ( s [ 1 ] ) } ) ;
allScenarios . sort ( ( lhs , rhs ) => { return lhs . label . localeCompare ( rhs . label ) ; } ) ;
let currScenarios = [ ] ;
if ( vals . article _scenarios ) {
currScenarios = vals . article _scenarios . map ( s => {
return { value : s , label : makeScenarioDisplayName ( gAppRef . caches . scenarios [ s ] ) }
} ) ;
}
// initialize the tags
const tags = gAppRef . makeTagLists ( vals . article _tags ) ;
// prepare the form content
/* eslint-disable jsx-a11y/img-redundant-alt */
const content = < div >
< div className = "image-container" >
< div className = "row image" >
< img src = { imageUrl } className = "image" onError = { onMissingImage } onClick = { ( ) => onUploadImage ( null ) } ref = { r => imageRef = r } alt = "Upload image." title = "Click to upload an image for this article." / >
< img src = "/images/delete.png" className = "remove-image" onClick = { onRemoveImage } ref = { r => removeImageRef = r } alt = "Remove image." title = "Remove the article's image." / >
< input type = "file" accept = "image/*" onChange = { onUploadImage } style = { { display : "none" } } ref = { r => uploadImageRef = r } / >
< / d i v >
< / d i v >
< div className = "row title" > < label className = "top" > Title : < / l a b e l >
< input type = "text" defaultValue = { vals . article _title } autoFocus ref = { r => refs . article _title = r } / >
< / d i v >
< div className = "row subtitle" > < label className = "top" > Subtitle : < / l a b e l >
< input type = "text" defaultValue = { vals . article _subtitle } ref = { r => refs . article _subtitle = r } / >
< / d i v >
< div className = "row publication" > < label className = "select top" > Publication : < / l a b e l >
< Select className = "react-select" classNamePrefix = "react-select" options = { publications } isSearchable = { true }
defaultValue = { currPub }
ref = { r => refs . pub _id = r }
/ >
< input className = "pageno" type = "text" defaultValue = { vals . article _pageno } ref = { r => refs . article _pageno = r } title = "Page number." / >
< / d i v >
< div className = "row snippet" > < label > Snippet : < / l a b e l >
< textarea defaultValue = { vals . article _snippet } ref = { r => refs . article _snippet = r } / >
< / d i v >
< div className = "row authors" > < label className = "select" > Authors : < / l a b e l >
< CreatableSelect className = "react-select" classNamePrefix = "react-select" options = { allAuthors } isMulti
defaultValue = { currAuthors }
ref = { r => refs . article _authors = r }
/ >
< / d i v >
< div className = "row scenarios" > < label className = "select" > Scenarios : < / l a b e l >
< CreatableSelect className = "react-select" classNamePrefix = "react-select" options = { allScenarios } isMulti
defaultValue = { currScenarios }
ref = { r => refs . article _scenarios = r }
/ >
< / d i v >
< div className = "row tags" > < label className = "select" > Tags : < / l a b e l >
< CreatableSelect className = "react-select" classNamePrefix = "react-select" options = { tags [ 1 ] } isMulti
defaultValue = { tags [ 0 ] }
ref = { r => refs . article _tags = r }
/ >
< / d i v >
< div className = "row url" > < label > Web : < / l a b e l >
< input type = "text" defaultValue = { vals . article _url } ref = { r => refs . article _url = r } / >
< / d i v >
< / d i v > ;
// prepare the form buttons
const buttons = {
OK : ( ) => {
// unload the new values
let newVals = { } ;
for ( let r in refs ) {
if ( r === "pub_id" )
newVals [ r ] = refs [ r ] . state . value && refs [ r ] . state . value . value ;
else if ( r === "article_authors" ) {
let vals = unloadCreatableSelect ( refs [ r ] ) ;
newVals . article _authors = [ ] ;
vals . forEach ( v => {
if ( v . _ _isNew _ _ )
newVals . article _authors . push ( v . label ) ; // nb: string = new author name
else
newVals . article _authors . push ( v . value ) ; // nb: integer = existing author ID
} ) ;
} else if ( r === "article_scenarios" ) {
let vals = unloadCreatableSelect ( refs [ r ] ) ;
newVals . article _scenarios = [ ] ;
vals . forEach ( v => {
if ( v . _ _isNew _ _ )
newVals . article _scenarios . push ( parseScenarioDisplayName ( v . label ) ) ; // nb: array = new scenario
else
newVals . article _scenarios . push ( v . value ) ; // nb: integer = existing scenario ID
} ) ;
} else if ( r === "article_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 . article _title === "" ) {
gAppRef . showErrorMsg ( < div > Please specify the article ' s title . < / d i v > ) ;
return ;
}
// notify the caller about the new details
notify ( newVals , refs ) ;
} ,
Cancel : ( ) => { gAppRef . closeModalForm ( ) ; } ,
} ;
// show the form
gAppRef . showModalForm ( "article-form" , isNew ? "New article" : "Edit article" , "#d3edfc" , content , buttons ) ;
}
}