Skip to content

Commit 7050101

Browse files
committed
[frontend] save tab state
1 parent d301697 commit 7050101

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

frontend/src/Tab.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
addHistoryListeners,
66
History,
77
injectHistoryEmulation,
8+
type SerializedHistory,
89
} from "./history";
910
import { NewTab } from "./pages/NewTab";
1011
import { Playground } from "./pages/Playground";
@@ -13,6 +14,12 @@ import { About } from "./pages/About";
1314

1415
const requestInspectElement = createDelegate<[HTMLElement, Tab]>();
1516

17+
export type SerializedTab = {
18+
id: number;
19+
title: string | null;
20+
history: SerializedHistory;
21+
};
22+
1623
let id = 0;
1724
export class Tab extends StatefulClass {
1825
id: number;
@@ -74,6 +81,21 @@ export class Tab extends StatefulClass {
7481
this.devtoolsFrame = scramjet.createFrame();
7582
}
7683

84+
serialize(): SerializedTab {
85+
return {
86+
id: this.id,
87+
title: this.title,
88+
history: this.history.serialize(),
89+
};
90+
}
91+
deserialize(de: SerializedTab) {
92+
this.id = de.id;
93+
this.title = de.title;
94+
this.history.deserialize(de.history);
95+
console.log(this.history.states[this.history.index].url);
96+
this._directnavigate(this.history.states[this.history.index].url);
97+
}
98+
7799
// only caller should be history.ts for this
78100
_directnavigate(url: URL) {
79101
this.url = url;

frontend/src/browser.tsx

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,25 @@ import { createState, type Stateful } from "dreamland/core";
22
import { ThemeVars, type Theme } from "./theme";
33
import { Tabs } from "./components/TabStrip";
44
import { Omnibox } from "./components/Omnibox";
5-
import { scramjet } from "./main";
5+
import { browser, 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 { StatefulClass } from "./StatefulClass";
10-
import { Tab } from "./Tab";
10+
import { Tab, type SerializedTab } from "./Tab";
1111
import { createDelegate } from "dreamland/core";
1212

1313
export const pushTab = createDelegate<Tab>();
1414
export const popTab = createDelegate<Tab>();
1515
export const forceScreenshot = createDelegate<Tab>();
1616

17+
export const saveBrowserState = createDelegate<void>();
18+
19+
export type SerializedBrowser = {
20+
tabs: SerializedTab[];
21+
activetab: number;
22+
};
23+
1724
export class Browser extends StatefulClass {
1825
built: boolean = false;
1926

@@ -49,6 +56,23 @@ export class Browser extends StatefulClass {
4956
};
5057
}
5158

59+
serialize(): SerializedBrowser {
60+
return {
61+
tabs: this.tabs.map((t) => t.serialize()),
62+
activetab: this.activetab.id,
63+
};
64+
}
65+
deserialize(de: SerializedBrowser) {
66+
this.tabs = [];
67+
for (let detab of de.tabs) {
68+
let tab = this.newTab();
69+
console.log(tab);
70+
tab.deserialize(detab);
71+
}
72+
this.activetab = this.tabs.find((t) => t.id == de.activetab)!;
73+
console.log(this.activetab, this.activetab.url);
74+
}
75+
5276
newTab() {
5377
let tab = new Tab();
5478
pushTab(tab);
@@ -69,8 +93,10 @@ export class Browser extends StatefulClass {
6993
build(): HTMLElement {
7094
let shell = <Shell tabs={use(this.tabs)} activetab={use(this.activetab)} />;
7195

72-
let tab = this.newTab();
73-
this.activetab = tab;
96+
if (!this.activetab) {
97+
let tab = this.newTab();
98+
this.activetab = tab;
99+
}
74100
if (this.built) throw new Error("already built");
75101
this.built = true;
76102

@@ -115,5 +141,16 @@ export class Browser extends StatefulClass {
115141
export function createBrowser(): Browser {
116142
let browser = new Browser(createState({}));
117143
Object.setPrototypeOf(browser, Browser.prototype);
144+
let de = localStorage["browserstate"];
145+
if (de) {
146+
browser.deserialize(JSON.parse(de));
147+
}
148+
118149
return browser;
119150
}
151+
152+
saveBrowserState.listen(() => {
153+
let ser = browser.serialize();
154+
localStorage["browserstate"] = JSON.stringify(ser);
155+
});
156+
setInterval(saveBrowserState, 1000);

frontend/src/history.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,34 @@ export type HistoryState = {
66
url: URL;
77
};
88

9+
export type SerializedHistory = {
10+
index: number;
11+
states: {
12+
state: any;
13+
url: string;
14+
}[];
15+
};
16+
917
export class History {
1018
index: number = -1;
1119
states: HistoryState[] = [];
1220

1321
constructor(private tab: Tab) {}
1422

23+
serialize(): SerializedHistory {
24+
return {
25+
index: this.index,
26+
states: this.states.map((s) => ({ state: s.state, url: s.url.href })),
27+
};
28+
}
29+
deserialize(de: SerializedHistory) {
30+
this.index = de.index;
31+
this.states = de.states.map((s) => ({
32+
state: s.state,
33+
url: new URL(s.url),
34+
}));
35+
}
36+
1537
push(url: URL, state: any = null, navigate: boolean = true): HistoryState {
1638
this.states.push({ url, state });
1739
this.index++;

0 commit comments

Comments
 (0)