Skip to content

Commit

Permalink
new dirty cut handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Mårten Wikström committed Feb 21, 2018
1 parent 9732c70 commit c8f7cc0
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 168 deletions.
37 changes: 13 additions & 24 deletions src/Cutter.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import { IHistory, ILocation, IWrappedState } from "./api";
import { IHistory, ILocation } from "./api";
import { isWrappedLocation } from "./isWrappedLocation";
import { Mutator } from "./Mutator";
import { Suppressor } from "./Suppressor";
import { wrapLocation } from "./wrapLocation";

export class Cutter {
private static createDirtyCut = (
curr: ILocation<IWrappedState>,
how: "before" | "here",
): ILocation<IWrappedState> => ({
...curr,
key: undefined,
state: {
...curr.state,
meta: {
...curr.state.meta,
cut: how,
},
},
})
const NOGO_STATE_VALUE = "__NOGO__";

export const isBackOutLocation = (location: ILocation) => location.state === NOGO_STATE_VALUE;

export const makeBackOutLocation = (input: ILocation) => ({
...input,
state: NOGO_STATE_VALUE,
});

export class Cutter {
constructor(
private source: IHistory,
private suppressor: Suppressor,
Expand All @@ -39,7 +32,7 @@ export class Cutter {

if (isWrappedLocation(this.source.location)) {
const meta = this.source.location.state.meta;
ok = meta.depth > 0 || meta.cut === "before";
ok = meta.depth > 0;
}

return ok;
Expand All @@ -55,11 +48,7 @@ export class Cutter {
}

private async makeDirtyCut() {
const curr = isWrappedLocation(this.source.location) ?
this.source.location : wrapLocation(this.source.location);
const next = Cutter.createDirtyCut(curr, "before");
const prev = Cutter.createDirtyCut(curr, "here");
await this.mutator.update(() => this.source.replace(prev));
await this.mutator.update(() => this.source.push(next));
const nogo = makeBackOutLocation(this.source.location);
await this.mutator.update(() => this.source.push(nogo));
}
}
15 changes: 9 additions & 6 deletions src/Mutator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IHistory, ILocation, PUSH, REPLACE } from "./api";
import { isBackOutLocation } from "./Cutter";
import { nextLocation } from "./nextLocation";
import { nextState } from "./nextState";

Expand Down Expand Up @@ -29,12 +30,14 @@ export class Mutator {
}
};

const unlisten = this.source.listen(() => {
try {
unlisten();
resolve();
} finally {
this.currentRejectFunc = null;
const unlisten = this.source.listen(location => {
if (!isBackOutLocation(location)) {
try {
unlisten();
resolve();
} finally {
this.currentRejectFunc = null;
}
}
});

Expand Down
7 changes: 1 addition & 6 deletions src/Scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ export class Scanner {
};

if (typeof match === "undefined") {
if (isWrappedLocation(this.source.location) && this.source.location.state.meta.cut === "before") {
result.delta = -2;
} else {
result.delta = -1;
}

result.delta = -1;
return result;
}

Expand Down
17 changes: 15 additions & 2 deletions src/Tracker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IHistory, ILocation, NavigationAction, UnregisterCallback } from "./api";

import { isBackOutLocation } from "./Cutter";
import { isWrappedLocation } from "./isWrappedLocation";
import { Mutator } from "./Mutator";
import { Suppressor } from "./Suppressor";
Expand Down Expand Up @@ -27,7 +28,7 @@ export class Tracker {
public get ready() { return !!this.unlisten; }

public start = async () => {
await this.setupLocation();
await this.setup();

if (!this.unlisten) {
this.unlisten = this.source.listen(this.onLocationChanged);
Expand All @@ -41,7 +42,14 @@ export class Tracker {
}
}

private async setupLocation() {
private async setup() {
this.trackedLocation = this.source.location;
this.trackedAction = this.source.action;

if (isBackOutLocation(this.trackedLocation)) {
await this.mutator.update(() => this.source.goBack());
}

if (isWrappedLocation(this.trackedLocation)) {
const meta = this.trackedLocation.state.meta;
this.trackedDepth = meta.depth;
Expand All @@ -57,6 +65,11 @@ export class Tracker {
location: ILocation,
action: NavigationAction,
) => {
if (isBackOutLocation(location)) {
this.source.goBack();
return;
}

if (this.suppressor.isActive) {
return;
}
Expand Down
1 change: 0 additions & 1 deletion src/api/IMetaState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export interface IMetaState {
depth: number;
cache: string[];
cut?: "here" | "before";
}
80 changes: 10 additions & 70 deletions src/createAppHistory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createMemoryHistory, MemoryHistory } from "history";

import { IAppHistoryOptions, POP, PUSH, REPLACE } from "./api";
import { createAppHistory } from "./createAppHistory";
import { isBackOutLocation } from "./Cutter";

const createAndInitAppHistory = async (options?: IAppHistoryOptions) => {
const history = createAppHistory(options);
Expand Down Expand Up @@ -401,89 +402,28 @@ describe("createAppHistory", async () => {
await history.cut();
expect(history.location.pathname).toBe("/a");
expect(history.length).toBe(3);
expect(source.index).toBe(2);
expect(source.index).toBe(1);

await history.goForward();
await history.goForward(); // shall back out again
expect(history.location.pathname).toBe("/a");
expect(history.length).toBe(3);
expect(source.index).toBe(2);

expect(source.entries[1].state.meta.cut).toBe("here");
expect(source.entries[2].state.meta.cut).toBe("before");
});

it("can go back through (and skip over) dirty cut point", async () => {
const history = await createAndInitAppHistory({mode: "memory"});
const source = history.source as MemoryHistory;
source.push("a");

await history.push("b");
expect(source.index).toBe(2);

await history.goBack();
expect(source.index).toBe(1);

await history.cut();
expect(source.index).toBe(2);

expect(source.entries[1].state.meta.cut).toBe("here");
expect(source.entries[2].state.meta.cut).toBe("before");

await history.goBack();
expect(source.index).toBe(0);
expect(source.entries[2].state).toBe("__NOGO__");
});

it("can be initialized on dirty cut point", async () => {
const history = await createAndInitAppHistory({mode: "memory"});
it("cannot be initialized on no-go location (will back out)", async () => {
const history = createAppHistory({mode: "memory"});
const source = history.source as MemoryHistory;
source.push("a");
source.replace("/a");
source.push("/b", "__NOGO__");

await history.push("b");
expect(source.index).toBe(2);

await history.goBack();
expect(source.index).toBe(1);
expect(isBackOutLocation(source.location)).toBe(true);

await history.cut();
expect(source.index).toBe(2);
await history.init();

await history.go(-2);
expect(source.index).toBe(0);

expect(source.entries[1].state.meta.cut).toBe("here");
expect(source.entries[2].state.meta.cut).toBe("before");

history.suppress();

source.goForward();
expect(source.index).toBe(1);
});

it("can make dirty cut on tampered state", async () => {
const history = await createAndInitAppHistory({mode: "memory"});
const source = history.source as MemoryHistory;
source.push("a");

await history.push("b");
expect(source.index).toBe(2);

await history.goBack();
expect(source.index).toBe(1);

source.replace("a", "tampered");
await history.cut();
expect(source.index).toBe(2);

await history.go(-2);
expect(source.index).toBe(0);

expect(source.entries[1].state.meta.cut).toBe("here");
expect(source.entries[2].state.meta.cut).toBe("before");

history.suppress();

source.goForward();
expect(source.index).toBe(1);
});

it("can go back to matched pattern", async () => {
Expand Down
54 changes: 0 additions & 54 deletions src/nextMetaState.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,6 @@ describe("nextMetaState", () => {
});
});

it("can push with cut", () => {
const source = createMemoryHistory();
const initialState: IMetaState = {
cache: ["/foo", "/bar"],
cut: "here",
depth: 123,
};

source.replace("baz", wrapState(null, initialState));
const nextState = nextMetaState(source, PUSH, -1);

expect(nextState).toMatchObject({
cache: ["/foo", "/bar", "/baz"],
cut: "before",
depth: 124,
});
});

it("can replace", () => {
const source = createMemoryHistory();
const initialState = {
Expand All @@ -92,42 +74,6 @@ describe("nextMetaState", () => {
});
});

it("can replace with cut here", () => {
const source = createMemoryHistory();
const initialState: IMetaState = {
cache: ["/foo", "/bar"],
cut: "here",
depth: 123,
};

source.replace("baz", wrapState(null, initialState));
const nextState = nextMetaState(source, REPLACE, -1);

expect(nextState).toMatchObject({
cache: ["/foo", "/bar"],
cut: "here",
depth: 123,
});
});

it("can replace with cut before", () => {
const source = createMemoryHistory();
const initialState: IMetaState = {
cache: ["/foo", "/bar"],
cut: "before",
depth: 123,
};

source.replace("baz", wrapState(null, initialState));
const nextState = nextMetaState(source, REPLACE, -1);

expect(nextState).toMatchObject({
cache: ["/foo", "/bar"],
cut: "before",
depth: 123,
});
});

it("can pop", () => {
const source = createMemoryHistory();
const initialState = {
Expand Down
5 changes: 0 additions & 5 deletions src/nextMetaState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ function afterPush(meta: IMetaState, path: string): IMetaState {
depth: meta.depth + 1,
};

if (meta.cut === "here") {
next.cut = "before";
}

return next;
}

Expand All @@ -57,7 +53,6 @@ function afterPop(meta: IMetaState): IMetaState {
function afterReplace(meta: IMetaState): IMetaState {
return {
cache: meta.cache.slice(),
cut: meta.cut,
depth: meta.depth,
};
}

0 comments on commit c8f7cc0

Please sign in to comment.