@pluv/react 0.1.3 → 0.3.0

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,5 +1,5 @@
1
1
 
2
- > @pluv/react@0.1.3 build /home/runner/work/pluv/pluv/packages/react
2
+ > @pluv/react@0.3.0 build /home/runner/work/pluv/pluv/packages/react
3
3
  > tsup src/index.ts --format esm,cjs --dts --external react
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,10 +8,10 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 7.70 KB
12
- ESM ⚡️ Build success in 86ms
13
- CJS dist/index.js 9.71 KB
14
- CJS ⚡️ Build success in 88ms
11
+ ESM dist/index.mjs 8.27 KB
12
+ ESM ⚡️ Build success in 134ms
13
+ CJS dist/index.js 10.35 KB
14
+ CJS ⚡️ Build success in 164ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 5192ms
17
- DTS dist/index.d.ts 3.92 KB
16
+ DTS ⚡️ Build success in 4002ms
17
+ DTS dist/index.d.ts 4.37 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @pluv/react
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 043c915: Added MockedDataProvider from createRoomBundle
8
+
9
+ ### Patch Changes
10
+
11
+ - 93f8c54: chore(deps): update dependency @types/react to v18.0.27
12
+ - Updated dependencies [595e66f]
13
+ - @pluv/client@0.3.0
14
+
15
+ ## 0.2.0
16
+
17
+ ### Minor Changes
18
+
19
+ - 95b5ef8: breaking: update PluvRoom.broadcast api
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [95b5ef8]
24
+ - @pluv/client@0.2.0
25
+ - @pluv/types@0.1.2
26
+
3
27
  ## 0.1.3
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -26,7 +26,9 @@
26
26
  <img src="https://img.shields.io/badge/docs-coming%20soon!-blue" alt="Docs coming soon!" />
27
27
  </p>
28
28
 
29
- <img src="https://github.com/pluv-io/pluv/blob/master/assets/demo-events.gif?raw=true" alt="Demo" />
29
+ <p align="center">
30
+ <img src="https://github.com/pluv-io/pluv/blob/master/assets/demo-events.gif?raw=true" alt="Demo" />
31
+ </p>
30
32
 
31
33
  ## `@pluv/react`
32
34
 
