Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1757 new routing #3494

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f16b9df
wip catalog routing
soyarsauce May 29, 2019
bfbb88d
Remove duplicate link style
soyarsauce May 31, 2019
735741a
wip catalog routing
soyarsauce May 31, 2019
363aef1
Update routing to use const
soyarsauce Jun 5, 2019
db6ae8c
Add gorup routing & add graceful preview errors
soyarsauce Jun 6, 2019
4f8e624
Update catalog item html titles & meta descriptions
soyarsauce Jun 6, 2019
3f5994b
Update docs for baseHref
soyarsauce Jun 6, 2019
392d214
Add rel="canonical" link tags for DataPreview helmet
soyarsauce Jun 20, 2019
6851830
Add prerender end event for catalog routes
soyarsauce Jun 21, 2019
cfa920e
Lint
soyarsauce Jun 24, 2019
046f546
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Jun 24, 2019
6955c5c
Export non-withRouter-hoc components
soyarsauce Jun 24, 2019
1a150e2
Fix DataCatalogItemSpec `selected` prop tests
soyarsauce Jun 24, 2019
9b19cb7
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Aug 7, 2019
b168ab7
Track page views
soyarsauce Aug 7, 2019
444df22
Resolve quick minor comments from PR
soyarsauce Aug 7, 2019
994f4b1
Fix haeding structure
soyarsauce Aug 7, 2019
158f9ba
Fix "About This Data" & preview map reload bug
soyarsauce Aug 8, 2019
66084ef
Fix group-selected-state & open on first load
soyarsauce Aug 8, 2019
da13136
Fix tabs not updating when navigating
soyarsauce Aug 8, 2019
1e6fa60
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Aug 8, 2019
8435d8a
Fix incorrect "About This Data" open-close flow
soyarsauce Aug 8, 2019
6b8b521
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Sep 16, 2019
533e6da
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Dec 2, 2019
7301a84
Bump version, fix bad merge
soyarsauce Dec 2, 2019
230d8da
Merge remote-tracking branch 'origin/master' into 1757-new-routing
soyarsauce Dec 2, 2019
6515f2c
Lint
soyarsauce Dec 3, 2019
40f4351
Update add user data button (bandage)
soyarsauce Dec 3, 2019
5da6ad6
Bump version
soyarsauce Dec 3, 2019
5ea5986
Merge tag '7.11.3' into 1757-new-routing
tephenavies Mar 17, 2020
e77fa4b
Merge tag '7.11.4' into 1757-new-routing
reginapramesti Apr 17, 2020
aeb6e1d
Run prettier
reginapramesti Apr 17, 2020
1f3d1f7
Create prerender-troubleshooting.md
soyarsauce Apr 22, 2020
7c89cde
feedback form is now scrollable
KeyboardSounds Jun 17, 2020
d5fe95d
new version 7.4.11-prerender-1
KeyboardSounds Jun 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/customizing/skinning.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import React from 'react';

import version from '../../version';

import StandardUserInterface from 'terriajs/lib/ReactViews/StandardUserInterface/StandardUserInterface.jsx';
import { StandardUserInterface } from 'terriajs/lib/ReactViews/StandardUserInterface/StandardUserInterface.jsx';
import MenuItem from 'terriajs/lib/ReactViews/StandardUserInterface/customizable/MenuItem';
import RelatedMaps from './RelatedMaps';
import { Menu, Nav } from 'terriajs/lib/ReactViews/StandardUserInterface/customizable/Groups';
Expand Down
3 changes: 3 additions & 0 deletions doc/deploying/deploying-terriamap.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ And on the server, change to the directory where you copied those files and dire

The server will start on port 3001. You can specify a different port by adding `--port 1234` to the command-line above.

Ensure that you specify the `baseHref` path in your `devserverconfig.json` if you are serving your TerriaMap from a directory, and an `appBaseUrl` so a sitemap & other links can be generated.

