Update router-router to v4.2.0 (#872)

* Migrated to react-router v4.2.0
This commit is contained in:
Mavis Ou 2017-11-30 15:50:09 -08:00 коммит произвёл GitHub
Родитель 8bf01f885b
Коммит b12c8a83b7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
34 изменённых файлов: 281 добавлений и 231 удалений

Просмотреть файл

@ -1,9 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactGA from 'react-ga';
import { browserHistory, Link } from 'react-router';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import Utility from '../../js/utility.js';
import SignOutButton from '../sign-out-button.jsx';
class Bio extends React.Component {
constructor(props) {
@ -55,31 +55,7 @@ class Bio extends React.Component {
renderSignOut() {
if (!this.props.my_profile) return null;
return <div className="ml-sm-3"><button className="btn btn-link inline-link" onClick={(event) => this.handleLogOutBtnClick(event)}>Sign out</button></div>;
}
handleSocialMediaClick(event, type) {
ReactGA.event({
category: `Profile`,
action: `Social link tap`,
label: `${this.profileOwnerName} - ${type}`,
transport: `beacon`
});
}
handleLogOutBtnClick(event) {
event.preventDefault();
ReactGA.event({
category: `Account`,
action: `Logout`,
label: `Logout ${window.location.pathname}`,
});
this.props.user.logout();
browserHistory.push({
pathname: `/featured`
});
return <div className="ml-sm-3"><SignOutButton user={this.props.user} history={this.props.history} /></div>;
}
renderMeta(type, text, link) {

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { NavLink as ReactRouterNavLink } from 'react-router-dom';
import classNames from 'classnames';
import PropTypes from 'prop-types';
@ -18,7 +18,7 @@ class NavLink extends React.Component {
let classes = classNames(`open-sans`, this.props.className);
return (
<Link {...this.props}
<ReactRouterNavLink {...this.props}
className={classes}
activeClassName="active"
onClick={() => this.handleClick()}

Просмотреть файл

@ -1,11 +1,12 @@
import React from 'react';
import ReactGA from 'react-ga';
import { IndexLink } from 'react-router';
import { NavLink as ReactRouterNavLink } from 'react-router-dom';
import classNames from 'classnames';
import NavLink from '../nav-link/nav-link.jsx';
import user from '../../js/app-user';
import utility from '../../js/utility';
class NavListItem extends React.Component {
render() {
let classes = classNames(`d-inline-block my-md-0`, this.props.className, {
@ -29,7 +30,7 @@ class NavBar extends React.Component {
componentDidMount() {
user.addListener(this);
user.verify();
user.verify(this.props.location, this.props.history);
}
componentWillUnmount() {
@ -125,10 +126,10 @@ class NavBar extends React.Component {
<div className="container">
<div className="row open-sans align-items-center">
<div className="col-12 col-lg-9 d-flex flex-column flex-lg-row" id="main-nav-wrapper">
<IndexLink to="/" className="d-inline-block">
<ReactRouterNavLink to="/" className="d-inline-block">
<img src="/assets/svg/pulse-logo-mobile.svg" alt="Mozilla Pulse" className="logo hidden-md-up" width="40" />
<img src="/assets/svg/pulse-logo.svg" alt="Mozilla Pulse" className="logo hidden-sm-down" width="187" />
</IndexLink>
</ReactRouterNavLink>
{ this.renderNavList() }
</div>
<div className="pinned col-6 col-lg-3">

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
const DEFAULT_TEXT = `Help with this, or find other projects that have similar ways to get involved.`;

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Utility from '../../../js/utility.js';

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
class Thumbnail extends React.Component {
constructor(props) {

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
class Title extends React.Component {

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import ReactGA from 'react-ga';
import PropTypes from 'prop-types';
import classNames from 'classnames';

Просмотреть файл

@ -14,12 +14,20 @@ class ProjectList extends React.Component {
};
}
componentDidMount() {
pageSettings.setCurrentPathname(window.location.pathname);
}
componentDidUpdate() {
if (!this.state.inPageUpdate && this.props.restoreScrollPosition) {
if (!this.state.inPageUpdate) {
pageSettings.restoreScrollPosition();
}
}
componentWillUnmount() {
pageSettings.setScrollPosition();
}
handleLoadMoreBtnClick() {
ReactGA.event({
category: `Browse`,

Просмотреть файл

@ -156,7 +156,6 @@ class ProjectLoader extends React.Component {
loadingData={this.state.loadingData}
moreEntriesToFetch={this.state.moreEntriesToFetch}
fetchData={() => this.fetchData()}
restoreScrollPosition={pageSettings.shouldRestore}
onModerationMode={!!this.props.moderationState} />
</div>
);

Просмотреть файл

@ -0,0 +1,37 @@
import React from 'react';
import ReactGA from 'react-ga';
import PropTypes from 'prop-types';
class SignOutButton extends React.Component {
constructor(props) {
super(props);
}
handleLogOutBtnClick(event) {
event.preventDefault();
ReactGA.event({
category: `Account`,
action: `Logout`,
label: `Logout ${window.location.pathname}`,
});
this.props.user.logout();
this.props.history.push({
pathname: `/featured`
});
}
render() {
return (
<button className="btn btn-link inline-link" onClick={(event) => this.handleLogOutBtnClick(event)}>Sign out</button>
);
}
}
SignOutButton.propTypes = {
user: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
export default SignOutButton;

Просмотреть файл

@ -24,7 +24,7 @@ export default {
// https://developers.google.com/analytics/devguides/collection/analyticsjs/command-queue-reference#set
ReactGA.set({ page: window.location.pathname });
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#location
ReactGA.set({ location: window.location.href });
ReactGA.set({ location: window.location.href, title: window.title });
ReactGA.pageview(`${window.location.pathname}/${window.location.search}`);
}

Просмотреть файл

@ -47,6 +47,7 @@ class PageSettings {
restoreScrollPosition() {
if (typeof window !== `undefined` && this.shouldRestore) {
window.scrollTo(0, this.currentScrollPosition);
this.shouldRestore = false;
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
import { browserHistory } from 'react-router';
import qs from "qs";
import env from "../config/env.generated.json";
import localstorage from './localstorage.js';
import Service from './service.js';
@ -21,18 +21,17 @@ const Login = {
* setUserData(error, username). If the user is not logged in
* the username will be falsey.
*/
isLoggedIn(location, setUserData) {
isLoggedIn(location, history, setUserData) {
if (location) {
// Make sure that oauth loggedin=True/False query parameters are
// removed from the current URL, as they should not end up in
// bookmarks etc.
const query = location.query;
let query = qs.parse(location.search.substring(1));
if (query.loggedin) {
delete location.query.loggedin;
browserHistory.replace({
pathname: location.pathname,
query: query
});
delete query.loggedin;
location.search = `?${qs.stringify(query)}`;
history.replace(location);
}
}
@ -120,8 +119,8 @@ class User {
window.location = Login.getLoginURL(redirectUrl);
}
verify(location) {
Login.isLoggedIn(location, (error, username, customName, email, moderator) => {
verify(location, history) {
Login.isLoggedIn(location, history, (error, username, customName, email, moderator) => {
this.update(error, username, customName, email, moderator);
});
}

Просмотреть файл

@ -1,15 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import { BrowserRouter } from 'react-router-dom';
import Analytics from './js/analytics.js';
import routes from './routes.jsx';
import App from './routes.jsx';
Analytics.initialize();
let routerUpdateHandler = function() {
Analytics.logPageView();
};
ReactDOM.render((
<Router routes={routes} history={browserHistory} onUpdate={routerUpdateHandler} />
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById(`app`));

92
package-lock.json сгенерированный
Просмотреть файл

@ -4645,13 +4645,14 @@
"integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys="
},
"history": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/history/-/history-3.3.0.tgz",
"integrity": "sha1-/O3M6PEpdTcVRdc1RhAzV5ptrpw=",
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
"integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
"requires": {
"invariant": "2.2.2",
"loose-envify": "1.3.1",
"query-string": "4.3.4",
"resolve-pathname": "2.2.0",
"value-equal": "0.4.0",
"warning": "3.0.0"
}
},
@ -4671,9 +4672,9 @@
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
},
"hoist-non-react-statics": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
"integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
},
"home-or-tmp": {
"version": "2.0.0",
@ -6885,18 +6886,9 @@
"dev": true
},
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"requires": {
"object-assign": "4.1.1",
"strict-uri-encode": "1.1.0"
}
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
},
"querystring": {
"version": "0.2.0",
@ -7073,16 +7065,44 @@
}
},
"react-router": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.0.tgz",
"integrity": "sha512-sXlLOg0TRCqnjCVskqBHGjzNjcJKUqXEKnDSuxMYJSPJNq9hROE9VsiIW2kfIq7Ev+20Iz0nxayekXyv0XNmsg==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz",
"integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==",
"requires": {
"create-react-class": "15.6.2",
"history": "3.3.0",
"hoist-non-react-statics": "1.2.0",
"history": "4.7.2",
"hoist-non-react-statics": "2.3.1",
"invariant": "2.2.2",
"loose-envify": "1.3.1",
"path-to-regexp": "1.7.0",
"prop-types": "15.6.0",
"warning": "3.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz",
"integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==",
"requires": {
"history": "4.7.2",
"invariant": "2.2.2",
"loose-envify": "1.3.1",
"prop-types": "15.6.0",
"react-router": "4.2.0",
"warning": "3.0.0"
}
},
@ -7337,6 +7357,13 @@
"tough-cookie": "2.3.2",
"tunnel-agent": "0.6.0",
"uuid": "3.1.0"
},
"dependencies": {
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
}
}
},
"require-directory": {
@ -7379,6 +7406,11 @@
"integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
"dev": true
},
"resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
"integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
},
"restore-cursor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
@ -8086,11 +8118,6 @@
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string-length": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz",
@ -9032,6 +9059,11 @@
"spdx-expression-parse": "1.0.4"
}
},
"value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
"integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

Просмотреть файл

@ -86,13 +86,15 @@
"npm-run-all": "^4.1.1",
"postcss-cli-simple": "^1.0.3",
"prop-types": "^15.6.0",
"qs": "^6.5.1",
"react": "^15.6.2",
"react-debounce-input": "^3.1.0",
"react-dom": "^15.6.2",
"react-formbuilder": "^0.10.0",
"react-ga": "^2.3.5",
"react-helmet": "^5.2.0",
"react-router": "^3.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-select": "^1.0.0-rc.10",
"react-tag-autocomplete": "^5.4.1",
"shx": "^0.2.1",

Просмотреть файл

@ -1,8 +1,9 @@
import React from 'react';
import ReactGA from 'react-ga';
import { browserHistory, Link } from 'react-router';
import { Link } from 'react-router-dom';
import { Helmet } from "react-helmet";
import { Form } from 'react-formbuilder';
import SignOutButton from '../../components/sign-out-button.jsx';
import HintMessage from '../../components/hint-message/hint-message.jsx';
import Service from '../../js/service.js';
import utility from '../../js/utility';
@ -43,7 +44,7 @@ class Add extends React.Component {
componentDidMount() {
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
}
componentWillUnmount() {
@ -70,21 +71,6 @@ class Add extends React.Component {
user.login(utility.getCurrentURL());
}
handleLogOutBtnClick(event) {
event.preventDefault();
ReactGA.event({
category: `Account`,
action: `Logout`,
label: `Logout ${window.location.pathname}`,
});
user.logout();
browserHistory.push({
pathname: `/featured`
});
}
handleCreatorClick(event) {
event.preventDefault();
this.setState({numCreatorFields: this.state.numCreatorFields+1});
@ -145,14 +131,14 @@ class Add extends React.Component {
.get(entryId)
.then(result => {
if(result) {
browserHistory.push({
this.props.history.push({
pathname: `/entry/${entryId}`,
query: {
justPostedByUser: true
}
});
} else {
browserHistory.push({
this.props.history.push({
pathname: `/submitted`,
query: { entryId }
});
@ -161,7 +147,7 @@ class Add extends React.Component {
.catch(reason => {
// a 404 is yielded as an error by Service.entry
console.error(reason);
browserHistory.push({
this.props.history.push({
pathname: `/submitted`,
query: { entryId }
});
@ -196,7 +182,7 @@ class Add extends React.Component {
<h2>Basic Info</h2>
<div className="posted-by">
<p className="d-inline-block mr-3 mb-3">Posted by: <span className="text-muted">{user.name}</span></p>
<p className="d-inline-block text-muted">Not you? <button className="btn btn-link inline-link" onClick={(event) => this.handleLogOutBtnClick(event)}>Sign out</button>.</p>
<p className="d-inline-block text-muted">Not you? <SignOutButton user={user} history={this.props.history} />.</p>
</div>
<Form ref="basicForm" fields={basicInfoFields}
inlineErrors={true}

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import IssuesField from '../../../components/form-fields/issues.jsx';
import validator from './validator';
import Creators from './creators';

Просмотреть файл

@ -1,5 +1,6 @@
import React from 'react';
import { browserHistory, Link } from 'react-router';
import { Link } from 'react-router-dom';
import qs from 'qs';
import { Helmet } from "react-helmet";
import HintMessage from '../../components/hint-message/hint-message.jsx';
@ -14,8 +15,8 @@ export default class Submitted extends React.Component {
}
componentDidMount() {
let location = this.props.router.location;
let query = location.query;
let location = this.props.location;
let query = qs.parse(this.props.location.search.substring(1));
if (query && query.entryId) {
let entryId = parseInt(query.entryId, 10);
@ -28,7 +29,7 @@ export default class Submitted extends React.Component {
// remove 'entryId' query from URL
delete query.entryId;
browserHistory.replace({
this.props.history.replace({
pathname: location.pathname,
query: query
});

Просмотреть файл

@ -1,13 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import { Helmet } from "react-helmet";
import ProjectLoader from '../components/project-loader/project-loader.jsx';
import HintMessage from '../components/hint-message/hint-message.jsx';
import Service from '../js/service.js';
import bookmarkManager from '../js/bookmarks-manager';
import user from '../js/app-user';
import pageSettings from '../js/app-page-settings';
class Bookmarks extends React.Component{
constructor(props) {
@ -26,15 +25,10 @@ class Bookmarks extends React.Component{
componentDidMount() {
// get IDs of user's bookmarked entries
this.setState({lsBookmarkedIds: bookmarkManager.bookmarks.get()}, () => {
if (pageSettings.shouldRestore) {
// restore state back to what is stored in pageSettings
this.setState(pageSettings.currentList);
}
});
this.setState({lsBookmarkedIds: bookmarkManager.bookmarks.get()});
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
}
componentWillUnmount() {
@ -141,9 +135,7 @@ class Bookmarks extends React.Component{
}
Bookmarks.propTypes = {
router: PropTypes.shape({
location: PropTypes.object
}).isRequired
location: PropTypes.object
};
export default Bookmarks;

Просмотреть файл

@ -1,10 +1,11 @@
import React from 'react';
import { browserHistory } from 'react-router';
import { Helmet } from "react-helmet";
import qs from 'qs';
import LoadingNotice from '../components/loading-notice.jsx';
import ProjectCardDetialed from '../components/project-card/project-card-detailed.jsx';
import Service from '../js/service.js';
import Utility from '../js/utility.js';
import pageSettings from '../js/app-page-settings.js';
const NO_ENTRY_TITLE = `Entry unavailable`;
const NO_ENTRY_BLOCK = (
@ -28,7 +29,11 @@ class Entry extends React.Component {
}
componentDidMount() {
this.fetchData(this.props.params.entryId);
this.fetchData(this.props.match.params.entryId);
}
componentWillUnmount() {
pageSettings.setRestore(true);
}
fetchData(entryId = ``) {
@ -51,8 +56,8 @@ class Entry extends React.Component {
}
checkIfRedirectedFromFormSubmission() {
let location = this.props.router.location;
let query = location.query;
let location = this.props.location;
let query = qs.parse(location.search.substring(1));
let justPostedByUser;
if (query && query.justPostedByUser) {
@ -60,7 +65,7 @@ class Entry extends React.Component {
// remove 'justPostedByUser' query from URL
delete query.justPostedByUser;
browserHistory.replace({
this.props.history.replace({
pathname: location.pathname,
query: query
});

Просмотреть файл

@ -5,7 +5,7 @@ import ProjectLoader from '../components/project-loader/project-loader.jsx';
import Utility from '../js/utility.js';
export default function (props) {
const issueName = Utility.getIssueNameFromUriPath(props.params.issue);
const issueName = Utility.getIssueNameFromUriPath(props.match.params.issue);
return <div>
<Helmet><title>{issueName}</title></Helmet>

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import { Helmet } from "react-helmet";
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import IssueSelector from '../../components/issue-selector/issue-selector.jsx';
import Service from '../../js/service';
import Utility from '../../js/utility';

Просмотреть файл

@ -13,7 +13,7 @@ class Moderation extends React.Component {
componentDidMount() {
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
}
componentWillUnmount() {

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import ReactGA from 'react-ga';
import NotFound from './not-found.jsx';
import Service from '../js/service';
@ -20,7 +20,7 @@ class MyProfile extends React.Component {
componentDidMount() {
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
}
componentWillUnmount() {
@ -32,7 +32,7 @@ class MyProfile extends React.Component {
if (event === `verified` ) {
this.setState({ user }, () => {
if (this.state.user.loggedin) {
this.fetchProfile(this.props.params.id, newState => {
this.fetchProfile(this.props.match.params.id, newState => {
this.setState(newState);
});
}
@ -72,7 +72,7 @@ class MyProfile extends React.Component {
renderProfile() {
if (!this.state.userProfile) return <NotFound header="Profile not found" />;
return <Profile profile={this.state.userProfile} user={this.state.user} />;
return <Profile profile={this.state.userProfile} user={this.state.user} history={this.props.history} />;
}
getContentForLoggedInUser() {

Просмотреть файл

@ -1,17 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import HintMessage from '../components/hint-message/hint-message.jsx';
const NotFound = (props) => {
return (
<HintMessage iconComponent={<img src="/assets/svg/icon-404.svg" />}
header={props.header}
linkComponent={props.linkComponent}>
{ props.children || <p>Check your URL or try a search. Still no luck? <a href="https://github.com/mozilla/network-pulse/issues/new">Let us know</a>.</p> }
</HintMessage>
);
};
class NotFound extends React.Component {
componentWillMount() {
const { staticContext } = this.props;
if (staticContext) {
staticContext.pageNotFound = true;
}
}
render() {
return (
<HintMessage iconComponent={<img src="/assets/svg/icon-404.svg" />}
header={this.props.header}
linkComponent={this.props.linkComponent}>
{ this.props.children || <p>Check your URL or try a search. Still no luck? <a href="https://github.com/mozilla/network-pulse/issues/new">Let us know</a>.</p> }
</HintMessage>
);
}
}
NotFound.propTypes = {
header: PropTypes.string,

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import { Link } from 'react-router-dom';
import validator from '../../../js/form-validator';
import IssuesField from '../../../components/form-fields/issues.jsx';

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { browserHistory, Link } from 'react-router';
import { Link } from 'react-router-dom';
import { Helmet } from "react-helmet";
import { Form } from 'react-formbuilder';
import NotFound from '../not-found.jsx';
@ -32,7 +32,7 @@ class ProfileEdit extends React.Component{
componentDidMount() {
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
this.loadCurrentProfile();
}
@ -76,7 +76,7 @@ class ProfileEdit extends React.Component{
event.preventDefault();
user.logout();
browserHistory.push({
this.props.history.push({
pathname: `/featured`
});
}
@ -118,7 +118,7 @@ class ProfileEdit extends React.Component{
Service.myProfile
.put(profile)
.then(() => {
browserHistory.push({
this.props.history.push({
pathname: `/profile/me`,
});
})
@ -203,9 +203,7 @@ class ProfileEdit extends React.Component{
}
ProfileEdit.propTypes = {
router: PropTypes.shape({
location: PropTypes.object
}).isRequired
location: PropTypes.object
};
export default ProfileEdit;

Просмотреть файл

@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from "react-helmet";
import Bio from '../components/bio/bio.jsx';
import ProjectList from '../components/project-list/project-list.jsx';
import pageSettings from '../js/app-page-settings';
class Profile extends React.Component {
constructor(props) {
@ -12,7 +12,7 @@ class Profile extends React.Component {
renderProfile() {
if (!this.props.profile) return null;
return <div className="col-12"><Bio {...this.props.profile} user={this.props.user} /></div>;
return <div className="col-12"><Bio {...this.props.profile} user={this.props.user} history={this.props.history} /></div>;
}
renderProjects(entries, label) {
@ -30,7 +30,6 @@ class Profile extends React.Component {
loadingData={false}
moreEntriesToFetch={false}
fetchData={()=>{}}
restoreScrollPosition={pageSettings.shouldRestore}
onModerationMode={false}
/>
</div>
@ -51,4 +50,10 @@ class Profile extends React.Component {
);
}
}
Profile.propTypes = {
user: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
export default Profile;

Просмотреть файл

@ -17,9 +17,9 @@ class PublicProfile extends React.Component {
componentDidMount() {
user.addListener(this);
user.verify(this.props.router.location);
user.verify(this.props.location, this.props.history);
this.fetchProfile(this.props.params.id, newState => {
this.fetchProfile(this.props.match.params.id, newState => {
this.setState(newState);
});
}
@ -47,7 +47,7 @@ class PublicProfile extends React.Component {
renderProfile() {
if (!this.state.userProfile) return <NotFound header="Profile not found" />;
return <Profile profile={this.state.userProfile} user={this.state.user} />;
return <Profile profile={this.state.userProfile} user={this.state.user} history={this.props.history} />;
}
render() {

Просмотреть файл

@ -1,7 +1,7 @@
import React from 'react';
import { browserHistory } from 'react-router';
import { Helmet } from "react-helmet";
import classNames from 'classnames';
import qs from "qs";
import DebounceInput from 'react-debounce-input';
import Select from 'react-select';
import ReactGA from 'react-ga';
@ -31,18 +31,19 @@ class Search extends React.Component {
}
getSearchCriteria(props) {
let query = qs.parse(props.location.search.substring(1));
let criteria = {
keywordSearched: props.location.query.keyword
keywordSearched: query.keyword
};
if (this.props.moderation) {
// the following states are only useful on moderation mode
if (props.location.query.featured === `True`) {
criteria.featured = props.location.query.featured;
if (query.featured === `True`) {
criteria.featured = query.featured;
}
criteria.moderationState = {
value: ``,
label: props.location.query.moderationstate || DEFAULT_MODERATION_FILTER
label: query.moderationstate || DEFAULT_MODERATION_FILTER
};
}
@ -53,7 +54,7 @@ class Search extends React.Component {
let keywordSearched = this.state.keywordSearched;
let moderationState = this.state.moderationState;
let featured = this.state.featured;
let location = { pathname: this.props.router.location.pathname };
let location = { pathname: this.props.location.pathname };
let query = {};
if ( keywordSearched ) {
@ -69,8 +70,8 @@ class Search extends React.Component {
query.moderationstate = moderationState.label;
}
location.query = query;
browserHistory.push(location);
location.search = `?${qs.stringify(query)}`;
this.props.history.push(location);
}
handleInputChange(event) {

Просмотреть файл

@ -1,8 +1,9 @@
import React from 'react';
import ReactGA from 'react-ga';
import { Route, IndexRoute, IndexRedirect } from 'react-router';
import { withRouter } from 'react-router';
import { Switch, Route, Redirect } from 'react-router-dom';
import { Helmet } from "react-helmet";
import pageSettings from './js/app-page-settings';
import Analytics from './js/analytics.js';
import env from "./config/env.generated.json";
import ProjectLoader from './components/project-loader/project-loader.jsx';
@ -49,30 +50,65 @@ const Latest = () => {
};
const Help = (router) => {
let searchParam = { key: `help_type`, value: router.params.helpType };
let searchParam = { key: `help_type`, value: router.match.params.helpType };
return <SingleFilterCriteriaPage searchParam={searchParam} headerLabel="Help" />;
};
const Tag = (router) => {
let searchParam = { key: `tag`, value: router.params.tag };
let searchParam = { key: `tag`, value: router.match.params.tag };
return <SingleFilterCriteriaPage searchParam={searchParam} headerLabel="Tag" />;
};
const Main = () => (
<Switch>
<Route exact path="/" render={() => <Redirect to="/featured"/>} />
<Route path="/featured" component={Featured} />
<Route path="/latest" component={Latest} />
<Route path="/favs" component={Bookmarks} />
<Route exact path="/issues" component={Issues} />
<Route path="/issues/:issue" component={Issue} />
<Route path="/entry/:entryId" component={Entry} />
<Route path="/add" component={Add} onEnter={() => { if (typeof window !== `undefined`) { window.scroll(0, 0); } }} />
<Route path="/submitted" component={Submitted} />
<Route path="/search" component={Search} />
<Route exact path="/tags" render={() => <Redirect to="/latest"/>} />
<Route path="/tags/:tag" component={Tag} />
<Route exact path="/help" render={() => <Redirect to="/latest"/>} />
<Route path="/help/:helpType" component={Help} />
<Route path="/moderation" component={Moderation} />
<Route path="/profile/me" component={MyProfile} />
<Route path="/profile/:id" component={PublicProfile} />
<Route path="/myprofile" component={ProfileEdit} />
<Route path="*" component={NotFound}/>
</Switch>
);
const NavbarWithRouter = withRouter(Navbar);
class App extends React.Component {
constructor(props) {
super(props);
this.pageTitle = `Mozilla Network Pulse`;
}
componentDidMount() {
Analytics.logPageView();
}
componentDidUpdate() {
Analytics.logPageView();
window.scrollTo(0, 0);
}
render() {
return (
<div>
<Helmet titleTemplate={`%s - ${this.pageTitle}`}
defaultTitle={this.pageTitle}>
</Helmet>
<Navbar router={this.props.router}/>
<NavbarWithRouter />
<div id="main" className="container">
{this.props.children}
<Main />
</div>
<Footer/>
</div>
@ -80,6 +116,7 @@ class App extends React.Component {
}
}
// We have renamed all non user facing "favorites" related variables and text (e.g., favs, faved, etc) to "bookmarks".
// This is because we want client side code to match what Pulse API uses (i.e., bookmarks)
// For user facing bits like UI labels and URL path we want them to stay as "favorites".
@ -89,34 +126,4 @@ class App extends React.Component {
// PageSettings is used to preserve a project list view state.
// Attach route enter hook pageSettings.setCurrentPathname(evt.location.pathname)
// *only* to routes that render a list of projects.
module.exports = (
<Route path="/" component={App}>
<IndexRedirect to="/featured" />
<Route path="featured" component={Featured} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="latest" component={Latest} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="favs" component={Bookmarks} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="issues">
<IndexRoute component={Issues} />
<Route path=":issue" component={Issue} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
</Route>
<Route path="entry/:entryId" component={Entry} onEnter={() => pageSettings.setScrollPosition()} onLeave={() => pageSettings.setRestore(true)} />
<Route path="add" component={Add} onEnter={() => { if (typeof window !== `undefined`) { window.scroll(0, 0); } }} />
<Route path="submitted" component={Submitted} />
<Route path="search" component={Search} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="tags">
<IndexRedirect to="/latest" />
<Route path=":tag" component={Tag} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
</Route>
<Route path="help">
<IndexRedirect to="/latest" />
<Route path=":helpType" component={Help} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
</Route>
<Route path="moderation" component={Moderation} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="profile">
<Route path="me" component={MyProfile} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path=":id" component={PublicProfile} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
</Route>
<Route path="myprofile" component={ProfileEdit} onEnter={evt => pageSettings.setCurrentPathname(evt.location.pathname)} />
<Route path="*" component={NotFound}/>
</Route>
);
export default App;

Просмотреть файл

@ -4,8 +4,8 @@ import helmet from 'helmet';
import { Helmet as ReactHelmet } from "react-helmet";
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes.jsx';
import { StaticRouter } from 'react-router';
import Main from './routes.jsx';
import securityHeaders from './js/security-headers';
const app = express();
@ -66,28 +66,21 @@ app.use((req, res, next) => {
app.use(express.static(path.resolve(__dirname, `dist`)));
app.get(`*`, (req, res) => {
match({ routes: routes, location: req.url }, (err, redirect, props) => {
if (err) {
res.status(500).send(err.message);
} else if (redirect) {
res.redirect(302, redirect.pathname + redirect.search);
} else if (props) {
// we've got props!
// let's match a route and render the corresponding page component
const appHtml = renderToString(<RouterContext {...props}/>);
const reactHelmet = ReactHelmet.renderStatic();
const reactHelmet = ReactHelmet.renderStatic();
const context = {}; // context object contains the results of the render
if (props.components[props.components.length-1].displayName === `not-found`) {
// if route matches the "Not Found" route, let's render the "Not Found" 404 page
res.status(404).send(renderPage(appHtml,reactHelmet));
} else {
res.status(200).send(renderPage(appHtml,reactHelmet));
}
} else {
// nothing was matched
res.status(404).send(`Not Found`);
}
});
const appHtml = renderToString(
<StaticRouter location={req.url} context={context}>
<Main />
</StaticRouter>
);
if (context.url) {
// Somewhere a `<Redirect>` was rendered
res.redirect(301, context.url);
} else {
res.status(context.pageNotFound ? 404 : 200).send(renderPage(appHtml, reactHelmet));
}
});
function renderPage(appHtml,reactHelmet) {