@@ -130,12 +132,9 @@ export const MyRoom: FC<Record<string, never>> = () => {
130
132
  return (
131
133
  <button
132
134
  onClick={() => {
133
- broadcast({
134
- type: "SEND_MESSAGE",
135
- data: {
136
- // TypeScript will require that message is typed as string
137
- message: "Hello world!",
138
- }
135
+ broadcast("SEND_MESSAGE", {
136
+ // TypeScript will require that message is typed as string
137
+ message: "Hello world!",
139
138
  });
140
139
  }}
141
140
  type="button"
package/dist/index.d.ts CHANGED
@@ -1,36 +1,39 @@
1
- import { PluvRoomDebug, WebSocketConnection, UserInfo, PluvRoom, InferYjsSharedTypeJson, PluvClient } from '@pluv/client';
1
+ import { PluvRoomDebug, WebSocketConnection, UserInfo, AbstractRoom, InferYjsSharedTypeJson, MockedRoomEvents, PluvClient } from '@pluv/client';
2
2
  export { AbstractType, ConnectionState, createClient, y } from '@pluv/client';
3
3
  import * as _pluv_types from '@pluv/types';
4
- import { JsonObject, InputZodLike, IOLike, Id, InferEventMessage, InferIOInput, InferIOOutput, IOEventMessage } from '@pluv/types';
4
+ import { JsonObject, InputZodLike, IOLike, InferIOInput, Id, InferIOOutput, IOEventMessage } from '@pluv/types';
5
5
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
6
- import { ReactNode, FC, Dispatch } from 'react';
6
+ import { FC, Dispatch, ReactNode } from 'react';
7
7
  import { AbstractType } from 'yjs';
8
8
 
9
9
  interface CreateRoomBundleOptions<TPresence extends JsonObject = {}, TStorage extends Record<string, AbstractType<any>> = {}> {
10
10
  initialStorage?: () => TStorage;
11
11
  presence?: InputZodLike<TPresence>;
12
12
  }
13
- type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> = {
13
+ type BaseRoomProviderProps<TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> = {
14
14
  children?: ReactNode;
15
- debug?: boolean | PluvRoomDebug<TIO>;
16
- onAuthorizationFail?: (error: Error) => void;
15
+ initialStorage?: keyof TStorage extends never ? never : () => TStorage;
17
16
  room: string;
18
17
  } & (keyof TPresence extends never ? {
19
18
  initialPresence?: never;
20
19
  } : {
21
20
  initialPresence: TPresence;
22
- }) & (keyof TStorage extends never ? {
23
- initialStorage?: never;
24
- } : {
25
- initialStorage?: () => TStorage;
26
21
  });
22
+ type MockedRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
23
+ events?: MockedRoomEvents<TIO>;
24
+ };
25
+ type PluvRoomProviderProps<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> = BaseRoomProviderProps<TPresence, TStorage> & {
26
+ debug?: boolean | PluvRoomDebug<TIO>;
27
+ onAuthorizationFail?: (error: Error) => void;
28
+ };
27
29
  interface SubscriptionHookOptions<T extends unknown> {
28
30
  isEqual?: (a: T, b: T) => boolean;
29
31
  }
30
32
  type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
31
33
  interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TStorage extends Record<string, AbstractType<any>>> {
34
+ MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
32
35
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
33
- usePluvBroadcast: <TMessage extends Id<InferEventMessage<InferIOInput<TIO>>>>() => (message: TMessage) => void;
36
+ usePluvBroadcast: () => <TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>) => void;
34
37
  usePluvConnection: <T extends unknown = WebSocketConnection>(selector: (connection: WebSocketConnection) => T, options?: SubscriptionHookOptions<Id<T>>) => Id<T>;
35
38
  usePluvEvent: <TType extends keyof InferIOOutput<TIO>>(type: TType, callback: (data: Id<IOEventMessage<TIO, TType>>) => void) => void;
36
39
  usePluvMyPresence: <T extends unknown = TPresence>(selector?: (myPresence: TPresence) => T, options?: SubscriptionHookOptions<Id<T> | null>) => [
@@ -40,7 +43,7 @@ interface CreateRoomBundle<TIO extends IOLike, TPresence extends JsonObject, TSt
40
43
  usePluvMyself: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (myself: Id<UserInfo<TIO, TPresence>>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
41
44
  usePluvOther: <T extends unknown = UserInfo<TIO, TPresence>>(connectionId: string, selector?: (other: UserInfo<TIO, TPresence>) => T, options?: SubscriptionHookOptions<Id<T> | null>) => Id<T> | null;
42
45
  usePluvOthers: <T extends unknown = UserInfo<TIO, TPresence>>(selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T[], options?: SubscriptionHookOptions<readonly Id<T>[]>) => readonly Id<T>[];
43
- usePluvRoom: () => PluvRoom<TIO, TPresence>;
46
+ usePluvRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
44
47
  usePluvStorage: <TKey extends keyof TStorage, TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>>(key: TKey, selector?: (data: InferYjsSharedTypeJson<TStorage[TKey]>) => TData, options?: SubscriptionHookOptions<TData | null>) => [data: TData | null, sharedType: TStorage[TKey] | null];
45
48
  }
46
49
 
package/dist/index.js CHANGED
@@ -26,19 +26,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
26
26
  // src/index.ts
27
27
  var src_exports = {};
28
28
  __export(src_exports, {
29
- ConnectionState: () => import_client.ConnectionState,
29
+ ConnectionState: () => import_client2.ConnectionState,
30
30
  createBundle: () => createBundle,
31
- createClient: () => import_client.createClient,
32
- y: () => import_client.y
31
+ createClient: () => import_client2.createClient,
32
+ y: () => import_client2.y
33
33
  });
34
34
  module.exports = __toCommonJS(src_exports);
35
- var import_client = require("@pluv/client");
35
+ var import_client2 = require("@pluv/client");
36
36
 
37
37
  // src/createBundle.tsx
38
38
  var import_react4 = require("react");
39
39
  var import_react5 = require("react");
40
40
 
41
41
  // src/createRoomBundle.tsx
42
+ var import_client = require("@pluv/client");
42
43
  var import_fast_deep_equal = __toESM(require("fast-deep-equal"));
43
44
  var import_react2 = require("react");
44
45
  var import_react3 = require("react");
@@ -66,9 +67,29 @@ var shallowArrayEqual = (a, b) => {
66
67
  // src/createRoomBundle.tsx
67
68
  var import_jsx_runtime = require("react/jsx-runtime");
68
69
  var createRoomBundle = (client, options = {}) => {
69
- const PluvRoomContext = (0, import_react2.createContext)(
70
- null
71
- );
70
+ const PluvRoomContext = (0, import_react2.createContext)(null);
71
+ const MockedRoomProvider = (0, import_react3.memo)((props) => {
72
+ const {
73
+ children,
74
+ events,
75
+ initialPresence,
76
+ initialStorage,
77
+ room: _room
78
+ } = props;
79
+ const [room] = (0, import_react2.useState)(() => {
80
+ return new import_client.MockedRoom(_room, {
81
+ events,
82
+ initialPresence,
83
+ initialStorage,
84
+ presence: options.presence
85
+ });
86
+ });
87
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PluvRoomContext.Provider, {
88
+ value: room,
89
+ children
90
+ });
91
+ });
92
+ MockedRoomProvider.displayName = "MockedRoomProvider";
72
93
  const PluvRoomProvider = (0, import_react3.memo)((props) => {
73
94
  const {
74
95
  children,
@@ -112,8 +133,8 @@ var createRoomBundle = (client, options = {}) => {
112
133
  const usePluvBroadcast = () => {
113
134
  const room = usePluvRoom();
114
135
  return (0, import_react3.useCallback)(
115
- (message) => {
116
- room.broadcast(message);
136
+ (event, data) => {
137
+ room.broadcast(event, data);
117
138
  },
118
139
  [room]
119
140
  );
@@ -174,10 +195,10 @@ var createRoomBundle = (client, options = {}) => {
174
195
  (value) => {
175
196
  const oldMyPresence = room.getMyPresence();
176
197
  const presence = typeof value === "function" ? value(oldMyPresence) : value;
177
- room.broadcast({
178
- type: "$UPDATE_PRESENCE",
179
- data: { presence }
180
- });
198
+ room.broadcast(
199
+ "$UPDATE_PRESENCE",
200
+ { presence }
201
+ );
181
202
  },
182
203
  [room]
183
204
  );
@@ -278,6 +299,7 @@ var createRoomBundle = (client, options = {}) => {
278
299
  return [data, sharedType];
279
300
  };
280
301
  return {
302
+ MockedRoomProvider,
281
303
  PluvRoomProvider,
282
304
  usePluvBroadcast,
283
305
  usePluvConnection,
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ import { createContext as createContext2 } from "react";
6
6
  import { memo as memo2, useContext as useContext2 } from "react";
7
7
 
8
8
  // src/createRoomBundle.tsx
9
+ import { MockedRoom } from "@pluv/client";
9
10
  import fastDeepEqual from "fast-deep-equal";
10
11
  import { createContext, useState as useState2 } from "react";
11
12
  import { memo, useCallback as useCallback2, useContext, useEffect } from "react";
@@ -33,9 +34,29 @@ var shallowArrayEqual = (a, b) => {
33
34
  // src/createRoomBundle.tsx
34
35
  import { jsx } from "react/jsx-runtime";
35
36
  var createRoomBundle = (client, options = {}) => {
36
- const PluvRoomContext = createContext(
37
- null
38
- );
37
+ const PluvRoomContext = createContext(null);
38
+ const MockedRoomProvider = memo((props) => {
39
+ const {
40
+ children,
41
+ events,
42
+ initialPresence,
43
+ initialStorage,
44
+ room: _room
45
+ } = props;
46
+ const [room] = useState2(() => {
47
+ return new MockedRoom(_room, {
48
+ events,
49
+ initialPresence,
50
+ initialStorage,
51
+ presence: options.presence
52
+ });
53
+ });
54
+ return /* @__PURE__ */ jsx(PluvRoomContext.Provider, {
55
+ value: room,
56
+ children
57
+ });
58
+ });
59
+ MockedRoomProvider.displayName = "MockedRoomProvider";
39
60
  const PluvRoomProvider = memo((props) => {
40
61
  const {
41
62
  children,
@@ -79,8 +100,8 @@ var createRoomBundle = (client, options = {}) => {
79
100
  const usePluvBroadcast = () => {
80
101
  const room = usePluvRoom();
81
102
  return useCallback2(
82
- (message) => {
83
- room.broadcast(message);
103
+ (event, data) => {
104
+ room.broadcast(event, data);
84
105
  },
85
106
  [room]
86
107
  );
@@ -141,10 +162,10 @@ var createRoomBundle = (client, options = {}) => {
141
162
  (value) => {
142
163
  const oldMyPresence = room.getMyPresence();
143
164
  const presence = typeof value === "function" ? value(oldMyPresence) : value;
144
- room.broadcast({
145
- type: "$UPDATE_PRESENCE",
146
- data: { presence }
147
- });
165
+ room.broadcast(
166
+ "$UPDATE_PRESENCE",
167
+ { presence }
168
+ );
148
169
  },
149
170
  [room]
150
171
  );
@@ -245,6 +266,7 @@ var createRoomBundle = (client, options = {}) => {
245
266
  return [data, sharedType];
246
267
  };
247
268
  return {
269
+ MockedRoomProvider,
248
270
  PluvRoomProvider,
249
271
  usePluvBroadcast,
250
272
  usePluvConnection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/react",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "@pluv/client React.js bindings",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -20,7 +20,7 @@
20
20
  "@types/use-sync-external-store": "^0.0.3",
21
21
  "fast-deep-equal": "^3.1.3",
22
22
  "use-sync-external-store": "^1.2.0",
23
- "@pluv/client": "^0.1.3",
23
+ "@pluv/client": "^0.3.0",
24
24
  "@pluv/types": "^0.1.2"
25
25
  },
26
26
  "peerDependencies": {
@@ -28,13 +28,13 @@
28
28
  "yjs": "^13.0.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@types/react": "18.0.26",
31
+ "@types/react": "18.0.27",
32
32
  "react": "18.2.0",
33
33
  "tsup": "^6.5.0",
34
34
  "typescript": "^4.9.4",
35
35
  "yjs": "^13.5.44",
36
36
  "@pluv/tsconfig": "^0.1.2",
37
- "eslint-config-pluv": "^0.1.2"
37
+ "eslint-config-pluv": "^0.1.3"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsup src/index.ts --format esm,cjs --dts --external react",
@@ -1,14 +1,16 @@
1
1
  import type {
2
+ AbstractRoom,
2
3
  InferYjsSharedTypeJson,
4
+ MockedRoomEvents,
3
5
  PluvClient,
6
+ PluvRoom,
4
7
  PluvRoomDebug,
5
8
  UserInfo,
6
9
  WebSocketConnection,
7
10
  } from "@pluv/client";
8
- import type { PluvRoom } from "@pluv/client";
11
+ import { MockedRoom } from "@pluv/client";
9
12
  import type {
10
13
  Id,
11
- InferEventMessage,
12
14
  InferIOInput,
13
15
  InferIOOutput,
14
16
  InputZodLike,
@@ -35,21 +37,33 @@ export interface CreateRoomBundleOptions<
35
37
  presence?: InputZodLike<TPresence>;
36
38
  }
37
39
 
38
- export type PluvRoomProviderProps<
39
- TIO extends IOLike,
40
+ type BaseRoomProviderProps<
40
41
  TPresence extends JsonObject,
41
42
  TStorage extends Record<string, AbstractType<any>>
42
43
  > = {
43
44
  children?: ReactNode;
44
- debug?: boolean | PluvRoomDebug<TIO>;
45
- onAuthorizationFail?: (error: Error) => void;
45
+ initialStorage?: keyof TStorage extends never ? never : () => TStorage;
46
46
  room: string;
47
47
  } & (keyof TPresence extends never
48
48
  ? { initialPresence?: never }
49
- : { initialPresence: TPresence }) &
50
- (keyof TStorage extends never
51
- ? { initialStorage?: never }
52
- : { initialStorage?: () => TStorage });
49
+ : { initialPresence: TPresence });
50
+
51
+ export type MockedRoomProviderProps<
52
+ TIO extends IOLike,
53
+ TPresence extends JsonObject,
54
+ TStorage extends Record<string, AbstractType<any>>
55
+ > = BaseRoomProviderProps<TPresence, TStorage> & {
56
+ events?: MockedRoomEvents<TIO>;
57
+ };
58
+
59
+ export type PluvRoomProviderProps<
60
+ TIO extends IOLike,
61
+ TPresence extends JsonObject,
62
+ TStorage extends Record<string, AbstractType<any>>
63
+ > = BaseRoomProviderProps<TPresence, TStorage> & {
64
+ debug?: boolean | PluvRoomDebug<TIO>;
65
+ onAuthorizationFail?: (error: Error) => void;
66
+ };
53
67
 
54
68
  export interface SubscriptionHookOptions<T extends unknown> {
55
69
  isEqual?: (a: T, b: T) => boolean;
@@ -65,12 +79,14 @@ export interface CreateRoomBundle<
65
79
  TStorage extends Record<string, AbstractType<any>>
66
80
  > {
67
81
  // components
82
+ MockedRoomProvider: FC<MockedRoomProviderProps<TIO, TPresence, TStorage>>;
68
83
  PluvRoomProvider: FC<PluvRoomProviderProps<TIO, TPresence, TStorage>>;
69
84
 
70
85
  // hooks
71
- usePluvBroadcast: <
72
- TMessage extends Id<InferEventMessage<InferIOInput<TIO>>>
73
- >() => (message: TMessage) => void;
86
+ usePluvBroadcast: () => <TEvent extends keyof InferIOInput<TIO>>(
87
+ event: TEvent,
88
+ data: Id<InferIOInput<TIO>[TEvent]>
89
+ ) => void;
74
90
  usePluvConnection: <T extends unknown = WebSocketConnection>(
75
91
  selector: (connection: WebSocketConnection) => T,
76
92
  options?: SubscriptionHookOptions<Id<T>>
@@ -99,7 +115,7 @@ export interface CreateRoomBundle<
99
115
  selector?: (other: readonly Id<UserInfo<TIO, TPresence>>[]) => T[],
100
116
  options?: SubscriptionHookOptions<readonly Id<T>[]>
101
117
  ) => readonly Id<T>[];
102
- usePluvRoom: () => PluvRoom<TIO, TPresence>;
118
+ usePluvRoom: () => AbstractRoom<TIO, TPresence, TStorage>;
103
119
  usePluvStorage: <
104
120
  TKey extends keyof TStorage,
105
121
  TData extends unknown = InferYjsSharedTypeJson<TStorage[TKey]>
@@ -125,9 +141,38 @@ export const createRoomBundle = <
125
141
  * @author leedavidcs
126
142
  * @date November 11, 2022
127
143
  */
128
- const PluvRoomContext = createContext<PluvRoom<TIO, TPresence, TStorage>>(
129
- null as any
130
- );
144
+ const PluvRoomContext = createContext<
145
+ AbstractRoom<TIO, TPresence, TStorage>
146
+ >(null as any);
147
+
148
+ const MockedRoomProvider = memo<
149
+ MockedRoomProviderProps<TIO, TPresence, TStorage>
150
+ >((props) => {
151
+ const {
152
+ children,
153
+ events,
154
+ initialPresence,
155
+ initialStorage,
156
+ room: _room,
157
+ } = props;
158
+
159
+ const [room] = useState<MockedRoom<TIO, TPresence, TStorage>>(() => {
160
+ return new MockedRoom<TIO, TPresence, TStorage>(_room, {
161
+ events,
162
+ initialPresence,
163
+ initialStorage,
164
+ presence: options.presence,
165
+ });
166
+ });
167
+
168
+ return (
169
+ <PluvRoomContext.Provider value={room}>
170
+ {children}
171
+ </PluvRoomContext.Provider>
172
+ );
173
+ });
174
+
175
+ MockedRoomProvider.displayName = "MockedRoomProvider";
131
176
 
132
177
  const PluvRoomProvider = memo<
133
178
  PluvRoomProviderProps<TIO, TPresence, TStorage>
@@ -185,14 +230,18 @@ export const createRoomBundle = <
185
230
 
186
231
  const usePluvRoom = () => useContext(PluvRoomContext);
187
232
 
188
- const usePluvBroadcast = <
189
- TMessage extends Id<InferEventMessage<InferIOInput<TIO>>>
190
- >(): ((message: TMessage) => void) => {
233
+ const usePluvBroadcast = (): (<TEvent extends keyof InferIOInput<TIO>>(
234
+ event: TEvent,
235
+ data: Id<InferIOInput<TIO>[TEvent]>
236
+ ) => void) => {
191
237
  const room = usePluvRoom();
192
238
 
193
239
  return useCallback(
194
- (message: TMessage) => {
195
- room.broadcast(message);
240
+ <TEvent extends keyof InferIOInput<TIO>>(
241
+ event: TEvent,
242
+ data: Id<InferIOInput<TIO>[TEvent]>
243
+ ) => {
244
+ room.broadcast(event, data);
196
245
  },
197
246
  [room]
198
247
  );
@@ -281,10 +330,10 @@ export const createRoomBundle = <
281
330
  ? value(oldMyPresence)
282
331
  : value;
283
332
 
284
- room.broadcast({
285
- type: "$UPDATE_PRESENCE",
286
- data: { presence },
287
- } as Id<InferEventMessage<InferIOInput<TIO>>>);
333
+ room.broadcast(
334
+ "$UPDATE_PRESENCE" as keyof InferIOInput<TIO>,
335
+ { presence } as any
336
+ );
288
337
  },
289
338
  [room]
290
339
  );
@@ -431,6 +480,7 @@ export const createRoomBundle = <
431
480
 
432
481
  return {
433
482
  // components
483
+ MockedRoomProvider,
434
484
  PluvRoomProvider,
435
485
 
436
486
  // hooks