@noya-app/noya-multiplayer-react 0.1.36 → 0.1.37

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.
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import {
4
+ AssetManager,
4
5
  ConnectionEvent,
5
6
  EphemeralUserData,
6
7
  ExtendedPathKey,
@@ -26,6 +27,7 @@ import {
26
27
  } from "react-inspector";
27
28
  import { shouldTrackEvents$, shouldTrackTasks$ } from "../globals";
28
29
  import { useEphemeralUserData, useManagedHistory } from "../hooks";
30
+ import { useObservable } from "../useObservable";
29
31
  import { useLocalStorageState } from "./useLocalStorageState";
30
32
 
31
33
  const lightTheme = {
@@ -258,6 +260,7 @@ export const StateInspector = memo(function StateInspector<S, M, E>({
258
260
  unstyled,
259
261
  colorScheme = "light",
260
262
  multiplayerStateManager,
263
+ assetManager,
261
264
  ephemeralUserData,
262
265
  anchor = "right",
263
266
  forceInit,
@@ -270,6 +273,7 @@ export const StateInspector = memo(function StateInspector<S, M, E>({
270
273
  userId?: string;
271
274
  unstyled?: boolean;
272
275
  multiplayerStateManager: MultiplayerStateManager<S, M>;
276
+ assetManager: AssetManager;
273
277
  ephemeralUserData: EphemeralUserData<E>;
274
278
  forceInit: () => void;
275
279
  } & StateInspectorOptions &
@@ -311,6 +315,10 @@ export const StateInspector = memo(function StateInspector<S, M, E>({
311
315
  "noya-multiplayer-react-show-ephemeral",
312
316
  false
313
317
  );
318
+ const [showAssets, setShowAssets] = useLocalStorageState(
319
+ "noya-multiplayer-react-show-assets",
320
+ false
321
+ );
314
322
 
315
323
  useEffect(() => {
316
324
  shouldTrackEvents$.set(showEvents);
@@ -329,6 +337,7 @@ export const StateInspector = memo(function StateInspector<S, M, E>({
329
337
 
330
338
  const ephemeral = useEphemeralUserData(ephemeralUserData);
331
339
  const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
340
+ const assets = useObservable(assetManager.assets$);
332
341
 
333
342
  useEffect(() => {
334
343
  if (historyContainerRef.current) {
@@ -680,6 +689,52 @@ export const StateInspector = memo(function StateInspector<S, M, E>({
680
689
  ))}
681
690
  </div>
682
691
  </DisclosureSection>
692
+ <DisclosureSection
693
+ open={showAssets}
694
+ setOpen={setShowAssets}
695
+ title="Assets"
696
+ colorScheme={colorScheme}
697
+ right={
698
+ <span style={{ display: "flex", gap: "4px" }}>
699
+ <SmallButton
700
+ theme={theme}
701
+ onClick={() => {
702
+ // get file input
703
+ const input = document.createElement("input");
704
+ input.type = "file";
705
+ input.onchange = () => {
706
+ const file = input.files?.[0];
707
+ if (file) {
708
+ const reader = new FileReader();
709
+ reader.onload = () => {
710
+ const buffer = reader.result as ArrayBuffer;
711
+ assetManager.create(new Uint8Array(buffer));
712
+ };
713
+ reader.readAsArrayBuffer(file);
714
+ }
715
+ };
716
+ input.click();
717
+ }}
718
+ >
719
+ Upload
720
+ </SmallButton>
721
+ </span>
722
+ }
723
+ >
724
+ <div style={styles.sectionInner}>
725
+ {assets.map((asset) => (
726
+ <InspectorRow key={asset.id} colorScheme={colorScheme}>
727
+ <ObjectInspector name={asset.id} data={asset} theme={theme} />
728
+ <SmallButton
729
+ theme={theme}
730
+ onClick={() => assetManager.delete(asset.id)}
731
+ >
732
+ Delete
733
+ </SmallButton>
734
+ </InspectorRow>
735
+ ))}
736
+ </div>
737
+ </DisclosureSection>
683
738
  <DisclosureSection
684
739
  title="Tasks"
685
740
  colorScheme={colorScheme}
@@ -939,7 +994,7 @@ function SmallButton({
939
994
  display: "inline-flex",
940
995
  alignItems: "center",
941
996
  justifyContent: "center",
942
- padding: "2px 0",
997
+ padding: "0",
943
998
  cursor: "pointer",
944
999
  ...style,
945
1000
  }}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import {
4
+ AssetManager,
4
5
  ConnectionEvent,
5
6
  EphemeralUserData,
6
7
  MultiplayerStateManager,
@@ -13,10 +14,13 @@ import { createRoot } from "react-dom/client";
13
14
  import { Root } from "react-dom/client";
14
15
  import { StateInspector } from "./StateInspector";
15
16
 
16
- function createPortalElement() {
17
- const el = document.createElement("div");
18
- el.id = "noya-multiplayer-react-inspector-portal";
19
- return el;
17
+ function createShadowRootElement() {
18
+ const host = document.createElement("div");
19
+ host.id = "noya-multiplayer-react-inspector-host";
20
+ const shadowRoot = host.attachShadow({ mode: "open" });
21
+ const container = document.createElement("div");
22
+ shadowRoot.appendChild(container);
23
+ return { host, container };
20
24
  }
21
25
 
22
26
  export function useStateInspector<S, M, E>({
@@ -29,6 +33,7 @@ export function useStateInspector<S, M, E>({
29
33
  colorScheme,
30
34
  anchor,
31
35
  multiplayerStateManager,
36
+ assetManager,
32
37
  ephemeralUserData,
33
38
  forceInit,
34
39
  }: {
@@ -39,6 +44,7 @@ export function useStateInspector<S, M, E>({
39
44
  userId?: string;
40
45
  disabled: boolean;
41
46
  multiplayerStateManager: MultiplayerStateManager<S, M>;
47
+ assetManager: AssetManager;
42
48
  ephemeralUserData: EphemeralUserData<E>;
43
49
  forceInit: () => void;
44
50
  } & Pick<ComponentProps<typeof StateInspector>, "colorScheme" | "anchor">) {
@@ -47,16 +53,15 @@ export function useStateInspector<S, M, E>({
47
53
  useLayoutEffect(() => {
48
54
  if (disabled) return;
49
55
 
50
- const portalElement = createPortalElement();
56
+ const { host, container } = createShadowRootElement();
57
+ const root = createRoot(container);
51
58
 
52
- const root = createRoot(portalElement);
53
-
54
- document.body.appendChild(portalElement);
59
+ document.body.appendChild(host);
55
60
 
56
61
  setRoot(root);
57
62
 
58
63
  return () => {
59
- document.body.removeChild(portalElement);
64
+ document.body.removeChild(host);
60
65
  };
61
66
  }, [disabled]);
62
67
 
@@ -76,6 +81,7 @@ export function useStateInspector<S, M, E>({
76
81
  multiplayerStateManager={
77
82
  multiplayerStateManager as MultiplayerStateManager<any, any>
78
83
  }
84
+ assetManager={assetManager}
79
85
  ephemeralUserData={ephemeralUserData as EphemeralUserData<any>}
80
86
  />
81
87
  );
@@ -91,5 +97,6 @@ export function useStateInspector<S, M, E>({
91
97
  tasks,
92
98
  forceInit,
93
99
  ephemeralUserData,
100
+ assetManager,
94
101
  ]);
95
102
  }
package/src/noyaApp.ts CHANGED
@@ -160,7 +160,7 @@ export function isEmbeddedApp() {
160
160
  return window.self !== window.top;
161
161
  }
162
162
 
163
- export type UseNoyaAppStateOptions<
163
+ export type UseNoyaStateOptions<
164
164
  S,
165
165
  M = void,
166
166
  E = void,
@@ -168,7 +168,7 @@ export type UseNoyaAppStateOptions<
168
168
  localStorageKey?: string;
169
169
  };
170
170
 
171
- export type UseNoyaAppStateResult<S, M = void, E = void> = [
171
+ export type UseNoyaStateResult<S, M = void, E = void> = [
172
172
  ReturnType<typeof useMultiplayerState<S, M, E>>[0],
173
173
  ReturnType<typeof useMultiplayerState<S, M, E>>[1],
174
174
  ReturnType<typeof useMultiplayerState<S, M, E>>[2] & {
@@ -182,15 +182,15 @@ export type UseNoyaAppStateResult<S, M = void, E = void> = [
182
182
  *
183
183
  * There are 3 ways to use this hook:
184
184
  *
185
- * 1. Pass an initial state: `useNoyaAppState(initialState)`
186
- * 2. Pass an initial state and a schema: `useNoyaAppState(initialState, { schema })`
187
- * 3. Pass only a schema, and a conforming initial state is created automatically: `useNoyaAppState({ schema })`
185
+ * 1. Pass an initial state: `useNoyaState(initialState)`
186
+ * 2. Pass an initial state and a schema: `useNoyaState(initialState, { schema })`
187
+ * 3. Pass only a schema, and a conforming initial state is created automatically: `useNoyaState({ schema })`
188
188
  */
189
- export function useNoyaAppState<
189
+ export function useNoyaState<
190
190
  S,
191
191
  M = void,
192
192
  E = void,
193
- O extends UseNoyaAppStateOptions<S, M, E> = UseNoyaAppStateOptions<S, M, E>,
193
+ O extends UseNoyaStateOptions<S, M, E> = UseNoyaStateOptions<S, M, E>,
194
194
  >(
195
195
  ...args:
196
196
  | [
@@ -200,7 +200,7 @@ export function useNoyaAppState<
200
200
  options?: O,
201
201
  ]
202
202
  | [options: O & { schema: TSchema }]
203
- ): UseNoyaAppStateResult<
203
+ ): UseNoyaStateResult<
204
204
  O["schema"] extends TSchema ? Static<O["schema"]> : S,
205
205
  M,
206
206
  E
@@ -250,7 +250,7 @@ export function useNoyaAppState<
250
250
  return { ...extras, theme, viewType } as const;
251
251
  }, [extras, theme, viewType]);
252
252
 
253
- return [result[0], result[1], mergedExtras] as UseNoyaAppStateResult<
253
+ return [result[0], result[1], mergedExtras] as UseNoyaStateResult<
254
254
  O["schema"] extends TSchema ? Static<O["schema"]> : S,
255
255
  M,
256
256
  E
@@ -258,11 +258,11 @@ export function useNoyaAppState<
258
258
  }
259
259
 
260
260
  // function typeTest() {
261
- // const [a, setA] = useNoyaAppState(7);
262
- // const [b, setB] = useNoyaAppState(7, {
261
+ // const [a, setA] = useNoyaState(7);
262
+ // const [b, setB] = useNoyaState(7, {
263
263
  // schema: Type.Number(),
264
264
  // });
265
- // const [c, setC] = useNoyaAppState({
265
+ // const [c, setC] = useNoyaState({
266
266
  // schema: Type.Number(),
267
267
  // });
268
268
  // }