summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pesse <samypesse@gmail.com>2016-10-03 00:59:26 +0200
committerSamy Pesse <samypesse@gmail.com>2016-10-03 00:59:26 +0200
commit3ae72bb47c146212d40fc74d857880fa0616ae57 (patch)
tree28986218bb512580272f9709fbdee01f6ede52dd
parentcd2d5e5101edb466b13ada19b09ea42ef726ad96 (diff)
downloadgitbook-3ae72bb47c146212d40fc74d857880fa0616ae57.zip
gitbook-3ae72bb47c146212d40fc74d857880fa0616ae57.tar.gz
gitbook-3ae72bb47c146212d40fc74d857880fa0616ae57.tar.bz2
Sync search with querystring
-rw-r--r--docs/api/i18n.md4
-rw-r--r--docs/api/navigation.md21
-rw-r--r--packages/gitbook-core/package.json1
-rw-r--r--packages/gitbook-core/src/actions/TYPES.js2
-rw-r--r--packages/gitbook-core/src/actions/navigation.js61
-rw-r--r--packages/gitbook-core/src/models/Location.js73
-rw-r--r--packages/gitbook-core/src/reducers/navigation.js13
-rw-r--r--packages/gitbook-plugin-search/src/actions/search.js15
-rw-r--r--packages/gitbook-plugin-search/src/index.js13
9 files changed, 160 insertions, 43 deletions
diff --git a/docs/api/i18n.md b/docs/api/i18n.md
index 026a2d5..b58f1e8 100644
--- a/docs/api/i18n.md
+++ b/docs/api/i18n.md
@@ -8,8 +8,8 @@ The first step is to register messages for a language:
```js
module.exports = GitBook.createPlugin({
- init: (dispatch) => {
- dispatch(GitBook.registerLocale('en-US', {
+ init: (dispatch, getState, { I18n }) => {
+ dispatch(I18n.registerLocale('en-US', {
MY_PLUGIN_MESSAGE: 'Hello World'
}));
}
diff --git a/docs/api/navigation.md b/docs/api/navigation.md
new file mode 100644
index 0000000..667aa66
--- /dev/null
+++ b/docs/api/navigation.md
@@ -0,0 +1,21 @@
+# Navigation
+
+### Listen to url change
+
+Listen for changes to the current location:
+
+```js
+const onLocationChanged = (location) => {
+ console.log(location.pathname);
+ console.log(location.query);
+ console.log(location.hash);
+};
+
+module.exports = GitBook.createPlugin({
+ init: (dispatch, getState, { Navigation }) => {
+ dispatch(Navigation.listen(onLocationChanged));
+ }
+});
+```
+
+The `onLocationChanged` will be triggered for initial state.
diff --git a/packages/gitbook-core/package.json b/packages/gitbook-core/package.json
index 1bda456..c6cfeed 100644
--- a/packages/gitbook-core/package.json
+++ b/packages/gitbook-core/package.json
@@ -6,6 +6,7 @@
"dependencies": {
"bluebird": "^3.4.6",
"classnames": "^2.2.5",
+ "history": "^4.3.0",
"html-tags": "^1.1.1",
"immutable": "^3.8.1",
"react": "^15.3.1",
diff --git a/packages/gitbook-core/src/actions/TYPES.js b/packages/gitbook-core/src/actions/TYPES.js
index 3177dbd..e758a44 100644
--- a/packages/gitbook-core/src/actions/TYPES.js
+++ b/packages/gitbook-core/src/actions/TYPES.js
@@ -7,8 +7,6 @@ module.exports = {
PAGE_FETCH_START: 'navigation/fetch:start',
PAGE_FETCH_END: 'navigation/fetch:end',
PAGE_FETCH_ERROR: 'navigation/fetch:error',
- PAGE_UPDATE_ANCHOR: 'navigation/update:anchor',
- PAGE_UPDATE_QUERY: 'navigation/update:query',
// i18n
I18N_REGISTER_LOCALE: 'i18n/register:locale'
};
diff --git a/packages/gitbook-core/src/actions/navigation.js b/packages/gitbook-core/src/actions/navigation.js
index f13e878..4afd4fb 100644
--- a/packages/gitbook-core/src/actions/navigation.js
+++ b/packages/gitbook-core/src/actions/navigation.js
@@ -1,5 +1,10 @@
+const { createBrowserHistory, createMemoryHistory } = require('history');
+
const ACTION_TYPES = require('./TYPES');
const getPayload = require('../lib/getPayload');
+const Location = require('../models/Location');
+
+const isServerSide = (typeof window === 'undefined');
const SUPPORTED = (
typeof window !== 'undefined' &&
@@ -8,48 +13,39 @@ const SUPPORTED = (
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
);
-let PUSH_ID = 0;
-
-/**
- * Generate a new state to be pushed or replaced
- * @param {Object}
- */
-function genState() {
- return {
- id: (PUSH_ID++)
- };
-}
+// Create tge history instance
+const history = isServerSide ? createMemoryHistory() : createBrowserHistory();
/**
* Push a new url into the navigation
- * @param {String} uri
+ * @param {String|Location} location
* @return {Action} action
*/
-function pushURI(uri) {
+function pushURI(location) {
return () => {
- const state = genState();
+ location = Location.fromNative(location);
if (SUPPORTED) {
- window.history.pushState(state, '', uri);
+ history.push(location.toNative());
} else {
- redirect(uri);
+ redirect(location.toString());
}
};
}
/**
* Replace current state in navigation
- * @param {String} uri
+ * @param {String|Location} location
* @return {Action} action
*/
-function replaceURI(uri) {
+function replaceURI(location) {
return () => {
- const state = genState();
+ location = Location.fromNative(location);
if (SUPPORTED) {
- window.history.replaceState(state, '', uri);
+ history.replace(location.toNative());
} else {
- redirect(uri);
+ redirect(location.toString());
}
};
}
@@ -66,6 +62,20 @@ function redirect(uri) {
}
/**
+ * Listen to url change
+ * @param {Function} fn
+ * @return {Action} action
+ */
+function listen(fn) {
+ return (dispatch, getState) => {
+ history.listen(location => {
+ location = Location.fromNative(location);
+ fn(location, dispatch, getState);
+ });
+ };
+}
+
+/**
* Fetch a new page and update the store accordingly
* @param {String} uri
* @param {Boolean} options.replace
@@ -125,11 +135,11 @@ function fetchArticle(article) {
/**
* Update anchor for current page
- * @param {String} anchor
+ * @param {String} hash
* @return {Action} action
*/
-function updateAnchor(anchor) {
- return { type: ACTION_TYPES.PAGE_UPDATE_ANCHOR, anchor };
+function updateAnchor(hash) {
+ return pushURI({ hash });
}
/**
@@ -138,10 +148,11 @@ function updateAnchor(anchor) {
* @return {Action} action
*/
function updateQuery(query) {
- return { type: ACTION_TYPES.PAGE_UPDATE_QUERY, query };
+ return pushURI({ query });
}
module.exports = {
+ listen,
pushURI,
fetchPage,
fetchArticle,
diff --git a/packages/gitbook-core/src/models/Location.js b/packages/gitbook-core/src/models/Location.js
new file mode 100644
index 0000000..3b62bef
--- /dev/null
+++ b/packages/gitbook-core/src/models/Location.js
@@ -0,0 +1,73 @@
+const { Record, Map } = require('immutable');
+const querystring = require('querystring');
+
+const DEFAULTS = {
+ pathname: String(''),
+ // Hash without the #
+ hash: String(''),
+ // If query is a non empty map
+ query: Map()
+};
+
+class Location extends Record(DEFAULTS) {
+ get search() {
+ const { query } = this;
+ return query.size === 0 ?
+ '' :
+ '?' + querystring.stringify(query.toJS());
+ }
+
+ /**
+ * Convert this location to a string.
+ * @return {String}
+ */
+ toString() {
+
+ }
+
+ /**
+ * Convert this immutable instance to an object
+ * for "history".
+ * @return {Object}
+ */
+ toNative() {
+ return {
+ pathname: this.pathname,
+ hash: this.hash ? `#${this.hash}` : '',
+ search: this.search
+ };
+ }
+
+ /**
+ * Convert an instance from "history" to Location.
+ * @param {Object|String} location
+ * @return {Location}
+ */
+ static fromNative(location) {
+ if (typeof location === 'string') {
+ location = { pathname: location };
+ }
+
+ const pathname = location.pathname;
+ let hash = location.hash || '';
+ let search = location.search || '';
+ let query = location.query;
+
+ hash = hash[0] === '#' ? hash.slice(1) : hash;
+ search = search[0] === '?' ? search.slice(1) : search;
+
+ if (query) {
+ query = Map(query);
+ } else {
+ query = Map(querystring.parse(search));
+ }
+
+ return new Location({
+ pathname,
+ hash,
+ query
+ });
+ }
+}
+
+module.exports = Location;
diff --git a/packages/gitbook-core/src/reducers/navigation.js b/packages/gitbook-core/src/reducers/navigation.js
index 625adef..9831cd5 100644
--- a/packages/gitbook-core/src/reducers/navigation.js
+++ b/packages/gitbook-core/src/reducers/navigation.js
@@ -1,15 +1,11 @@
-const { Record, Map } = require('immutable');
+const { Record } = require('immutable');
const ACTION_TYPES = require('../actions/TYPES');
const NavigationState = Record({
// Are we loading a new page
loading: Boolean(false),
// Did we fail loading a page?
- error: null,
- // Query string
- query: Map(),
- // Current anchor
- anchor: String('')
+ error: null
});
function reduceNavigation(state, action) {
@@ -32,11 +28,6 @@ function reduceNavigation(state, action) {
error: action.error
});
- case ACTION_TYPES.PAGE_UPDATE_ANCHOR:
- return state.merge({
- anchor: action.anchor
- });
-
default:
return state;
diff --git a/packages/gitbook-plugin-search/src/actions/search.js b/packages/gitbook-plugin-search/src/actions/search.js
index 7d42917..b4f811e 100644
--- a/packages/gitbook-plugin-search/src/actions/search.js
+++ b/packages/gitbook-plugin-search/src/actions/search.js
@@ -8,14 +8,24 @@ const TYPES = require('./types');
* @return {Action}
*/
function query(q) {
+ return (dispatch, getState, { Navigation }) => {
+ dispatch(Navigation.updateQuery({ q }));
+ };
+}
+
+
+/**
+ * Update results for a query
+ * @param {String} q
+ * @return {Action}
+ */
+function handleQuery(q) {
if (!q) {
return clear();
}
return (dispatch, getState, { Navigation }) => {
const { handlers } = getState().search;
-
- dispatch(Navigation.updateQuery({ q }));
dispatch({ type: TYPES.START, query: q });
return Promise.reduce(
@@ -64,6 +74,7 @@ function unregisterHandler(name) {
module.exports = {
clear,
query,
+ handleQuery,
registerHandler,
unregisterHandler
};
diff --git a/packages/gitbook-plugin-search/src/index.js b/packages/gitbook-plugin-search/src/index.js
index f08d5d6..05c2056 100644
--- a/packages/gitbook-plugin-search/src/index.js
+++ b/packages/gitbook-plugin-search/src/index.js
@@ -5,8 +5,19 @@ const SearchResults = require('./components/Results');
const reducers = require('./reducers');
const Search = require('./actions/search');
+const onLocationChange = (location, dispatch) => {
+ const { query } = location;
+ const q = query.get('q');
+
+ dispatch(Search.handleQuery(q));
+};
+
module.exports = GitBook.createPlugin({
- init: (dispatch, getState, { Components }) => {
+ init: (dispatch, getState, { Navigation, Components }) => {
+ // Register the navigation handler
+ dispatch(Navigation.listen(onLocationChange));
+
+ // Register components
dispatch(Components.registerComponent(SearchInput, { role: 'search:input' }));
dispatch(Components.registerComponent(SearchResults, { role: 'search:results' }));
},