@object-ui/collaboration 5.1.1 → 5.2.1
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/CHANGELOG.md +31 -0
- package/dist/PresenceProvider.d.ts +75 -0
- package/dist/PresenceProvider.d.ts.map +1 -0
- package/dist/PresenceProvider.js +71 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @object-ui/collaboration
|
|
2
2
|
|
|
3
|
+
## 5.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @object-ui/types@5.2.1
|
|
8
|
+
|
|
9
|
+
## 5.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- a4a0e1d: Add `<PresenceProvider>` abstraction with `useTenantPresence()` and
|
|
14
|
+
`useRecordPresence(objectName, recordId)` hooks. The default source is a
|
|
15
|
+
no-op so hooks return `[]` until a host app wires in a realtime
|
|
16
|
+
transport (WebSocket / SSE). Replaces the two architectural TODOs in
|
|
17
|
+
`AppHeader` (tenant scope) and `RecordDetailView` (record scope) that
|
|
18
|
+
were waiting on this abstraction.
|
|
19
|
+
|
|
20
|
+
`AppHeader` now falls back to `useTenantPresence()` when the
|
|
21
|
+
`presenceUsers` prop is omitted, and `RecordDetailView` renders
|
|
22
|
+
`<PresenceAvatars>` next to the lifecycle badge when other users are
|
|
23
|
+
viewing the same record. Both code paths render exactly as before when
|
|
24
|
+
no provider is mounted, so this change is non-visual for existing
|
|
25
|
+
consumers.
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- Updated dependencies [de0c5e6]
|
|
30
|
+
- Updated dependencies [9997cae]
|
|
31
|
+
- Updated dependencies [70b5570]
|
|
32
|
+
- @object-ui/types@5.2.0
|
|
33
|
+
|
|
3
34
|
## 5.1.1
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import type { PresenceUser } from './usePresence';
|
|
10
|
+
/**
|
|
11
|
+
* Scope key for record-level presence ("who else is viewing this record").
|
|
12
|
+
*/
|
|
13
|
+
export interface RecordPresenceScope {
|
|
14
|
+
objectName: string;
|
|
15
|
+
recordId: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Transport-agnostic source of presence updates. Implementations would
|
|
19
|
+
* typically be backed by a WebSocket / SSE channel; the platform ships a
|
|
20
|
+
* no-op default so consumers can read presence hooks unconditionally and
|
|
21
|
+
* simply render nothing until a real transport is wired in.
|
|
22
|
+
*
|
|
23
|
+
* Both `subscribeTenant` and `subscribeRecord` follow the same contract:
|
|
24
|
+
*
|
|
25
|
+
* 1. Push the *current* known user list to the callback synchronously
|
|
26
|
+
* (or as soon as it's available).
|
|
27
|
+
* 2. Push subsequent updates as users join / leave / change status.
|
|
28
|
+
* 3. Return an unsubscribe function that the hook will invoke on unmount
|
|
29
|
+
* or when the scope changes.
|
|
30
|
+
*/
|
|
31
|
+
export interface PresenceSource {
|
|
32
|
+
/** Tenant-wide "who else is online in this workspace". */
|
|
33
|
+
subscribeTenant?: (cb: (users: PresenceUser[]) => void) => () => void;
|
|
34
|
+
/** Record-scoped "who else is viewing this record". */
|
|
35
|
+
subscribeRecord?: (scope: RecordPresenceScope, cb: (users: PresenceUser[]) => void) => () => void;
|
|
36
|
+
}
|
|
37
|
+
export interface PresenceProviderProps {
|
|
38
|
+
/**
|
|
39
|
+
* Concrete presence source. When omitted, all presence hooks resolve to
|
|
40
|
+
* an empty user list — useful for development and for environments
|
|
41
|
+
* without a realtime transport yet.
|
|
42
|
+
*/
|
|
43
|
+
source?: PresenceSource;
|
|
44
|
+
children: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Provides a presence source to descendants. Wrap the application shell
|
|
48
|
+
* once; nested components consume the source via `useTenantPresence()`
|
|
49
|
+
* or `useRecordPresence()`.
|
|
50
|
+
*
|
|
51
|
+
* The default value is a no-op source so unwrapped trees still render
|
|
52
|
+
* correctly — the hooks simply return `[]`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function PresenceProvider({ source, children }: PresenceProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
/**
|
|
56
|
+
* Read tenant-wide presence ("who else is in this workspace right now").
|
|
57
|
+
* Returns an empty array when no `PresenceProvider` is mounted or the
|
|
58
|
+
* source doesn't implement `subscribeTenant`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function useTenantPresence(): PresenceUser[];
|
|
61
|
+
/**
|
|
62
|
+
* Read presence for a specific record ("who else is viewing this customer
|
|
63
|
+
* detail page"). Returns an empty array when no `PresenceProvider` is
|
|
64
|
+
* mounted, the source doesn't implement `subscribeRecord`, or the scope
|
|
65
|
+
* is incomplete.
|
|
66
|
+
*/
|
|
67
|
+
export declare function useRecordPresence(objectName: string | undefined, recordId: string | undefined): PresenceUser[];
|
|
68
|
+
/**
|
|
69
|
+
* Internal-only test helper that exposes the raw context value. Not part
|
|
70
|
+
* of the stable API; do not import from outside the package.
|
|
71
|
+
*
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
export declare function __unsafe_usePresenceContext(): PresenceSource;
|
|
75
|
+
//# sourceMappingURL=PresenceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceProvider.d.ts","sourceRoot":"","sources":["../src/PresenceProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACtE,uDAAuD;IACvD,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,mBAAmB,EAC1B,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,IAAI,KAChC,MAAM,IAAI,CAAC;CACjB;AAMD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAK3E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAelD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,YAAY,EAAE,CAkBhB;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,CAE5D"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ObjectUI
|
|
4
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
import * as React from 'react';
|
|
10
|
+
const NOOP_SOURCE = {};
|
|
11
|
+
const PresenceContext = React.createContext(NOOP_SOURCE);
|
|
12
|
+
/**
|
|
13
|
+
* Provides a presence source to descendants. Wrap the application shell
|
|
14
|
+
* once; nested components consume the source via `useTenantPresence()`
|
|
15
|
+
* or `useRecordPresence()`.
|
|
16
|
+
*
|
|
17
|
+
* The default value is a no-op source so unwrapped trees still render
|
|
18
|
+
* correctly — the hooks simply return `[]`.
|
|
19
|
+
*/
|
|
20
|
+
export function PresenceProvider({ source, children }) {
|
|
21
|
+
const value = source ?? NOOP_SOURCE;
|
|
22
|
+
return (_jsx(PresenceContext.Provider, { value: value, children: children }));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read tenant-wide presence ("who else is in this workspace right now").
|
|
26
|
+
* Returns an empty array when no `PresenceProvider` is mounted or the
|
|
27
|
+
* source doesn't implement `subscribeTenant`.
|
|
28
|
+
*/
|
|
29
|
+
export function useTenantPresence() {
|
|
30
|
+
const source = React.useContext(PresenceContext);
|
|
31
|
+
const [users, setUsers] = React.useState([]);
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
if (!source.subscribeTenant) {
|
|
34
|
+
setUsers([]);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
return source.subscribeTenant((next) => {
|
|
38
|
+
setUsers(Array.isArray(next) ? next : []);
|
|
39
|
+
});
|
|
40
|
+
}, [source]);
|
|
41
|
+
return users;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Read presence for a specific record ("who else is viewing this customer
|
|
45
|
+
* detail page"). Returns an empty array when no `PresenceProvider` is
|
|
46
|
+
* mounted, the source doesn't implement `subscribeRecord`, or the scope
|
|
47
|
+
* is incomplete.
|
|
48
|
+
*/
|
|
49
|
+
export function useRecordPresence(objectName, recordId) {
|
|
50
|
+
const source = React.useContext(PresenceContext);
|
|
51
|
+
const [users, setUsers] = React.useState([]);
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
if (!source.subscribeRecord || !objectName || !recordId) {
|
|
54
|
+
setUsers([]);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
return source.subscribeRecord({ objectName, recordId }, (next) => {
|
|
58
|
+
setUsers(Array.isArray(next) ? next : []);
|
|
59
|
+
});
|
|
60
|
+
}, [source, objectName, recordId]);
|
|
61
|
+
return users;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Internal-only test helper that exposes the raw context value. Not part
|
|
65
|
+
* of the stable API; do not import from outside the package.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export function __unsafe_usePresenceContext() {
|
|
70
|
+
return React.useContext(PresenceContext);
|
|
71
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,5 +26,6 @@ export { useMentionNotifications, type MentionNotificationsConfig, type MentionN
|
|
|
26
26
|
export { useCommentSearch, type CommentSearchConfig, type CommentSearchReturn, } from './useCommentSearch';
|
|
27
27
|
export { LiveCursors, type LiveCursorsProps, } from './LiveCursors';
|
|
28
28
|
export { PresenceAvatars, type PresenceAvatarsProps, } from './PresenceAvatars';
|
|
29
|
+
export { PresenceProvider, useTenantPresence, useRecordPresence, type PresenceSource, type PresenceProviderProps, type RecordPresenceScope, } from './PresenceProvider';
|
|
29
30
|
export type { CollaborationPresence, CollaborationOperation, CollaborationConfig, } from '@object-ui/types';
|
|
30
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,aAAa,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,3 +26,4 @@ export { useMentionNotifications, } from './useMentionNotifications';
|
|
|
26
26
|
export { useCommentSearch, } from './useCommentSearch';
|
|
27
27
|
export { LiveCursors, } from './LiveCursors';
|
|
28
28
|
export { PresenceAvatars, } from './PresenceAvatars';
|
|
29
|
+
export { PresenceProvider, useTenantPresence, useRecordPresence, } from './PresenceProvider';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/collaboration",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Real-time collaboration for Object UI with presence tracking, live cursors, conflict resolution, and comment threads.",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@object-ui/types": "5.
|
|
33
|
+
"@object-ui/types": "5.2.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "19.2.14",
|