Improved how we manage image caching.

master
Pacman Ghost 4 years ago
parent 898e34535d
commit e25d478f6a
  1. 41
      web/src/App.js
  2. 4
      web/src/ArticleSearchResult.js
  3. 3
      web/src/ArticleSearchResult2.js
  4. 4
      web/src/PublicationSearchResult.js
  5. 6
      web/src/PublicationSearchResult2.js
  6. 4
      web/src/PublisherSearchResult.js
  7. 3
      web/src/PublisherSearchResult2.js

@ -66,6 +66,14 @@ export class App extends React.Component
// This also has the nice side-effect of removing CORS issues :-/
this._flaskBaseUrl = "/api" ;
}
// NOTE: Managing publisher/publication/article images is a bit tricky, since they are accessed via a URL
// such as "/articles/images/123", so if the user uploads a new image, the browser has no way of knowing
// that it can't use what's in its cache and must get a new one. We can add something to the URL to force
// a reload (e.g. "?foo=" + Math.random()), but this forces the image to be reloaded *every* time, which is
// pretty inefficient.
// Instead, we track a unique cache-busting value for each image URL, and change it when necessary.
this._flaskImageUrlVersions = {} ;
}
render() {
@ -465,21 +473,38 @@ export class App extends React.Component
}
return url ;
}
makeFlaskImageUrl( type, imageId, force ) {
makeExternalDocUrl( url ) {
// generate a URL for an external document
if ( url.substr( 0, 2 ) === "$/" )
url = url.substr( 2 ) ;
return this.makeFlaskUrl( "/docs/" + encodeURIComponent(url) ) ;
}
makeFlaskImageUrl( type, imageId ) {
// generate an image URL for the Flask backend server
if ( ! imageId )
return null ;
let url = this.makeFlaskUrl( "/images/" + type + "/" + imageId ) ;
if ( force )
url += "?foo=" + Math.random() ; // FUDGE! To bypass the cache :-/
const key = this._makeFlaskImageKey( type, imageId ) ;
if ( ! this._flaskImageUrlVersions[ key ] ) {
// NOTE: It would be nice to only add this if necessary (i.e. the user has changed
// the image, thus requiring us to fetch the new image), but not doing so causes problems
// in a dev environment, since we are constantly changing things in the database
// outside the app (e.g. in tests) and the browser cache will get out of sync.
this.forceFlaskImageReload( type, imageId ) ;
}
url += "?v=" + this._flaskImageUrlVersions[key] ;
return url ;
}
makeExternalDocUrl( url ) {
// generate a URL for an external document
if ( url.substr( 0, 2 ) === "$/" )
url = url.substr( 2 ) ;
return this.makeFlaskUrl( "/docs/" + encodeURIComponent(url) ) ;
forceFlaskImageReload( type, imageId ) {
// bump the image's version#, which will force a new URL the next time makeFlaskImageUrl() is called
const key = this._makeFlaskImageKey( type, imageId ) ;
const version = this._flaskImageUrlVersions[ key ] ;
// NOTE: It would be nice to start at 1, but this causes problems in a dev environment, since
// we are constantly changing things in the database, and the browser cache will get out of sync.
this._flaskImageUrlVersions[ key ] = version ? version+1 : Math.floor(Date.now()/1000) ;
}
_makeFlaskImageKey( type, imageId ) { return type + ":" + imageId ; }
_onStartupTask( taskId ) {
// flag that the specified startup task has completed

@ -22,7 +22,7 @@ export class ArticleSearchResult extends React.Component
const display_subtitle = this.props.data[ "article_subtitle!" ] || this.props.data.article_subtitle ;
const display_snippet = this.props.data[ "article_snippet!" ] || this.props.data.article_snippet ;
const pub = gAppRef.caches.publications[ this.props.data.pub_id ] ;
const image_url = gAppRef.makeFlaskImageUrl( "article", this.props.data.article_image_id, true ) ;
const image_url = gAppRef.makeFlaskImageUrl( "article", this.props.data.article_image_id ) ;
// prepare the article's URL
let article_url = this.props.data.article_url ;
@ -202,6 +202,8 @@ export class ArticleSearchResult extends React.Component
// update the UI with the new details
applyUpdatedVals( this.props.data, newVals, resp.data.updated, refs ) ;
removeSpecialFields( this.props.data ) ;
if ( newVals.imageData )
gAppRef.forceFlaskImageReload( "article", newVals.article_id ) ;
this.forceUpdate() ;
if ( resp.data.warnings )
gAppRef.showWarnings( "The article was updated OK.", resp.data.warnings ) ;

@ -28,8 +28,7 @@ export class ArticleSearchResult2
// 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 imageUrl = gAppRef.makeFlaskImageUrl( "article", vals.article_id ) || "/force-404" ;
function onImageLoaded() { onReady() ; }
function onMissingImage() {
imageRef.src = "/images/placeholder.png" ;

@ -160,6 +160,8 @@ export class PublicationSearchResult extends React.Component
// update the UI with the new details
applyUpdatedVals( this.props.data, newVals, resp.data.updated, refs ) ;
removeSpecialFields( this.props.data ) ;
if ( newVals.imageData )
gAppRef.forceFlaskImageReload( "publication", newVals.pub_id ) ;
this.forceUpdate() ;
if ( resp.data.warnings )
gAppRef.showWarnings( "The publication was updated OK.", resp.data.warnings ) ;
@ -244,7 +246,7 @@ export class PublicationSearchResult extends React.Component
_makeDisplayName( allowAlternateContent ) { return PublicationSearchResult.makeDisplayName( this.props.data, allowAlternateContent ) ; }
static makeImageUrl( vals ) {
let image_url = gAppRef.makeFlaskImageUrl( "publication", vals.pub_image_id, true ) ;
let image_url = gAppRef.makeFlaskImageUrl( "publication", vals.pub_image_id ) ;
if ( ! image_url ) {
// check if the parent publisher has an image
if ( vals.publ_id ) {

@ -33,10 +33,8 @@ export class PublicationSearchResult2
let imageUrl ;
if ( initialVals )
imageUrl = imageRef.src ; // nb: leave whatever's there already
else {
imageUrl = gAppRef.makeFlaskUrl( "/images/publication/" + vals.pub_id ) ;
imageUrl += "?foo=" + Math.random() ; // FUDGE! To bypass the cache :-/
}
else
imageUrl = gAppRef.makeFlaskImageUrl( "publication", vals.pub_id ) || "/force-404" ;
function onImageLoaded() { onReady() ; }
function onMissingImage() {
imageRef.src = "/images/placeholder.png" ;

@ -20,7 +20,7 @@ export class PublisherSearchResult extends React.Component
// prepare the basic details
const display_name = this.props.data[ "publ_name!" ] || this.props.data.publ_name ;
const display_description = this.props.data[ "publ_description!" ] || this.props.data.publ_description ;
const image_url = gAppRef.makeFlaskImageUrl( "publisher", this.props.data.publ_image_id, true ) ;
const image_url = gAppRef.makeFlaskImageUrl( "publisher", this.props.data.publ_image_id ) ;
// prepare the publications
let pubs = [] ;
@ -112,6 +112,8 @@ export class PublisherSearchResult extends React.Component
// update the UI with the new details
applyUpdatedVals( this.props.data, newVals, resp.data.updated, refs ) ;
removeSpecialFields( this.props.data ) ;
if ( newVals.imageData )
gAppRef.forceFlaskImageReload( "publisher", newVals.publ_id ) ;
this.forceUpdate() ;
if ( resp.data.warnings )
gAppRef.showWarnings( "The publisher was updated OK.", resp.data.warnings ) ;

@ -23,8 +23,7 @@ export class PublisherSearchResult2
// initialize the image
let imageFilename=null, imageData=null ;
let imageRef=null, uploadImageRef=null, removeImageRef=null ;
let imageUrl = gAppRef.makeFlaskUrl( "/images/publisher/" + vals.publ_id ) ;
imageUrl += "?foo=" + Math.random() ; // FUDGE! To bypass the cache :-/
let imageUrl = gAppRef.makeFlaskImageUrl( "publisher", vals.publ_id ) || "/force-404" ;
function onImageLoaded() { onReady() ; }
function onMissingImage() {
imageRef.src = "/images/placeholder.png" ;

Loading…
Cancel
Save