Skip to content

Server Side Rendering

JP DeVries edited this page Mar 18, 2017 · 4 revisions

To demonstrate server side rendering via Node this example assumes you are using Express, Formidable, and some promises we will elaborate on that handle getting the media source data to be served.

getMediaSources

Your REST API needs to supply the media browser with JSON data for all available media source objects. Modify the promise returned by the getMediaSources function to do just that as needed.

getDirectoryListing

Your REST API needs to supply the media browser with JSON data for the current directory contents. Modify the promise returned by the getMediaSources function to do just that as needed.

import React from 'react';
import ReactDOM from 'react-dom';

import EurekaMediaBrowser from 'EurekaMediaBrowser';

function getMediaSources() {
  return new Promise((resolve, reject) => {
    /* this is where you get your media source information (data base query, whatever) and then return it with resolve() */
  })
}

function getDirectoryListing() {
  return new Promise((resolve, reject) => {
    /* this is where you get your directory listing information (data base query, whatever) and then return it with resolve() */
  })
}

function getEurekaPageMarkup(eurekaMarkup = '') {
  return (
    `<!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title data-site-name="Eureka Media Browser">Eureka Media Browser</title>
        <link rel="stylesheet" href="assets/css/components/eureka/eureka.2.0.0.min.css">
      </head>
      <body>
        <div id="root">${eurekaMarkup}</div>
      </body>
    </html>`
  );
}

function handleEurekaPostChosen(req, res, fields) {
  res.end(`<!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <title data-site-name="Eureka Media Browser">Eureka!</title>
    </head>
    <body>
      <h1>Eureka!</h1>
      <p>You chose ${path.join(fields['eureka__upload-dir'], fields.eureka__chosen_item)} of the ${name} (${cs}) media&nbsp;source.</p>
      <img src="/sources/filesystem/${path.join(fields['eureka__upload-dir'], fields.eureka__chosen_item)}" alt="/sources/filesystem/${path.join(fields['eureka__upload-dir'], fields.eureka__chosen_item)}" />
    </body>
  </html>`);
}


 /*___   __                                     ___      
/\  __`\/\ \                                   /\_ \     
\ \ \/\ \ \ \/'\             ___    ___     ___\//\ \    
 \ \ \ \ \ \ , <            /'___\ / __`\  / __`\\ \ \   
  \ \ \_\ \ \ \\`\   __    /\ \__//\ \L\ \/\ \L\ \\_\ \_
   \ \_____\ \_\ \_\/\ \   \ \____\ \____/\ \____//\____\
    \/_____/\/_/\/_/\ \/    \/____/\/___/  \/___/ \/____/
                     \/  You probably don't need to change the rest very much */

app.get('/eureka', (req, res) => {
  serveEurekaPage(req, res);
});

const allowMultipleUploads = true;
app.post('/eureka', (req, res) => {
  // using formidable because it rocks
  const form = new formidable.IncomingForm();
  form.multiples = allowMultipleUploads;

  form.parse(req, function(err, fields, files) { // parse the form
    const uploadFiles = files.eureka__uploadFiles, // Array of File Objects or a single File Object
    [cs, cd] = utility.parseMediaSourceOutOfCombinedPath(fields[`eureka__media-browser_${fields.eureka__mediaSourceId}__browsing`], '||'), // option values need to send both the media source id and the current directory so they are combined into a single string and parsed out on the server side
    uploadDir = path.join(__dirname, path.join('/sources/filesystem/', fields['eureka__upload-dir'])); // where we are going to be uploading files too

    function moveFile(file) {
      fs.renameSync(file.path, path.join(uploadDir, file.name))
    }

    // move the uploaded files into place
    try {
      uploadFiles.map(moveFile)
    } catch (e) { // its a single file not an Array
      if(uploadFiles.name) moveFile(uploadFiles)
    }

    if(fields.eureka__chosen_item) { // if they chose an item
      getMediaSources().then((mediaSources) => { // get the media source data
        const name = mediaSources.filter((mediaSource) => ( // try and get the name of the current media source
          mediaSource.id == cs
        ))[0].name || undefined;

        // this is where you redirect or do whatever you'd like once they have chosen an item
        return handleEurekaPostChosen(req, res, fields);
      });
    } else { // if they didn't chose something, they browsed or uploaded so time to serve them the media browser again
      return serveEurekaPage(req, res, cd);
    }
  });

});


/**
 * Serves the Eureka media browser component based off the current directory
 * @param req The Express req Object
 * @param res The Express res Object
 * @param cd The current directory
*/
function serveEurekaPage(req, res, cd = "/") {
  getEurekaMarkup(cd).then((eurekaMarkup) => {
    res.end(getEurekaPageMarkup(eurekaMarkup));
  });
}

/**
* Returns static markup of EurekaMediaBrowser
*
* Returns a promise that fetches media source data, then gets listings for the current directory of the current media source, then dispatch related Redux actions and resolves the promise with the static markup of EurekaMediaBrowser
 * @param dir The current directory
 * @return static markup of EurekaMediaBrowser component
*/
function getEurekaMarkup(dir = "/") {
  return new Promise((resolve, reject) => {
    getMediaSources().then((mediaSources) => ( // get the media source data
      store.dispatch(actions.fetchMediaSourcesSuccess(( // set the media source data on the Redux store
        mediaSources
      )))
    )).then(() => {
      return new Promise((resolve, reject) => { // get the media source tree listing
        const path = `${__dirname}/sources/filesystem/`;

        // crawl the filesystem (media source) to get the directory structure
        recursivelyGetSourceDirectories(path).then((results) => (
          resolve(results)
        )).catch((err) => (
          res.json(err)
        ));
      })
    }).then((results) => ( // set the directory tree data on the Redux store
      store.dispatch(actions.updateSourceTreeSuccess(
        results
      ))
    )).then(() => { // get the current directory listing
      return new Promise((resolve, reject) => {
        getDirectoryListing(`${__dirname}/sources/filesystem/`, dir || 'assets/img/hawaii', true, true, `${__dirname}/sources/filesystem/`).then((results) => (
          resolve(results)
        )).catch((err) => (
          res.json(err)
        ));
      })
    }).then((results) => (
      // set the current directory content data on the Redux store
      store.dispatch(actions.fetchDirectoryContentsSuccess(
        results
      ))
    )).then(() => ( // Resolve the promise with Redux state and React components flattened to a static string to be delivered in the initial HTML layer for world wide performance and accessibility
      resolve(
        ReactDOM.renderToStaticMarkup(
          <EurekaMediaBrowser
          uid="0"
          currentDirectory={dir} />
        )
      )
    ));
  });
}
Clone this wiki locally