compress state in url fragment w/ gzip now the APIs are standard
This commit is contained in:
parent
efda9c989e
commit
8efdd1f4fe
4 changed files with 46 additions and 10 deletions
|
|
@ -52,12 +52,12 @@ declare var BrowserFS: BrowserFSInterface
|
||||||
console.log('Failed to read the persisted state from local storage.', e)
|
console.log('Failed to read the persisted state from local storage.', e)
|
||||||
}
|
}
|
||||||
statePersister = {
|
statePersister = {
|
||||||
set: ({view, params}) => {
|
set: async ({view, params}) => {
|
||||||
fs.writeFile('/state.json', JSON.stringify({view, params}));
|
fs.writeFile('/state.json', JSON.stringify({view, params}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
persistedState = readStateFromFragment();
|
persistedState = await readStateFromFragment();
|
||||||
statePersister = {
|
statePersister = {
|
||||||
set: writeStateInFragment,
|
set: writeStateInFragment,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export interface State {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface StatePersister {
|
export interface StatePersister {
|
||||||
set(state: State): void;
|
set(state: State): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {}
|
export {}
|
||||||
|
|
@ -7,19 +7,55 @@ import { defaultModelColor } from "./initial-state";
|
||||||
export function buildUrlForStateParams(state: State) {//partialState: {params: State['params'], view: State['view']}) {
|
export function buildUrlForStateParams(state: State) {//partialState: {params: State['params'], view: State['view']}) {
|
||||||
return `${location.protocol}//${location.host}${location.pathname}#${encodeStateParamsAsFragment(state)}`;
|
return `${location.protocol}//${location.host}${location.pathname}#${encodeStateParamsAsFragment(state)}`;
|
||||||
}
|
}
|
||||||
export function writeStateInFragment(state: State) {
|
export async function writeStateInFragment(state: State) {
|
||||||
window.location.hash = encodeStateParamsAsFragment(state);
|
window.location.hash = await encodeStateParamsAsFragment(state);
|
||||||
}
|
}
|
||||||
|
async function compressString(input: string): Promise<string> {
|
||||||
|
const stream = new ReadableStream<Uint8Array>({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(new TextEncoder().encode(input));
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
|
||||||
|
const compressedData = await new Response(compressedStream).arrayBuffer();
|
||||||
|
return btoa(String.fromCharCode(...new Uint8Array(compressedData)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decompressString(compressedInput: string): Promise<string> {
|
||||||
|
const compressedData = Uint8Array.from(atob(compressedInput), c => c.charCodeAt(0));
|
||||||
|
const stream = new ReadableStream<Uint8Array>({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(compressedData);
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const decompressedStream = stream.pipeThrough(new DecompressionStream('gzip'));
|
||||||
|
const decompressedData = await new Response(decompressedStream).arrayBuffer();
|
||||||
|
return new TextDecoder().decode(decompressedData);
|
||||||
|
}
|
||||||
|
|
||||||
export function encodeStateParamsAsFragment(state: State) {
|
export function encodeStateParamsAsFragment(state: State) {
|
||||||
return encodeURIComponent(JSON.stringify({
|
const json = JSON.stringify({
|
||||||
params: state.params,
|
params: state.params,
|
||||||
view: state.view
|
view: state.view
|
||||||
}));
|
});
|
||||||
|
// return encodeURIComponent(json);
|
||||||
|
return compressString(json);
|
||||||
}
|
}
|
||||||
export function readStateFromFragment(): State | null {
|
export async function readStateFromFragment(): Promise<State | null> {
|
||||||
if (window.location.hash.startsWith('#') && window.location.hash.length > 1) {
|
if (window.location.hash.startsWith('#') && window.location.hash.length > 1) {
|
||||||
try {
|
try {
|
||||||
const {params, view} = JSON.parse(decodeURIComponent(window.location.hash.substring(1)));
|
const serialized = window.location.hash.substring(1);
|
||||||
|
let obj;
|
||||||
|
try {
|
||||||
|
obj = JSON.parse(await decompressString(serialized));
|
||||||
|
} catch (e) {
|
||||||
|
// Backwards compatibility
|
||||||
|
obj = JSON.parse(decodeURIComponent(serialized));
|
||||||
|
}
|
||||||
|
const {params, view} = obj;
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
sourcePath: validateString(params?.sourcePath),
|
sourcePath: validateString(params?.sourcePath),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
"ES2021",
|
"ES2023",
|
||||||
"WebWorker",
|
"WebWorker",
|
||||||
],
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue