Skip to content

Commit

Permalink
Simplify history state storage (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaperStrike authored Aug 18, 2021
1 parent 1de1fcd commit e55191e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 78 deletions.
18 changes: 8 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@ export interface SwitchesResult {
focusCleared: boolean;
}

export interface HistoryState {
[key: string]: unknown;
scrollPos?: [number, number];
interface State {
scrollPos: [number, number];
}

export interface History {
state: State | null;
pull(): void;
state: HistoryState;
}

export interface EventDetail {
Expand Down Expand Up @@ -110,19 +109,18 @@ export class Pjax {
if (event.state === null) return;

const overrideOptions: Partial<Options> = {};
if (this.options.scrollRestoration) {
const { scrollPos } = this.history.state;
if (scrollPos) {
overrideOptions.scrollTo = this.history.state.scrollPos;
}
if (this.options.scrollRestoration && this.history.state) {
overrideOptions.scrollTo = this.history.state.scrollPos;
}

this.load(window.location.href, overrideOptions).catch(() => {});
});
}

storeScrollPosition(): void {
this.history.state.scrollPos = [window.scrollX, window.scrollY];
this.history.state = {
scrollPos: [window.scrollX, window.scrollY],
};
}

/**
Expand Down
102 changes: 34 additions & 68 deletions src/libs/LazyHistory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,95 +15,61 @@
/**
* A valid history state object.
*/
export interface State {
export interface HistoryState {
[name: string]: unknown;
}

class LazyHistory {
class LazyHistory<State> {
/**
* Used to generate unique keys.
* The index of current state.
*/
private count = 0;
declare private index: number;

/**
* The prefix of the generated key.
* The key used in `window.history.state` and session storage.
*/
declare idPrefix: string;

/**
* The key used in `window.history.state`.
*/
declare historyKey: string;

/**
* The session key reflecting the current state.
*/
declare sessionKey: string;
declare key: string;

/**
* The current state.
*/
declare state: State;
declare state: State | null;

constructor(idPrefix: string, historyKey: string = idPrefix) {
this.idPrefix = idPrefix;
this.historyKey = historyKey;
constructor(key: string) {
this.key = key;

// Initialize current history entry.
window.history.replaceState(this.sign(), document.title);
}

/**
* Save current state to session storage.
*/
private save() {
window.sessionStorage.setItem(this.sessionKey, JSON.stringify(this.state));
}

/**
* Prepare a new session key and attach to current or given state.
*/
private sign(state: State | null = window.history.state as State | null): State {
if (this.count > 0) this.save();

// Generate a new key.
const sessionKey = `${this.idPrefix}_${this.count}`;
this.count += 1;

// Generate attached state.
const SignedState = {
...state || {},
[this.historyKey]: sessionKey,
};

this.sessionKey = sessionKey;
this.state = SignedState;

return SignedState;
}

/**
* Push to history entry.
*/
push(state: State, title: string, url: string): void {
window.history.pushState(this.sign(state), title, url);
this.pull();
}

/**
* Keep up with current browser history entry.
*/
pull(): void {
this.save();

const sessionKey = (window.history.state as State | null)
?.[this.historyKey] as string | undefined;
if (!sessionKey) {
// Initialize if haven't.
window.history.replaceState(this.sign(), document.title);
// Get new state index.
const historyState = window.history.state as HistoryState | null;
const pulledIndex = historyState?.[this.key] as number | undefined;

// Return if up-to-date.
if (pulledIndex !== undefined && this.index === pulledIndex) return;

// Get stored states.
const stateListStr = window.sessionStorage.getItem(this.key);
const stateList = stateListStr ? JSON.parse(stateListStr) as (State | null)[] : [];

// Store current state.
stateList[this.index] = this.state;
window.sessionStorage.setItem(this.key, JSON.stringify(stateList));

if (pulledIndex === undefined) {
this.index = stateList.length;
this.state = null;
window.history.replaceState({
...historyState,
[this.key]: this.index,
}, '');
} else {
this.sessionKey = sessionKey;
const savedState = window.sessionStorage.getItem(sessionKey);
this.state = savedState ? JSON.parse(savedState) as State : {};
this.index = pulledIndex;
this.state = stateListStr ? stateList[pulledIndex] : null;
}
}
}
Expand Down

0 comments on commit e55191e

Please sign in to comment.