Skip to content

Commit 5d0aa9e

Browse files
committed
[frontend] refactor history logic into history.ts
1 parent cfb6eba commit 5d0aa9e

File tree

5 files changed

+95
-27
lines changed

5 files changed

+95
-27
lines changed

frontend/src/Tab.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { createState } from "dreamland/core";
22
import { StatefulClass } from "./StatefulClass";
33
import { scramjet } from "./main";
4-
import type { HistoryState } from "./history";
4+
import { History, injectHistoryEmulation } from "./history";
55

66
let id = 0;
77
export class Tab extends StatefulClass {
88
id: number;
99
title: string | null;
1010
frame: ScramjetFrame;
11-
url: string;
1211

1312
dragoffset: number;
1413
dragpos: number;
@@ -18,30 +17,45 @@ export class Tab extends StatefulClass {
1817
pos: number;
1918
icon: string;
2019

21-
history: HistoryState[];
20+
history: History;
2221

23-
constructor() {
24-
super(createState({}));
22+
constructor(public url: URL = new URL("puter://newtab")) {
23+
super(createState(new Object(Tab.prototype)));
2524
this.id = id++;
2625

27-
this.frame = scramjet.createFrame();
2826
this.title = null;
29-
this.url = "puter://blank";
27+
28+
this.history = new History(this);
29+
this.history.push(this.url, undefined);
30+
3031
this.icon = "/vite.svg";
3132

3233
this.dragoffset = -1;
3334
this.startdragpos = -1;
3435
this.dragpos = -1;
3536
this.width = 0;
3637
this.pos = 0;
37-
this.history = [];
38+
39+
const frame = scramjet.createFrame();
40+
injectHistoryEmulation(frame, this);
41+
42+
this.frame = frame;
43+
}
44+
45+
// only caller should be history.ts for this
46+
navigate(url: URL) {
47+
this.url = url;
48+
if (url.protocol == "puter:") {
49+
switch (url.host) {
50+
case "newtab":
51+
this.title = "New Tab";
52+
}
53+
} else {
54+
this.frame.go(url);
55+
}
3856
}
3957
}
4058

41-
// frame.addEventListener("navigate", (e) => {
42-
// console.error(e);
43-
// tab.url = frame.client.url.href;
44-
// });
4559
// frame.frame.addEventListener("load", (e) => {
4660
// tab.url = frame.client.url.href;
4761
// });

frontend/src/browser.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { createState, type Stateful } from "dreamland/core";
22
import { ThemeVars, type Theme } from "./theme";
3-
import { Tabs, Tab } from "./components/TabStrip";
3+
import { Tabs } from "./components/TabStrip";
44
import { Omnibox } from "./components/Omnibox";
55
import { scramjet } from "./main";
66
import iconAdd from "@ktibow/iconset-ion/add";
77
import { Shell } from "./components/Shell";
88
import { createMenu } from "./components/Menu";
99
import { createDelegate } from "./delegate";
1010
import { StatefulClass } from "./StatefulClass";
11+
import { Tab } from "./Tab";
1112

1213
export const pushTab = createDelegate<Tab>();
1314
export const popTab = createDelegate<Tab>();
@@ -55,15 +56,6 @@ export class Browser extends StatefulClass {
5556
return tab;
5657
}
5758

58-
navigate(url: string) {
59-
if (URL.canParse(url)) {
60-
this.activetab.frame.go(url);
61-
} else {
62-
const search = `https://google.com/search?q=${encodeURIComponent(url)}`;
63-
this.activetab.frame.go(search);
64-
}
65-
}
66-
6759
destroyTab(tab: Tab) {
6860
this.tabs = this.tabs.filter((t) => t !== tab);
6961
console.log(this.tabs);
@@ -96,10 +88,10 @@ export class Browser extends StatefulClass {
9688
<Omnibox
9789
tabUrl={use(this.activetab.url)}
9890
goBack={() => {
99-
this.activetab.frame.back();
91+
this.activetab.history.go(-1);
10092
}}
10193
goForwards={() => {
102-
this.activetab.frame.forward();
94+
this.activetab.history.go(1);
10395
}}
10496
refresh={() => {
10597
this.activetab.frame.reload();

frontend/src/history.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,71 @@ import type { Tab } from "./Tab";
33
// history api emulation
44
export type HistoryState = {
55
state: any;
6-
url: string;
6+
url: URL;
77
};
88

9-
export function injectHistoryEmulation(client: ScramjetClient, tab: Tab) {
9+
export class History {
10+
index: number = 0;
11+
states: HistoryState[] = [];
12+
13+
constructor(private tab: Tab) {}
14+
15+
push(url: URL, state: any, navigate: boolean = true): HistoryState {
16+
this.states.push({ url, state });
17+
this.index++;
18+
19+
if (navigate) this.tab.navigate(url);
20+
21+
return this.states[this.index];
22+
}
23+
replace(url: URL, state: any, navigate: boolean = true): HistoryState {
24+
if (this.index < this.states.length) {
25+
this.states[this.index] = { url, state };
26+
} else {
27+
this.push(url, state);
28+
}
29+
30+
if (navigate) this.tab.navigate(url);
31+
32+
return this.states[this.index];
33+
}
34+
go(delta: number, navigate: boolean = true): HistoryState {
35+
this.index += delta;
36+
if (this.index < 0) {
37+
this.index = 0;
38+
} else if (this.index >= this.states.length) {
39+
this.index = this.states.length - 1;
40+
}
41+
42+
if (navigate) this.tab.navigate(this.states[this.index].url);
43+
44+
return this.states[this.index];
45+
}
46+
canGoBack(): boolean {
47+
return this.index > 0;
48+
}
49+
canGoForward(): boolean {
50+
return this.index < this.states.length - 1;
51+
}
52+
}
53+
54+
export function injectHistoryEmulation(frame: ScramjetFrame, tab: Tab) {
55+
frame.addEventListener("navigate", (e) => {
56+
// this event is fired whenever location.href is set, or similar
57+
// importantly not fired when replaceState is called (we overwrite it ourselves in injectContext)
58+
59+
// behavior here is just to create a new history entry
60+
const url = new URL(e.url);
61+
tab.history.push(url, undefined, false);
62+
63+
console.log("History push from navigate", url, tab.history.states);
64+
});
65+
frame.frame.addEventListener("beforeunload", (e: BeforeUnloadEvent) => {
66+
console.log("History beforeunload", e);
67+
});
68+
}
69+
70+
function injectContext(client: ScramjetClient, tab: Tab) {
1071
client.Proxy("History.prototype.pushState", {
1172
apply(ctx) {
1273
console.log("STATE PUSH", ctx.args);

frontend/src/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ try {
5151
`Error mounting: ${"message" in err ? err.message : err}`
5252
)
5353
);
54+
console.error(err);
5455
}

frontend/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"strict": true,
2020
"noUnusedLocals": true,
2121
"noUnusedParameters": true,
22-
"erasableSyntaxOnly": true,
22+
"erasableSyntaxOnly": false,
2323
"noFallthroughCasesInSwitch": true,
2424
"noUncheckedSideEffectImports": true,
2525
"jsx": "react-jsx",

0 commit comments

Comments
 (0)