@fluid-topics/ft-app-context 1.2.58 → 1.2.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ import { WithEventBus } from "@fluid-topics/ft-wc-utils";
2
+ import { HistoryChangeEvent, HistoryState, HistoryStateUpdate } from "./HistoryService.models";
3
+ import { HistorySessionStorage } from "./HistorySessionStorage";
4
+ export declare class HistoryService extends WithEventBus {
5
+ private history;
6
+ private historyStorage;
7
+ private windowLocation;
8
+ static build(): HistoryService;
9
+ private states;
10
+ private readonly initialIndex;
11
+ private currentIndex;
12
+ private currentState;
13
+ private readonly realPushState;
14
+ private readonly realReplaceState;
15
+ constructor(history: History, historyStorage: HistorySessionStorage, windowLocation: () => Location | URL, initDataInstantly: boolean);
16
+ private setCurrentState;
17
+ private installProxies;
18
+ private initEventListeners;
19
+ private initData;
20
+ updateCurrentState(update?: HistoryStateUpdate, forceMajorStateFromUpdate?: boolean): void;
21
+ addHistoryChangeListener(listener: (event: HistoryChangeEvent) => void): void;
22
+ removeHistoryChangeListener(listener: (event: HistoryChangeEvent) => void): void;
23
+ currentItem(): HistoryState;
24
+ back(): void;
25
+ backwardItem(): HistoryState | undefined;
26
+ private previousMajorPosition;
27
+ forward(): void;
28
+ forwardItem(): HistoryState | undefined;
29
+ private nextMajorPosition;
30
+ private previousExistingPosition;
31
+ private buildCurrentState;
32
+ private hasState;
33
+ private isMajorState;
34
+ }
@@ -0,0 +1,163 @@
1
+ import { deepCopy, deepEqual, WithEventBus } from "@fluid-topics/ft-wc-utils";
2
+ import { HistoryChangeEvent } from "./HistoryService.models";
3
+ import { historyStorage } from "./HistorySessionStorage";
4
+ export class HistoryService extends WithEventBus {
5
+ static build() {
6
+ return new HistoryService(window.history, historyStorage, () => window.location, false);
7
+ }
8
+ constructor(history, historyStorage, windowLocation, initDataInstantly) {
9
+ var _a, _b;
10
+ super();
11
+ this.history = history;
12
+ this.historyStorage = historyStorage;
13
+ this.windowLocation = windowLocation;
14
+ this.states = [];
15
+ this.realPushState = history.pushState;
16
+ this.realReplaceState = history.replaceState;
17
+ this.initialIndex = (_b = (_a = history.state) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : history.length - 1;
18
+ this.currentIndex = this.initialIndex;
19
+ this.setCurrentState(this.buildCurrentState());
20
+ this.installProxies();
21
+ this.initEventListeners();
22
+ this.initData(initDataInstantly);
23
+ }
24
+ setCurrentState(state, clearForwardHistoryIfNecessary = false) {
25
+ const clearForwardHistory = clearForwardHistoryIfNecessary && this.currentIndex === state.index - 1;
26
+ this.currentState = { ...this.buildCurrentState(), ...state };
27
+ this.currentIndex = this.currentState.index;
28
+ this.states[this.currentIndex] = this.currentState;
29
+ if (clearForwardHistory) {
30
+ this.states = this.states.slice(0, this.currentIndex + 1);
31
+ }
32
+ this.historyStorage.set(this.currentIndex, this.currentState);
33
+ if (!deepEqual(this.currentState, this.history.state)) {
34
+ this.realReplaceState.apply(this.history, [this.currentState, this.currentState.title, window.location.href]);
35
+ }
36
+ setTimeout(() => this.dispatchEvent(new HistoryChangeEvent(this.currentItem())), 0);
37
+ }
38
+ installProxies() {
39
+ const editStateProxy = (indexProvider) => (method, history, [data, title, url]) => {
40
+ const index = indexProvider();
41
+ const state = {
42
+ ...(index === this.currentIndex ? this.currentState : undefined),
43
+ ...data,
44
+ index: index,
45
+ href: typeof url === "string" ? url : (url !== null && url !== void 0 ? url : this.windowLocation()).href,
46
+ };
47
+ method.apply(history, [state, title, url]);
48
+ this.setCurrentState(state, true);
49
+ };
50
+ this.history.pushState = new Proxy(this.history.pushState, { apply: editStateProxy(() => this.currentIndex + 1) });
51
+ this.history.replaceState = new Proxy(this.history.replaceState, { apply: editStateProxy(() => this.currentIndex) });
52
+ }
53
+ initEventListeners() {
54
+ window.addEventListener("popstate", (e) => this.setCurrentState(e.state));
55
+ if (document.querySelector("title") == null) {
56
+ document.head.append(document.createElement("title"));
57
+ }
58
+ new MutationObserver(() => this.updateCurrentState({ title: document.title }))
59
+ .observe(document.querySelector("title"), { subtree: true, characterData: true, childList: true });
60
+ }
61
+ initData(instant) {
62
+ for (let i = this.history.length - 1; i >= 0; i--) {
63
+ if (instant) {
64
+ this.states[i] = this.historyStorage.get(i);
65
+ }
66
+ else {
67
+ setTimeout(() => this.states[i] = this.historyStorage.get(i), this.history.length - i);
68
+ }
69
+ }
70
+ }
71
+ updateCurrentState(update, forceMajorStateFromUpdate = false) {
72
+ var _a, _b, _c;
73
+ const newState = {
74
+ ...this.buildCurrentState(),
75
+ ...update,
76
+ index: this.currentIndex,
77
+ title: (_a = update === null || update === void 0 ? void 0 : update.title) !== null && _a !== void 0 ? _a : this.currentState.title,
78
+ };
79
+ const majorState = forceMajorStateFromUpdate
80
+ ? ((_b = update === null || update === void 0 ? void 0 : update.majorState) !== null && _b !== void 0 ? _b : this.currentState.majorState)
81
+ : ((_c = this.currentState.majorState) !== null && _c !== void 0 ? _c : update === null || update === void 0 ? void 0 : update.majorState);
82
+ if (majorState != undefined) {
83
+ newState.majorState = majorState;
84
+ }
85
+ this.setCurrentState(newState);
86
+ }
87
+ addHistoryChangeListener(listener) {
88
+ this.addEventListener(HistoryChangeEvent.eventName, listener);
89
+ }
90
+ removeHistoryChangeListener(listener) {
91
+ this.removeEventListener(HistoryChangeEvent.eventName, listener);
92
+ }
93
+ currentItem() {
94
+ return deepCopy(this.currentState);
95
+ }
96
+ back() {
97
+ const previousMajorPosition = this.previousMajorPosition();
98
+ if (previousMajorPosition >= 0) {
99
+ this.history.go(previousMajorPosition - this.currentIndex);
100
+ }
101
+ else if (this.currentIndex !== this.initialIndex) {
102
+ this.history.go(this.initialIndex - this.currentIndex);
103
+ }
104
+ else {
105
+ this.history.back();
106
+ }
107
+ }
108
+ backwardItem() {
109
+ return deepCopy(this.states[this.previousMajorPosition()]);
110
+ }
111
+ previousMajorPosition() {
112
+ let position = this.currentIndex;
113
+ while (position > 0 && !this.isMajorState(position)) {
114
+ position--;
115
+ }
116
+ return this.previousExistingPosition(position);
117
+ }
118
+ forward() {
119
+ const nextMajorPosition = this.nextMajorPosition();
120
+ if (nextMajorPosition < this.states.length) {
121
+ this.history.go(nextMajorPosition - this.currentIndex);
122
+ }
123
+ else {
124
+ this.history.forward();
125
+ }
126
+ }
127
+ forwardItem() {
128
+ return deepCopy(this.states[this.nextMajorPosition()]);
129
+ }
130
+ nextMajorPosition() {
131
+ let position = this.currentIndex;
132
+ do {
133
+ position++;
134
+ } while (position < this.states.length && !this.isMajorState(position));
135
+ do {
136
+ position++;
137
+ } while (position < this.states.length && !this.isMajorState(position));
138
+ return this.previousExistingPosition(position);
139
+ }
140
+ previousExistingPosition(fromIndex) {
141
+ let position = fromIndex;
142
+ do {
143
+ position--;
144
+ } while (position > 0 && !this.hasState(position));
145
+ return position;
146
+ }
147
+ buildCurrentState() {
148
+ var _a, _b;
149
+ return {
150
+ ...this.history.state,
151
+ index: this.currentIndex,
152
+ href: this.windowLocation().href,
153
+ title: (_b = (_a = this.history.state) === null || _a === void 0 ? void 0 : _a.title) !== null && _b !== void 0 ? _b : document.title,
154
+ };
155
+ }
156
+ hasState(index) {
157
+ return this.states[index] != undefined;
158
+ }
159
+ isMajorState(index) {
160
+ var _a, _b;
161
+ return this.hasState(index) && ((_b = (_a = this.states[index]) === null || _a === void 0 ? void 0 : _a.majorState) !== null && _b !== void 0 ? _b : true);
162
+ }
163
+ }
@@ -0,0 +1,18 @@
1
+ export interface HistoryState {
2
+ index: number;
3
+ href: string;
4
+ token?: string;
5
+ title: string;
6
+ majorState?: boolean;
7
+ }
8
+ export interface HistoryStateUpdate {
9
+ token?: string;
10
+ title?: string;
11
+ majorState?: boolean;
12
+ }
13
+ export declare class HistoryChangeEvent extends CustomEvent<{
14
+ currentItem: HistoryState;
15
+ }> {
16
+ static eventName: string;
17
+ constructor(currentItem: HistoryState);
18
+ }
@@ -0,0 +1,7 @@
1
+ class HistoryChangeEvent extends CustomEvent {
2
+ constructor(currentItem) {
3
+ super(HistoryChangeEvent.eventName, { detail: { currentItem } });
4
+ }
5
+ }
6
+ HistoryChangeEvent.eventName = "change";
7
+ export { HistoryChangeEvent };
@@ -0,0 +1,7 @@
1
+ import { HistoryState } from "./HistoryService.models";
2
+ export declare class HistorySessionStorage {
3
+ private itemName;
4
+ get(index: number): HistoryState | undefined;
5
+ set(index: number, state: HistoryState): void;
6
+ }
7
+ export declare const historyStorage: HistorySessionStorage;
@@ -0,0 +1,13 @@
1
+ export class HistorySessionStorage {
2
+ itemName(index) {
3
+ return `fluid-topics-history-item-${index}`;
4
+ }
5
+ get(index) {
6
+ const item = sessionStorage.getItem(this.itemName(index));
7
+ return item ? JSON.parse(item) : undefined;
8
+ }
9
+ set(index, state) {
10
+ sessionStorage.setItem(this.itemName(index), JSON.stringify(state));
11
+ }
12
+ }
13
+ export const historyStorage = new HistorySessionStorage();
@@ -0,0 +1,12 @@
1
+ import { Constructor, FtLitElement } from "@fluid-topics/ft-wc-utils";
2
+ import { HistoryState } from "./HistoryService.models";
3
+ export interface WithHistoryInterface extends EventTarget {
4
+ backwardItem?: HistoryState;
5
+ forwardItem?: HistoryState;
6
+ }
7
+ export type WithHistoryComponentType<T extends Constructor<FtLitElement>> = T & Constructor<WithHistoryInterface>;
8
+ export declare function withHistory<T extends Constructor<FtLitElement>>(ReduxClass: T): WithHistoryComponentType<T>;
9
+ declare const WithHistory_base: WithHistoryComponentType<typeof FtLitElement>;
10
+ export declare class WithHistory extends WithHistory_base {
11
+ }
12
+ export {};
@@ -0,0 +1,37 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { FtLitElement } from "@fluid-topics/ft-wc-utils";
8
+ import { state } from "lit/decorators.js";
9
+ export function withHistory(ReduxClass) {
10
+ class WithHistoryClass extends ReduxClass {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.updateState = () => {
14
+ this.backwardItem = window.FluidTopicsHistoryService.backwardItem();
15
+ this.forwardItem = window.FluidTopicsHistoryService.forwardItem();
16
+ };
17
+ }
18
+ connectedCallback() {
19
+ super.connectedCallback();
20
+ window.FluidTopicsHistoryService.addHistoryChangeListener(this.updateState);
21
+ this.updateState();
22
+ }
23
+ disconnectedCallback() {
24
+ window.FluidTopicsHistoryService.addHistoryChangeListener(this.updateState);
25
+ super.disconnectedCallback();
26
+ }
27
+ }
28
+ __decorate([
29
+ state()
30
+ ], WithHistoryClass.prototype, "backwardItem", void 0);
31
+ __decorate([
32
+ state()
33
+ ], WithHistoryClass.prototype, "forwardItem", void 0);
34
+ return WithHistoryClass;
35
+ }
36
+ export class WithHistory extends withHistory(FtLitElement) {
37
+ }
@@ -0,0 +1,15 @@
1
+ import { HistoryService } from "./HistoryService";
2
+ declare global {
3
+ interface Window {
4
+ FluidTopicsInternalHistoryService: HistoryService;
5
+ FluidTopicsHistoryService: {
6
+ currentItem: HistoryService["currentItem"];
7
+ back: HistoryService["forward"];
8
+ forward: HistoryService["back"];
9
+ backwardItem: HistoryService["backwardItem"];
10
+ forwardItem: HistoryService["forwardItem"];
11
+ addHistoryChangeListener: HistoryService["addHistoryChangeListener"];
12
+ removeHistoryChangeListener: HistoryService["removeHistoryChangeListener"];
13
+ };
14
+ }
15
+ }
@@ -0,0 +1,13 @@
1
+ import { HistoryService } from "./HistoryService";
2
+ if (window.FluidTopicsInternalHistoryService == null) {
3
+ window.FluidTopicsInternalHistoryService = HistoryService.build();
4
+ window.FluidTopicsHistoryService = {
5
+ currentItem: () => window.FluidTopicsInternalHistoryService.currentItem(),
6
+ back: () => window.FluidTopicsInternalHistoryService.back(),
7
+ forward: () => window.FluidTopicsInternalHistoryService.forward(),
8
+ backwardItem: () => window.FluidTopicsInternalHistoryService.backwardItem(),
9
+ forwardItem: () => window.FluidTopicsInternalHistoryService.forwardItem(),
10
+ addHistoryChangeListener: (callback) => window.FluidTopicsInternalHistoryService.addHistoryChangeListener(callback),
11
+ removeHistoryChangeListener: (callback) => window.FluidTopicsInternalHistoryService.removeHistoryChangeListener(callback),
12
+ };
13
+ }
@@ -9,3 +9,4 @@ export * from "./DateService";
9
9
  export * from "./SearchPlaceConverterProvider";
