@mentra/miniapp 3.2.1-beta.373 → 3.2.1-beta.386
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.
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/useHistoryState.d.ts +35 -0
- package/dist/react/useHistoryState.d.ts.map +1 -0
- package/dist/react/useHistoryState.js +117 -0
- package/dist/react/useHistoryState.js.map +1 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/package.json +2 -2
- package/src/react/index.ts +1 -0
- package/src/react/useHistoryState.ts +135 -0
- package/src/ui/index.ts +1 -0
package/dist/react/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type { MiniappHeaderProps } from "./MiniappHeader";
|
|
|
10
10
|
export { MentraProvider } from "./MentraProvider";
|
|
11
11
|
export type { MentraProviderProps } from "./MentraProvider";
|
|
12
12
|
export { useRpc, type RpcCallable } from "./useRpc";
|
|
13
|
+
export { useHistoryState, type SetHistoryState } from "./useHistoryState";
|
|
13
14
|
export { StreamPreview } from "./StreamPreview";
|
|
14
15
|
export type { StreamPreviewProps, StreamPreviewStatus, PreviewFit, PreviewConnectionState } from "./StreamPreview";
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AACzC,YAAY,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAC7D,YAAY,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAC,kBAAkB,EAAC,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAC,MAAM,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAA;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,UAAU,EAAE,sBAAsB,EAAC,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AACzC,YAAY,EAAC,iBAAiB,EAAC,MAAM,eAAe,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAC7D,YAAY,EAAC,4BAA4B,EAAC,MAAM,yBAAyB,CAAA;AACzE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAC,kBAAkB,EAAC,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,YAAY,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAC,MAAM,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAA;AACjD,OAAO,EAAC,eAAe,EAAE,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACvE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,UAAU,EAAE,sBAAsB,EAAC,MAAM,iBAAiB,CAAA"}
|
package/dist/react/index.js
CHANGED
|
@@ -6,5 +6,6 @@ export { useCapsuleHeaderStyle } from "./useCapsuleHeaderStyle";
|
|
|
6
6
|
export { MiniappHeader } from "./MiniappHeader";
|
|
7
7
|
export { MentraProvider } from "./MentraProvider";
|
|
8
8
|
export { useRpc } from "./useRpc";
|
|
9
|
+
export { useHistoryState } from "./useHistoryState";
|
|
9
10
|
export { StreamPreview } from "./StreamPreview";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAEzC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAE7D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,OAAO,EAAC,MAAM,EAAmB,MAAM,UAAU,CAAA;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAEzC,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAE7D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA;AAE7C,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAE/C,OAAO,EAAC,MAAM,EAAmB,MAAM,UAAU,CAAA;AACjD,OAAO,EAAC,eAAe,EAAuB,MAAM,mBAAmB,CAAA;AACvE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview useHistoryState — `useState` whose changes are browser history
|
|
3
|
+
* entries, so the phone's back gesture steps back through them.
|
|
4
|
+
*
|
|
5
|
+
* The host sends the back gesture (iOS edge swipe, Android back) to the
|
|
6
|
+
* miniapp's WebView history first, and minimizes the miniapp only once that
|
|
7
|
+
* history is empty. Screens, tabs and dialogs kept in plain React state never
|
|
8
|
+
* create history, so back skips past them and minimizes the whole miniapp.
|
|
9
|
+
* This hook keeps its value in `history.state` instead:
|
|
10
|
+
*
|
|
11
|
+
* - Setting a new value pushes a history entry. Back pops it, and the hook
|
|
12
|
+
* re-renders with the previous value.
|
|
13
|
+
* - Setting the value back to what it was before its latest change(s) goes
|
|
14
|
+
* back instead of pushing. Closing a dialog with its own button therefore
|
|
15
|
+
* consumes the dialog's entry, and the next back gesture does not reopen it.
|
|
16
|
+
*
|
|
17
|
+
* Routers in hash or browser mode (react-router's `HashRouter`, wouter's
|
|
18
|
+
* `useHashLocation`) already create history for pages; use this hook for UI
|
|
19
|
+
* state that is not a route. Every hook on the page shares one history entry
|
|
20
|
+
* per step, and `key` names the value within it, so components passing the
|
|
21
|
+
* same key share the same value. Values are stored as structured clones.
|
|
22
|
+
*/
|
|
23
|
+
export type SetHistoryState<T> = (value: T | ((previous: T) => T)) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Like `useState`, but each change is a history entry that the phone's back
|
|
26
|
+
* gesture undoes. `key` names the value in history state and must be unique
|
|
27
|
+
* per piece of state on the page.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const [tab, setTab] = useHistoryState<"home" | "settings">("tab", "home")
|
|
31
|
+
* const [pickerOpen, setPickerOpen] = useHistoryState("languagePicker", false)
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function useHistoryState<T>(key: string, initialValue: T): [T, SetHistoryState<T>];
|
|
35
|
+
//# sourceMappingURL=useHistoryState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHistoryState.d.ts","sourceRoot":"","sources":["../../src/react/useHistoryState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyFH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAA;AAE1E;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAYxF"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview useHistoryState — `useState` whose changes are browser history
|
|
3
|
+
* entries, so the phone's back gesture steps back through them.
|
|
4
|
+
*
|
|
5
|
+
* The host sends the back gesture (iOS edge swipe, Android back) to the
|
|
6
|
+
* miniapp's WebView history first, and minimizes the miniapp only once that
|
|
7
|
+
* history is empty. Screens, tabs and dialogs kept in plain React state never
|
|
8
|
+
* create history, so back skips past them and minimizes the whole miniapp.
|
|
9
|
+
* This hook keeps its value in `history.state` instead:
|
|
10
|
+
*
|
|
11
|
+
* - Setting a new value pushes a history entry. Back pops it, and the hook
|
|
12
|
+
* re-renders with the previous value.
|
|
13
|
+
* - Setting the value back to what it was before its latest change(s) goes
|
|
14
|
+
* back instead of pushing. Closing a dialog with its own button therefore
|
|
15
|
+
* consumes the dialog's entry, and the next back gesture does not reopen it.
|
|
16
|
+
*
|
|
17
|
+
* Routers in hash or browser mode (react-router's `HashRouter`, wouter's
|
|
18
|
+
* `useHashLocation`) already create history for pages; use this hook for UI
|
|
19
|
+
* state that is not a route. Every hook on the page shares one history entry
|
|
20
|
+
* per step, and `key` names the value within it, so components passing the
|
|
21
|
+
* same key share the same value. Values are stored as structured clones.
|
|
22
|
+
*/
|
|
23
|
+
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
24
|
+
const STATE_KEY = "__mentraHistoryState";
|
|
25
|
+
const EMPTY = { values: {}, trail: [] };
|
|
26
|
+
let cachedState = null;
|
|
27
|
+
let cachedEntry = EMPTY;
|
|
28
|
+
/** This hook's slice of the current entry, cached so snapshots stay referentially stable. */
|
|
29
|
+
function currentEntry() {
|
|
30
|
+
if (typeof window === "undefined")
|
|
31
|
+
return EMPTY;
|
|
32
|
+
const state = window.history.state;
|
|
33
|
+
if (state !== cachedState) {
|
|
34
|
+
cachedState = state;
|
|
35
|
+
cachedEntry = state?.[STATE_KEY] ?? EMPTY;
|
|
36
|
+
}
|
|
37
|
+
return cachedEntry;
|
|
38
|
+
}
|
|
39
|
+
const listeners = new Set();
|
|
40
|
+
let boundWindow = null;
|
|
41
|
+
// history.go() lands later, on popstate. A change applied before then would
|
|
42
|
+
// push on top of the entry being left, and the traversal would pop that push
|
|
43
|
+
// instead, so changes wait in order until the traversal lands.
|
|
44
|
+
let traversing = false;
|
|
45
|
+
const pending = [];
|
|
46
|
+
function notify() {
|
|
47
|
+
listeners.forEach((listener) => listener());
|
|
48
|
+
}
|
|
49
|
+
function flush() {
|
|
50
|
+
while (!traversing && pending.length > 0)
|
|
51
|
+
pending.shift()();
|
|
52
|
+
}
|
|
53
|
+
function onPopState() {
|
|
54
|
+
traversing = false;
|
|
55
|
+
notify();
|
|
56
|
+
// Let every popstate listener, including the host's history bridge, see the
|
|
57
|
+
// landed entry before a waiting change pushes the next one.
|
|
58
|
+
setTimeout(flush, 0);
|
|
59
|
+
}
|
|
60
|
+
function subscribe(listener) {
|
|
61
|
+
if (boundWindow !== window) {
|
|
62
|
+
boundWindow?.removeEventListener("popstate", onPopState);
|
|
63
|
+
window.addEventListener("popstate", onPopState);
|
|
64
|
+
boundWindow = window;
|
|
65
|
+
traversing = false;
|
|
66
|
+
pending.length = 0;
|
|
67
|
+
}
|
|
68
|
+
listeners.add(listener);
|
|
69
|
+
return () => listeners.delete(listener);
|
|
70
|
+
}
|
|
71
|
+
function whenSettled(change) {
|
|
72
|
+
if (traversing || pending.length > 0)
|
|
73
|
+
pending.push(change);
|
|
74
|
+
else
|
|
75
|
+
change();
|
|
76
|
+
}
|
|
77
|
+
function apply(key, initialValue, update) {
|
|
78
|
+
const { values, trail } = currentEntry();
|
|
79
|
+
const current = key in values ? values[key] : initialValue;
|
|
80
|
+
const next = typeof update === "function" ? update(current) : update;
|
|
81
|
+
if (Object.is(current, next))
|
|
82
|
+
return;
|
|
83
|
+
for (let index = trail.length - 1; index >= 0 && trail[index].key === key; index--) {
|
|
84
|
+
if (Object.is(trail[index].previous, next)) {
|
|
85
|
+
traversing = true;
|
|
86
|
+
window.history.go(index - trail.length);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Keep other fields (a router's index, the host's depth marker) on the new entry.
|
|
91
|
+
const state = window.history.state;
|
|
92
|
+
const base = state !== null && typeof state === "object" ? state : {};
|
|
93
|
+
const entry = { values: { ...values, [key]: next }, trail: [...trail, { key, previous: current }] };
|
|
94
|
+
window.history.pushState({ ...base, [STATE_KEY]: entry }, "");
|
|
95
|
+
notify();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Like `useState`, but each change is a history entry that the phone's back
|
|
99
|
+
* gesture undoes. `key` names the value in history state and must be unique
|
|
100
|
+
* per piece of state on the page.
|
|
101
|
+
*
|
|
102
|
+
* ```tsx
|
|
103
|
+
* const [tab, setTab] = useHistoryState<"home" | "settings">("tab", "home")
|
|
104
|
+
* const [pickerOpen, setPickerOpen] = useHistoryState("languagePicker", false)
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export function useHistoryState(key, initialValue) {
|
|
108
|
+
const initialRef = useRef(initialValue);
|
|
109
|
+
const read = () => {
|
|
110
|
+
const { values } = currentEntry();
|
|
111
|
+
return (key in values ? values[key] : initialRef.current);
|
|
112
|
+
};
|
|
113
|
+
const value = useSyncExternalStore(subscribe, read, () => initialRef.current);
|
|
114
|
+
const setValue = useCallback((update) => whenSettled(() => apply(key, initialRef.current, update)), [key]);
|
|
115
|
+
return [value, setValue];
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=useHistoryState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHistoryState.js","sourceRoot":"","sources":["../../src/react/useHistoryState.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -69,6 +69,7 @@ export { useConnected } from "../react/useConnected";
|
|
|
69
69
|
export { useSafeArea } from "../react/useSafeArea";
|
|
70
70
|
export { useCapsuleHeaderStyle } from "../react/useCapsuleHeaderStyle";
|
|
71
71
|
export { useRpc, type RpcCallable } from "../react/useRpc";
|
|
72
|
+
export { useHistoryState, type SetHistoryState } from "../react/useHistoryState";
|
|
72
73
|
export { StreamPreview } from "../react/StreamPreview";
|
|
73
74
|
export type { StreamPreviewProps, StreamPreviewStatus, PreviewFit, PreviewConnectionState } from "../react/StreamPreview";
|
|
74
75
|
/**
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAC3E,YAAY,EAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAA;AACnG,OAAO,EAAC,cAAc,EAAE,qBAAqB,EAAC,MAAM,eAAe,CAAA;AAEnE,MAAM,WAAW,cAAc,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChF;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACrC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,KAAK,GAAG,CAAC,EACrD,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,GACpB,IAAI,CAAA;IAEP;;;OAGG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACnC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,KAAK,GAAG,CAAC,EACrD,EAAE,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,GAClC,MAAM,IAAI,CAAA;IAEb;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACxC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,EACrD,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAC7B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAElC;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAEnC;;;;;OAKG;IACH,KAAK,IAAI,IAAI,CAAA;CACd;AAKD,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAC,qBAAqB,EAAC,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAC,MAAM,EAAE,KAAK,WAAW,EAAC,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,YAAY,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,UAAU,EAAE,sBAAsB,EAAC,MAAM,wBAAwB,CAAA;AAEvH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAAC,SAAS,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAA;AAC3E,YAAY,EAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,eAAe,CAAA;AACnG,OAAO,EAAC,cAAc,EAAE,qBAAqB,EAAC,MAAM,eAAe,CAAA;AAEnE,MAAM,WAAW,cAAc,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChF;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACrC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,KAAK,GAAG,CAAC,EACrD,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,GACpB,IAAI,CAAA;IAEP;;;OAGG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACnC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,KAAK,GAAG,CAAC,EACrD,EAAE,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,GAClC,MAAM,IAAI,CAAA;IAEb;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,EACxC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,EACrD,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAC7B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAElC;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAEnC;;;;;OAKG;IACH,KAAK,IAAI,IAAI,CAAA;CACd;AAKD,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAC,qBAAqB,EAAC,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAC,MAAM,EAAE,KAAK,WAAW,EAAC,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAC,eAAe,EAAE,KAAK,eAAe,EAAC,MAAM,0BAA0B,CAAA;AAC9E,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,YAAY,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,UAAU,EAAE,sBAAsB,EAAC,MAAM,wBAAwB,CAAA;AAEvH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,WAAW,CAAC,SAAS,SAAS,MAAM,IAAI,cAAc,CAAC,SAAS,CAAC,CAAA"}
|
package/dist/ui/index.js
CHANGED
|
@@ -29,5 +29,6 @@ export { useConnected } from "../react/useConnected";
|
|
|
29
29
|
export { useSafeArea } from "../react/useSafeArea";
|
|
30
30
|
export { useCapsuleHeaderStyle } from "../react/useCapsuleHeaderStyle";
|
|
31
31
|
export { useRpc } from "../react/useRpc";
|
|
32
|
+
export { useHistoryState } from "../react/useHistoryState";
|
|
32
33
|
export { StreamPreview } from "../react/StreamPreview";
|
|
33
34
|
//# sourceMappingURL=index.js.map
|
package/dist/ui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAC,cAAc,EAAE,qBAAqB,EAAC,MAAM,eAAe,CAAA;AA2DnE,uEAAuE;AACvE,sEAAsE;AACtE,+DAA+D;AAC/D,OAAO,EACL,cAAc,GAEf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAC,qBAAqB,EAAC,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAC,MAAM,EAAmB,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAC,cAAc,EAAE,qBAAqB,EAAC,MAAM,eAAe,CAAA;AA2DnE,uEAAuE;AACvE,sEAAsE;AACtE,+DAA+D;AAC/D,OAAO,EACL,cAAc,GAEf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAC,qBAAqB,EAAC,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAC,MAAM,EAAmB,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAC,eAAe,EAAuB,MAAM,0BAA0B,CAAA;AAC9E,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/miniapp",
|
|
3
|
-
"version": "3.2.1-beta.
|
|
3
|
+
"version": "3.2.1-beta.386",
|
|
4
4
|
"description": "SDK for building MentraOS local miniapps (browser/WebView)",
|
|
5
5
|
"author": "Mentra Labs",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@mentra/cloud-protocol": "3.2.1-beta.
|
|
60
|
+
"@mentra/cloud-protocol": "3.2.1-beta.386",
|
|
61
61
|
"eventemitter3": "^5.0.1"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
package/src/react/index.ts
CHANGED
|
@@ -10,5 +10,6 @@ export type {MiniappHeaderProps} from "./MiniappHeader"
|
|
|
10
10
|
export {MentraProvider} from "./MentraProvider"
|
|
11
11
|
export type {MentraProviderProps} from "./MentraProvider"
|
|
12
12
|
export {useRpc, type RpcCallable} from "./useRpc"
|
|
13
|
+
export {useHistoryState, type SetHistoryState} from "./useHistoryState"
|
|
13
14
|
export {StreamPreview} from "./StreamPreview"
|
|
14
15
|
export type {StreamPreviewProps, StreamPreviewStatus, PreviewFit, PreviewConnectionState} from "./StreamPreview"
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview useHistoryState — `useState` whose changes are browser history
|
|
3
|
+
* entries, so the phone's back gesture steps back through them.
|
|
4
|
+
*
|
|
5
|
+
* The host sends the back gesture (iOS edge swipe, Android back) to the
|
|
6
|
+
* miniapp's WebView history first, and minimizes the miniapp only once that
|
|
7
|
+
* history is empty. Screens, tabs and dialogs kept in plain React state never
|
|
8
|
+
* create history, so back skips past them and minimizes the whole miniapp.
|
|
9
|
+
* This hook keeps its value in `history.state` instead:
|
|
10
|
+
*
|
|
11
|
+
* - Setting a new value pushes a history entry. Back pops it, and the hook
|
|
12
|
+
* re-renders with the previous value.
|
|
13
|
+
* - Setting the value back to what it was before its latest change(s) goes
|
|
14
|
+
* back instead of pushing. Closing a dialog with its own button therefore
|
|
15
|
+
* consumes the dialog's entry, and the next back gesture does not reopen it.
|
|
16
|
+
*
|
|
17
|
+
* Routers in hash or browser mode (react-router's `HashRouter`, wouter's
|
|
18
|
+
* `useHashLocation`) already create history for pages; use this hook for UI
|
|
19
|
+
* state that is not a route. Every hook on the page shares one history entry
|
|
20
|
+
* per step, and `key` names the value within it, so components passing the
|
|
21
|
+
* same key share the same value. Values are stored as structured clones.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {useCallback, useRef, useSyncExternalStore} from "react"
|
|
25
|
+
|
|
26
|
+
const STATE_KEY = "__mentraHistoryState"
|
|
27
|
+
|
|
28
|
+
interface Entry {
|
|
29
|
+
values: Record<string, unknown>
|
|
30
|
+
/** One record per entry this hook pushed, oldest first, with the value it replaced. */
|
|
31
|
+
trail: Array<{key: string; previous: unknown}>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const EMPTY: Entry = {values: {}, trail: []}
|
|
35
|
+
|
|
36
|
+
let cachedState: unknown = null
|
|
37
|
+
let cachedEntry: Entry = EMPTY
|
|
38
|
+
|
|
39
|
+
/** This hook's slice of the current entry, cached so snapshots stay referentially stable. */
|
|
40
|
+
function currentEntry(): Entry {
|
|
41
|
+
if (typeof window === "undefined") return EMPTY
|
|
42
|
+
const state: unknown = window.history.state
|
|
43
|
+
if (state !== cachedState) {
|
|
44
|
+
cachedState = state
|
|
45
|
+
cachedEntry = (state as Record<string, Entry | undefined> | null)?.[STATE_KEY] ?? EMPTY
|
|
46
|
+
}
|
|
47
|
+
return cachedEntry
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const listeners = new Set<() => void>()
|
|
51
|
+
let boundWindow: Window | null = null
|
|
52
|
+
// history.go() lands later, on popstate. A change applied before then would
|
|
53
|
+
// push on top of the entry being left, and the traversal would pop that push
|
|
54
|
+
// instead, so changes wait in order until the traversal lands.
|
|
55
|
+
let traversing = false
|
|
56
|
+
const pending: Array<() => void> = []
|
|
57
|
+
|
|
58
|
+
function notify() {
|
|
59
|
+
listeners.forEach((listener) => listener())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function flush() {
|
|
63
|
+
while (!traversing && pending.length > 0) pending.shift()!()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function onPopState() {
|
|
67
|
+
traversing = false
|
|
68
|
+
notify()
|
|
69
|
+
// Let every popstate listener, including the host's history bridge, see the
|
|
70
|
+
// landed entry before a waiting change pushes the next one.
|
|
71
|
+
setTimeout(flush, 0)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function subscribe(listener: () => void): () => void {
|
|
75
|
+
if (boundWindow !== window) {
|
|
76
|
+
boundWindow?.removeEventListener("popstate", onPopState)
|
|
77
|
+
window.addEventListener("popstate", onPopState)
|
|
78
|
+
boundWindow = window
|
|
79
|
+
traversing = false
|
|
80
|
+
pending.length = 0
|
|
81
|
+
}
|
|
82
|
+
listeners.add(listener)
|
|
83
|
+
return () => listeners.delete(listener)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function whenSettled(change: () => void) {
|
|
87
|
+
if (traversing || pending.length > 0) pending.push(change)
|
|
88
|
+
else change()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function apply(key: string, initialValue: unknown, update: unknown) {
|
|
92
|
+
const {values, trail} = currentEntry()
|
|
93
|
+
const current = key in values ? values[key] : initialValue
|
|
94
|
+
const next = typeof update === "function" ? (update as (previous: unknown) => unknown)(current) : update
|
|
95
|
+
if (Object.is(current, next)) return
|
|
96
|
+
for (let index = trail.length - 1; index >= 0 && trail[index].key === key; index--) {
|
|
97
|
+
if (Object.is(trail[index].previous, next)) {
|
|
98
|
+
traversing = true
|
|
99
|
+
window.history.go(index - trail.length)
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Keep other fields (a router's index, the host's depth marker) on the new entry.
|
|
104
|
+
const state: unknown = window.history.state
|
|
105
|
+
const base = state !== null && typeof state === "object" ? state : {}
|
|
106
|
+
const entry: Entry = {values: {...values, [key]: next}, trail: [...trail, {key, previous: current}]}
|
|
107
|
+
window.history.pushState({...base, [STATE_KEY]: entry}, "")
|
|
108
|
+
notify()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type SetHistoryState<T> = (value: T | ((previous: T) => T)) => void
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Like `useState`, but each change is a history entry that the phone's back
|
|
115
|
+
* gesture undoes. `key` names the value in history state and must be unique
|
|
116
|
+
* per piece of state on the page.
|
|
117
|
+
*
|
|
118
|
+
* ```tsx
|
|
119
|
+
* const [tab, setTab] = useHistoryState<"home" | "settings">("tab", "home")
|
|
120
|
+
* const [pickerOpen, setPickerOpen] = useHistoryState("languagePicker", false)
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function useHistoryState<T>(key: string, initialValue: T): [T, SetHistoryState<T>] {
|
|
124
|
+
const initialRef = useRef(initialValue)
|
|
125
|
+
const read = () => {
|
|
126
|
+
const {values} = currentEntry()
|
|
127
|
+
return (key in values ? values[key] : initialRef.current) as T
|
|
128
|
+
}
|
|
129
|
+
const value = useSyncExternalStore(subscribe, read, () => initialRef.current)
|
|
130
|
+
const setValue = useCallback<SetHistoryState<T>>(
|
|
131
|
+
(update) => whenSettled(() => apply(key, initialRef.current, update)),
|
|
132
|
+
[key],
|
|
133
|
+
)
|
|
134
|
+
return [value, setValue]
|
|
135
|
+
}
|
package/src/ui/index.ts
CHANGED
|
@@ -93,6 +93,7 @@ export {useConnected} from "../react/useConnected"
|
|
|
93
93
|
export {useSafeArea} from "../react/useSafeArea"
|
|
94
94
|
export {useCapsuleHeaderStyle} from "../react/useCapsuleHeaderStyle"
|
|
95
95
|
export {useRpc, type RpcCallable} from "../react/useRpc"
|
|
96
|
+
export {useHistoryState, type SetHistoryState} from "../react/useHistoryState"
|
|
96
97
|
export {StreamPreview} from "../react/StreamPreview"
|
|
97
98
|
export type {StreamPreviewProps, StreamPreviewStatus, PreviewFit, PreviewConnectionState} from "../react/StreamPreview"
|
|
98
99
|
|