ff-stream-web

git clone git://archive.git.mtrnord.blog/MTRNord/ff-stream-web.git
Log | Files | Refs | README | LICENSE

client.js (5954B)


      1 /**
      2  * React Starter Kit (https://www.reactstarterkit.com/)
      3  *
      4  * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
      5  *
      6  * This source code is licensed under the MIT license found in the
      7  * LICENSE.txt file in the root directory of this source tree.
      8  */
      9 
     10 import React from 'react';
     11 import ReactDOM from 'react-dom';
     12 import FastClick from 'fastclick';
     13 import UniversalRouter from 'universal-router';
     14 import queryString from 'query-string';
     15 import { createPath } from 'history/PathUtils';
     16 import history from './core/history';
     17 import App from './components/App';
     18 import { updateMeta } from './core/DOMUtils';
     19 import { ErrorReporter, deepForceUpdate } from './core/devUtils';
     20 
     21 // Global (context) variables that can be easily accessed from any React component
     22 // https://facebook.github.io/react/docs/context.html
     23 const context = {
     24   // Enables critical path CSS rendering
     25   // https://github.com/kriasoft/isomorphic-style-loader
     26   insertCss: (...styles) => {
     27     // eslint-disable-next-line no-underscore-dangle
     28     const removeCss = styles.map(x => x._insertCss());
     29     return () => { removeCss.forEach(f => f()); };
     30   },
     31 };
     32 
     33 // Switch off the native scroll restoration behavior and handle it manually
     34 // https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
     35 const scrollPositionsHistory = {};
     36 if (window.history && 'scrollRestoration' in window.history) {
     37   window.history.scrollRestoration = 'manual';
     38 }
     39 
     40 let onRenderComplete = function initialRenderComplete() {
     41   const elem = document.getElementById('css');
     42   if (elem) elem.parentNode.removeChild(elem);
     43   onRenderComplete = function renderComplete(route, location) {
     44     document.title = route.title;
     45 
     46     updateMeta('description', route.description);
     47     // Update necessary tags in <head> at runtime here, ie:
     48     // updateMeta('keywords', route.keywords);
     49     // updateCustomMeta('og:url', route.canonicalUrl);
     50     // updateCustomMeta('og:image', route.imageUrl);
     51     // updateLink('canonical', route.canonicalUrl);
     52     // etc.
     53 
     54     let scrollX = 0;
     55     let scrollY = 0;
     56     const pos = scrollPositionsHistory[location.key];
     57     if (pos) {
     58       scrollX = pos.scrollX;
     59       scrollY = pos.scrollY;
     60     } else {
     61       const targetHash = location.hash.substr(1);
     62       if (targetHash) {
     63         const target = document.getElementById(targetHash);
     64         if (target) {
     65           scrollY = window.pageYOffset + target.getBoundingClientRect().top;
     66         }
     67       }
     68     }
     69 
     70     // Restore the scroll position if it was saved into the state
     71     // or scroll to the given #hash anchor
     72     // or scroll to top of the page
     73     window.scrollTo(scrollX, scrollY);
     74 
     75     // Google Analytics tracking. Don't send 'pageview' event after
     76     // the initial rendering, as it was already sent
     77     if (window.ga) {
     78       window.ga('send', 'pageview', createPath(location));
     79     }
     80   };
     81 };
     82 
     83 // Make taps on links and buttons work fast on mobiles
     84 FastClick.attach(document.body);
     85 
     86 const container = document.getElementById('app');
     87 let appInstance;
     88 let currentLocation = history.location;
     89 let routes = require('./routes').default;
     90 
     91 // Re-render the app when window.location changes
     92 async function onLocationChange(location, action) {
     93   // Remember the latest scroll position for the previous location
     94   scrollPositionsHistory[currentLocation.key] = {
     95     scrollX: window.pageXOffset,
     96     scrollY: window.pageYOffset,
     97   };
     98   // Delete stored scroll position for next page if any
     99   if (action === 'PUSH') {
    100     delete scrollPositionsHistory[location.key];
    101   }
    102   currentLocation = location;
    103 
    104   try {
    105     // Traverses the list of routes in the order they are defined until
    106     // it finds the first route that matches provided URL path string
    107     // and whose action method returns anything other than `undefined`.
    108     const route = await UniversalRouter.resolve(routes, {
    109       path: location.pathname,
    110       query: queryString.parse(location.search),
    111     });
    112 
    113     // Prevent multiple page renders during the routing process
    114     if (currentLocation.key !== location.key) {
    115       return;
    116     }
    117 
    118     if (route.redirect) {
    119       history.replace(route.redirect);
    120       return;
    121     }
    122 
    123     appInstance = ReactDOM.render(
    124       <App context={context}>{route.component}</App>,
    125       container,
    126       () => onRenderComplete(route, location),
    127     );
    128   } catch (error) {
    129     // Display the error in full-screen for development mode
    130     if (__DEV__) {
    131       appInstance = null;
    132       document.title = `Error: ${error.message}`;
    133       ReactDOM.render(<ErrorReporter error={error} />, container);
    134       throw error;
    135     }
    136 
    137     console.error(error); // eslint-disable-line no-console
    138 
    139     // Do a full page reload if error occurs during client-side navigation
    140     if (action && currentLocation.key === location.key) {
    141       window.location.reload();
    142     }
    143   }
    144 }
    145 
    146 // Handle client-side navigation by using HTML5 History API
    147 // For more information visit https://github.com/mjackson/history#readme
    148 history.listen(onLocationChange);
    149 onLocationChange(currentLocation);
    150 
    151 // Handle errors that might happen after rendering
    152 // Display the error in full-screen for development mode
    153 if (__DEV__) {
    154   window.addEventListener('error', (event) => {
    155     appInstance = null;
    156     document.title = `Runtime Error: ${event.error.message}`;
    157     ReactDOM.render(<ErrorReporter error={event.error} />, container);
    158   });
    159 }
    160 
    161 // Enable Hot Module Replacement (HMR)
    162 if (module.hot) {
    163   module.hot.accept('./routes', () => {
    164     routes = require('./routes').default; // eslint-disable-line global-require
    165 
    166     if (appInstance) {
    167       try {
    168         // Force-update the whole tree, including components that refuse to update
    169         deepForceUpdate(appInstance);
    170       } catch (error) {
    171         appInstance = null;
    172         document.title = `Hot Update Error: ${error.message}`;
    173         ReactDOM.render(<ErrorReporter error={error} />, container);
    174         return;
    175       }
    176     }
    177 
    178     onLocationChange(currentLocation);
    179   });
    180 }