10
10
  export * from "./FtAnalyticsEventsService";
11
11
  export * from "./webapp/UrlService";
12
+ export * from "./history";
@@ -9,3 +9,4 @@ export * from "./DateService";
9
9
  export * from "./SearchPlaceConverterProvider";
10
10
  export * from "./FtAnalyticsEventsService";
11
11
  export * from "./webapp/UrlService";
12
+ export * from "./history";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-topics/ft-app-context",
3
- "version": "1.2.58",
3
+ "version": "1.2.59",
4
4
  "description": "Global application context for Fluid Topics integrations",
5
5
  "keywords": [
6
6
  "Lit"
@@ -19,11 +19,11 @@
19
19
  "url": "ssh://git@scm.mrs.antidot.net:2222/fluidtopics/ft-web-components.git"
20
20
  },
21
21
  "dependencies": {
22
- "@fluid-topics/ft-wc-utils": "1.2.58",
22
+ "@fluid-topics/ft-wc-utils": "1.2.59",
23
23
  "lit": "3.1.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@fluid-topics/public-api": "1.0.94"
27
27
  },
28
- "gitHead": "7aa0ab41f37fa5e6f4f6477c4994b15663d67b4f"
28
+ "gitHead": "ca70918bd99da1a41e1d5ee71f9e9156ac58289f"
29
29
  }