It is usually a good idea to run another web server, such as [nginx](https://nginx.org/en/) or [Varnish](https://varnish-cache.org/) on port 80 and then reverse-proxy to the Node.js server, rather than running terriajs-server on port 80 directly. You will find a varnish VCL file with the TerriaMap source code in the [deploy/varnish directory](https://github.com/TerriaJS/TerriaMap/tree/master/deploy/varnish). In addition to acting as a reverse proxy for the Node.js server, the supplied Varnish configuration also caches requests to proxied map data in order to improve performance.

### Using any web server
Expand All @@ -38,6 +40,7 @@ It is usually a good idea to run another web server, such as [nginx](https://ngi
2. It includes a simple service at `/proxy` that allows TerriaJS to access geospatial data servers that don't support [CORS](../connecting-to-data/cross-origin-resource-sharing.md). If this service is not available, TerriaJS won't be able to access any datasets that are on other servers and that don't support CORS.
3. It includes another service at `/convert` that uses [GDAL](http://www.gdal.org/) and OGR to transform otherwise unsupported geospatial vector data (e.g. shapefiles) to GeoJSON for display by the TerriaJS client. If this service is not available, these data formats will not be supported. However, all the [formats that TerriaJS supports directly](../connecting-to-data/catalog-items.md) will work just fine.
* When configured correctly, it persists blobs of JSON for use in the sharing feature. If this service is not available, the JSON can be stored in the share URL, instead. However, this makes for some extremely long URLs.
4. It reroutes and serves up a build-time-prerendered index.html of your catalog URLs so they can be indexed by search engines.

If these limitations are acceptable, you can run your TerriaMap on virtually any web server by simply copying the TerriaMap `wwwroot` onto the server!

Expand Down
16 changes: 16 additions & 0 deletions doc/deploying/prerender-troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Prerender Troubleshooting
TerriaMap prerenders pages through puppeteer.
It should JustWork on MacOS. Linux will require some extra deps. Some more general troubleshooting at:
https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md

## WSL
You'll need to tell PrerenderSPAPlugin to pass an `executablePath` down to puppeteer.

via @steve9164's findings

Add (changing the path to where your chrome is)
`executablePath: '/mnt/c/Program\ Files\ \(x86\)/Google/Chrome/Application/chrome.exe'`

to

https://github.com/TerriaJS/TerriaMap/blob/c8675bc62cd5e37b6df490910f6ed7f7ae264d95/buildprocess/webpack.config.js#L155
6 changes: 6 additions & 0 deletions lib/Core/ConsoleAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ ConsoleAnalytics.prototype.logEvent = function(category, action, label, value) {
}
};

ConsoleAnalytics.prototype.logPageView = function(location) {
if (this.logToConsole) {
console.log("New pageview at location: ", location);
}
};

module.exports = ConsoleAnalytics;
6 changes: 6 additions & 0 deletions lib/Core/GoogleAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ GoogleAnalytics.prototype.logEvent = function(category, action, label, value) {
ga("send", "event", category, action, label, value);
};

GoogleAnalytics.prototype.logPageView = function(location) {
initializeGoogleAnalytics(this);
ga("set", "page", location);
ga("send", "pageview");
};

function initializeGoogleAnalytics(that) {
if (defined(window.ga)) {
return;
Expand Down
2 changes: 2 additions & 0 deletions lib/Models/Terria.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var defaultConfigParameters = {
defaultMaximumShownFeatureInfos: 100,
/* These services are not included within Terria, but this is where we expect them to be, by default. */
regionMappingDefinitionsUrl: "build/TerriaJS/data/regionMapping.json",
appBaseUrl: undefined,
conversionServiceBaseUrl: "convert/",
proj4ServiceBaseUrl: "proj4/",
corsProxyBaseUrl: "proxy/",
Expand Down Expand Up @@ -524,6 +525,7 @@ Terria.prototype.start = function(options) {
that.configParameters = combine(config.parameters, that.configParameters);
}
var cp = that.configParameters;
cp.appBaseUrl = slashify(cp.appBaseUrl);
cp.conversionServiceBaseUrl = slashify(cp.conversionServiceBaseUrl);
cp.proj4ServiceBaseUrl = slashify(cp.proj4ServiceBaseUrl);
cp.corsProxyBaseUrl = slashify(cp.corsProxyBaseUrl);
Expand Down
14 changes: 14 additions & 0 deletions lib/ReactViewModels/TerriaRouting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Route paths for the app.

var ROOT_ROUTE = "/";

var CATALOG_ROUTE = `${ROOT_ROUTE}catalog/`;
// var CATALOG_ROUTE = "/catalog/";

var CATALOG_MEMBER_ROUTE = `${CATALOG_ROUTE}:catalogMemberId`;

module.exports = {
ROOT_ROUTE,
CATALOG_ROUTE,
CATALOG_MEMBER_ROUTE
};
60 changes: 57 additions & 3 deletions lib/ReactViewModels/ViewState.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import addedByUser from "../Core/addedByUser";
import knockout from "terriajs-cesium/Source/ThirdParty/knockout";
import MouseCoords from "./MouseCoords";
import SearchState from "./SearchState";
import URI from "urijs";

import "../Models/i18n.js";
export const DATA_CATALOG_NAME = "data-catalog";
export const USER_DATA_NAME = "my-data";
Expand All @@ -14,6 +16,9 @@ import { analyticsSetShowGuide } from "../ReactViews/Guide/Guide";
import { SATELLITE_GUIDE_KEY } from "../ReactViews/Guide/SatelliteGuide";
import { LOCAL_PROPERTY_KEY as WELCOME_PROPERTY_KEY } from "../ReactViews/WelcomeMessage/WelcomeMessage";

// import { ROOT_ROUTE, CATALOG_ROUTE } from "../ReactViewModels/TerriaRouting.js";
import { ROOT_ROUTE } from "../ReactViewModels/TerriaRouting.js";

/**
* Root of a global view model. Presumably this should get nested as more stuff goes into it. Basically this belongs to
* the root of the UI and then it can choose to pass either the whole thing or parts down as props to its children.
Expand All @@ -39,14 +44,15 @@ export default class ViewState {
this.terria = terria;
this.previewedItem = undefined;
this.userDataPreviewedItem = undefined;
this.explorerPanelIsVisible = false;
this.shareModalIsVisible = false;
this.activeTabCategory = DATA_CATALOG_NAME;
this.activeTabSubCategory = null; // Used to refer to an individual data-catalog tab
this.isDraggingDroppingFile = false;
this.mobileView = null;
this.isMapFullScreen = false;
this.myDataIsUploadView = true;
this.matchFromExplorer = {};
this.history = {};

/**
* Gets or sets a value indicating whether the small screen (mobile) user interface should be used.
Expand Down Expand Up @@ -107,6 +113,7 @@ export default class ViewState {

this.featurePrompts = [];

this._explorerPanelIsVisible = false;
/**
* The tool that will appear in the tool panel.
*/
Expand Down Expand Up @@ -143,9 +150,10 @@ export default class ViewState {
});

knockout.track(this, [
"matchFromExplorer",
"previewedItem",
"catalogSearch",
"explorerPanelIsVisible",
"_explorerPanelIsVisible",
"shareModalIsVisible",
"activeTabCategory",
"activeTabIdInCategory",
Expand Down Expand Up @@ -174,6 +182,21 @@ export default class ViewState {
"_showHelpMenu"
]);

knockout.defineProperty(this, "explorerPanelIsVisible", {
get: function() {
return this._explorerPanelIsVisible;
},
set: function(bool) {
if (!bool && this.location && this.location.pathname !== ROOT_ROUTE) {
setTimeout(() => {
this.history.push(ROOT_ROUTE);
this._explorerPanelIsVisible = false;
}, 300);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need a 300ms delay?

}
this._explorerPanelIsVisible = bool;
}
});

knockout.defineProperty(this, "chartIsOpen", {
get: function() {
const chartableItems = this.terria.catalog.chartableItems;
Expand Down Expand Up @@ -214,7 +237,6 @@ export default class ViewState {
});

// Reflect preview id from terria when loaded from an init source

this._sharedFromExplorerPanelSubscription = knockout
.getObservable(terria, "sharedFromExplorerPanel")
.subscribe(sharedFromExplorerPanel => {
Expand Down Expand Up @@ -292,6 +314,24 @@ export default class ViewState {

this._disclaimerHandler = new DisclaimerHandler(terria, this);

this._matchFromExplorerSubscription = knockout
.getObservable(this, "matchFromExplorer")
.subscribe(matchFromExplorer => {
if (
matchFromExplorer &&
matchFromExplorer.params &&
matchFromExplorer.params.catalogMemberId
) {
// viewCatalogMember handles which tab to activate
const idToFind = URI.decode(matchFromExplorer.params.catalogMemberId);
const catalogMember = terria.catalog.shareKeyIndex[idToFind];

if (defined(catalogMember)) {
this.viewCatalogMember(catalogMember);
}
}
});

this._storyPromptHandler = knockout
.getObservable(this, "storyShown")
.subscribe(storyShown => {
Expand Down Expand Up @@ -323,6 +363,7 @@ export default class ViewState {
}

dispose() {
this._matchFromExplorerSubscription.dispose();
this._sharedFromExplorerPanelSubscription.dispose();
this._previewedItemIdSubscription.dispose();
this._pickedFeaturesSubscription.dispose();
Expand All @@ -335,13 +376,26 @@ export default class ViewState {
this._shouldStartSatelliteGuidanceSubscription.dispose();
}

checkCatalogRoute() {
// buggy with current implementation of mobile-catalog logic
// if (defined(this.location) && defined(this.history)) {
// if (this.location.pathname === ROOT_ROUTE) {
// this.history.push(CATALOG_ROUTE);
// // would have done this if it syncs up with the mobile catalog, leaving commented out for now
// // this.explorerPanelIsVisible = true;
// }
// }
}

openAddData() {
this.checkCatalogRoute();
this.explorerPanelIsVisible = true;
this.activeTabCategory = DATA_CATALOG_NAME;
this.switchMobileView(this.mobileViewOptions.data);
}

openUserData() {
this.checkCatalogRoute();
this.explorerPanelIsVisible = true;
this.activeTabCategory = USER_DATA_NAME;
}
Expand Down
8 changes: 5 additions & 3 deletions lib/ReactViews/DataCatalog/CatalogGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import classNames from "classnames";

Expand All @@ -17,8 +18,8 @@ function CatalogGroup(props) {
const { t } = useTranslation();
return (
<li className={Styles.root}>
<button
type="button"
<Link
to={props.linkTo}
className={classNames(
Styles.btnCatalog,
{ [Styles.btnCatalogTopLevel]: props.topLevel },
Expand Down Expand Up @@ -49,7 +50,7 @@ function CatalogGroup(props) {
<Icon glyph={Icon.GLYPHS.closed} />
)}
</span>
</button>
</Link>
<If condition={props.removable}>
<button
type="button"
Expand Down Expand Up @@ -91,6 +92,7 @@ function CatalogGroup(props) {
CatalogGroup.propTypes = {
text: PropTypes.string,
title: PropTypes.string,
linkTo: PropTypes.string,
topLevel: PropTypes.bool,
open: PropTypes.bool,
loading: PropTypes.bool,
Expand Down
8 changes: 5 additions & 3 deletions lib/ReactViews/DataCatalog/CatalogItem.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import classNames from "classnames";
import Icon from "../Icon.jsx";
Expand Down Expand Up @@ -28,8 +29,8 @@ function CatalogItem(props) {
const stateToTitle = defaultValue(props.titleOverrides, STATE_TO_TITLE);
return (
<li className={classNames(Styles.root)}>
<button
type="button"
<Link
to={props.linkTo}
onClick={props.onTextClick}
title={props.title}
className={classNames(Styles.btnCatalogItem, {
Expand All @@ -38,7 +39,7 @@ function CatalogItem(props) {
})}
>
{props.text}
</button>
</Link>
<button
type="button"
onClick={props.onBtnClick}
Expand Down Expand Up @@ -66,6 +67,7 @@ CatalogItem.propTypes = {
selected: PropTypes.bool,
text: PropTypes.string,
title: PropTypes.string,
linkTo: PropTypes.string,
trashable: PropTypes.bool,
onTrashClick: PropTypes.func,
onBtnClick: PropTypes.func,
Expand Down
27 changes: 23 additions & 4 deletions lib/ReactViews/DataCatalog/DataCatalogGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from "react";
import createReactClass from "create-react-class";
import PropTypes from "prop-types";
import { withRouter } from "react-router-dom";
import URI from "urijs";
import addedByUser from "../../Core/addedByUser";
import removeUserAddedData from "../../Models/removeUserAddedData";
import CatalogGroup from "./CatalogGroup";
Expand All @@ -16,6 +18,7 @@ const DataCatalogGroup = createReactClass({
propTypes: {
group: PropTypes.object.isRequired,
viewState: PropTypes.object.isRequired,
match: PropTypes.object.isRequired,
/** Overrides whether to get the open state of the group from the group model or manage it internally */
manageIsOpenLocally: PropTypes.bool,
userData: PropTypes.bool,
Expand All @@ -38,6 +41,16 @@ const DataCatalogGroup = createReactClass({
};
},

componentDidMount() {
const group = this.props.group;
if (!group.isOpen) {
if (this.isSelected() && group.toggleOpen) {
// Toggle open when we initially navigate to a group
group.isOpen = true;
}
}
},

toggleStateIsOpen() {
this.setState({
isOpen: !this.state.isOpen
Expand Down Expand Up @@ -69,9 +82,14 @@ const DataCatalogGroup = createReactClass({
},

isSelected() {
return addedByUser(this.props.group)
? this.props.viewState.userDataPreviewedItem === this.props.group
: this.props.viewState.previewedItem === this.props.group;
const match = this.props.match || {};
const { params } = match;
return (
(addedByUser(this.props.group)
? this.props.viewState.userDataPreviewedItem === this.props.group
: this.props.viewState.previewedItem === this.props.group) ||
URI.decode(params.catalogMemberId) === this.props.group.uniqueId
);
},

getNameOrPrettyUrl() {
Expand All @@ -92,6 +110,7 @@ const DataCatalogGroup = createReactClass({
const { t } = this.props;
return (
<CatalogGroup
linkTo={URI.encode(group.uniqueId)}
text={this.getNameOrPrettyUrl()}
title={getAncestors(group)
.map(member => member.nameInCatalog)
Expand Down Expand Up @@ -125,4 +144,4 @@ const DataCatalogGroup = createReactClass({
}
});

module.exports = withTranslation()(DataCatalogGroup);
module.exports = withRouter(withTranslation()(DataCatalogGroup));
Loading