summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-10-30 10:24:55 +0100
committerOskar Nyberg <oskar@mullvad.net>2020-10-30 14:53:56 +0100
commit2908bdf3e61c83a447d7a2784c439ac7a7d502e1 (patch)
tree062d5b1264f3f0233d6af7247c4d1149f9370c89 /gui/src
parent01f0dc92f693976bc02b6d50c322b8cb3de1e514 (diff)
downloadmullvadvpn-2908bdf3e61c83a447d7a2784c439ac7a7d502e1.tar.xz
mullvadvpn-2908bdf3e61c83a447d7a2784c439ac7a7d502e1.zip
Add custom history implementation
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/app.tsx8
-rw-r--r--gui/src/renderer/lib/history.ts112
-rw-r--r--gui/src/renderer/redux/store.ts2
3 files changed, 117 insertions, 5 deletions
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index b7c9b1a187..e76eec0453 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -5,7 +5,6 @@ import {
} from 'connected-react-router';
import { ipcRenderer, shell, webFrame } from 'electron';
import log from 'electron-log';
-import { createMemoryHistory } from 'history';
import * as React from 'react';
import { Provider } from 'react-redux';
import { bindActionCreators } from 'redux';
@@ -29,6 +28,7 @@ import { IpcRendererEventChannel, IRelayListPair } from '../shared/ipc-event-cha
import ISplitTunnelingApplication from '../shared/linux-split-tunneling-application';
import { getRendererLogFile, setupLogging } from '../shared/logging';
import consumePromise from '../shared/promise';
+import History from './lib/history';
import {
AccountToken,
@@ -76,8 +76,8 @@ const SUPPORTED_LOCALE_LIST = [
];
export default class AppRenderer {
- private memoryHistory = createMemoryHistory();
- private reduxStore = configureStore(this.memoryHistory);
+ private history = new History('/');
+ private reduxStore = configureStore(this.history);
private reduxActions = {
account: bindActionCreators(accountActions, this.reduxStore.dispatch),
connection: bindActionCreators(connectionActions, this.reduxStore.dispatch),
@@ -229,7 +229,7 @@ export default class AppRenderer {
return (
<AppContext.Provider value={{ app: this }}>
<Provider store={this.reduxStore}>
- <ConnectedRouter history={this.memoryHistory}>
+ <ConnectedRouter history={this.history}>
<ErrorBoundary>
<AppRoutes />
</ErrorBoundary>
diff --git a/gui/src/renderer/lib/history.ts b/gui/src/renderer/lib/history.ts
new file mode 100644
index 0000000000..b726e72bbb
--- /dev/null
+++ b/gui/src/renderer/lib/history.ts
@@ -0,0 +1,112 @@
+import { Location, Action, LocationListener, LocationDescriptor } from 'history';
+
+// It currently isn't possible to implement this correctly with support for a generic state. State
+// can be added as a generic type (<S = unknown>) after this issue has been resolved:
+// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/49060
+type S = unknown;
+export default class History {
+ private listeners: LocationListener<S>[] = [];
+ private entries: Location<S>[];
+ private index = 0;
+ private lastAction: Action = 'POP';
+
+ public constructor(location: string | Location<S>, state?: S) {
+ this.entries = [this.createLocation(location, state)];
+ }
+
+ public get location(): Location<S> {
+ return this.entries[this.index];
+ }
+
+ public get length(): number {
+ return this.entries.length;
+ }
+
+ public get action(): Action {
+ return this.lastAction;
+ }
+
+ public push = (nextLocation: LocationDescriptor<S>, nextState?: S) => {
+ const location = this.createLocation(nextLocation, nextState);
+ this.lastAction = 'PUSH';
+ this.index += 1;
+ this.entries.splice(this.index, this.entries.length - this.index, location);
+ this.notify();
+ };
+
+ public replace = (nextLocation: LocationDescriptor<S>, nextState?: S) => {
+ this.entries[this.index] = this.createLocation(nextLocation, nextState);
+ this.lastAction = 'REPLACE';
+ this.notify();
+ };
+
+ public go = (n: number) => {
+ if (this.canGo(n)) {
+ this.index += n;
+ this.lastAction = 'POP';
+ this.notify();
+ }
+ };
+
+ public goBack = () => this.go(-1);
+ public goForward = () => this.go(1);
+
+ public reset = () => {
+ this.lastAction = 'POP';
+ this.index = 0;
+ this.notify();
+ };
+
+ public resetWith = (nextLocation: LocationDescriptor<S>, nextState?: S) => {
+ this.entries = [this.createLocation(nextLocation, nextState)];
+ this.lastAction = 'REPLACE';
+ this.index = 0;
+ this.notify();
+ };
+
+ public canGo(n: number) {
+ const nextIndex = this.index + n;
+ return nextIndex >= 0 && nextIndex < this.entries.length;
+ }
+
+ public listen(callback: LocationListener<S>) {
+ this.listeners.push(callback);
+ return () => (this.listeners = this.listeners.filter((listener) => listener !== callback));
+ }
+
+ public block(): () => void {
+ throw Error('Not implemented');
+ }
+
+ public createHref(): string {
+ throw Error('Not implemented');
+ }
+
+ private notify() {
+ this.listeners.forEach((listener) => listener(this.location, this.action));
+ }
+
+ private createLocation(location: LocationDescriptor<S>, state?: S): Location<S> {
+ if (typeof location === 'object') {
+ return {
+ pathname: location.pathname ?? this.location.pathname,
+ search: location.search ?? '',
+ hash: location.hash ?? '',
+ state: location.state,
+ key: location.key ?? this.getRandomKey(),
+ };
+ } else {
+ return {
+ pathname: location,
+ search: '',
+ hash: '',
+ state,
+ key: this.getRandomKey(),
+ };
+ }
+ }
+
+ private getRandomKey() {
+ return Math.random().toString(36).substr(8);
+ }
+}
diff --git a/gui/src/renderer/redux/store.ts b/gui/src/renderer/redux/store.ts
index bedaddf1bd..43ebc312ef 100644
--- a/gui/src/renderer/redux/store.ts
+++ b/gui/src/renderer/redux/store.ts
@@ -14,7 +14,7 @@ import userInterfaceReducer, { IUserInterfaceReduxState } from './userinterface/
import versionActions, { VersionAction } from './version/actions';
import versionReducer, { IVersionReduxState } from './version/reducers';
-import { History } from 'history';
+import History from '../lib/history';
export interface IReduxState {
account: IAccountReduxState;