@poolse/react 2.26.0 → 2.28.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.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, CallDeviceKind, CallDevice, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia, LiveCall } from '@poolse/sdk';
3
- export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, CallEndReason, CallLog, CallLogDisplay, CallLogEntry, CallLogMedia, CallMissedEntry, CallParticipantStatus, Conversation, ConversationCreateRequest, DeviceKind, LiveCall, LiveCallMember, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, RealtimeStatus, Uuid, callLogDisplayFor, describeCallLog, formatCallDuration, formatCallLogPreview, isCallLogMessage, isMessageVisibleTo, parseCallLog, parseCallLogBody } from '@poolse/sdk';
3
+ export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, CallEndReason, CallLog, CallLogDisplay, CallLogEntry, CallLogMedia, CallMissedEntry, CallParticipantStatus, Conversation, ConversationCreateRequest, DeviceKind, LiveCall, LiveCallMember, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, ReadReceiptsState, RealtimeStatus, RoomSession, Uuid, callLogDisplayFor, describeCallLog, formatCallDuration, formatCallLogPreview, isCallLogMessage, isMessageVisibleTo, parseCallLog, parseCallLogBody } from '@poolse/sdk';
4
4
  import { ReactNode } from 'react';
5
5
 
6
6
  interface PoolseProviderProps {
@@ -783,4 +783,88 @@ interface UseLiveCall {
783
783
  */
784
784
  declare function useLiveCall(conversationId: string | null): UseLiveCall;
785
785
 
786
- export { type CallPhase, type GroupPosition, PoolseProvider, type PoolseProviderProps, type ReactionMap, type UploadItem, type UploadItemStatus, type UseCallRoom, type UseCallRoomOptions, type UseCalls, type UseLiveCall, type UseUserState, type UseVoiceRoom, computeGroupPosition, formatDayLabel, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useRealtimeStatus, useThread, useTyping, useUser, useVoiceRoom };
786
+ interface UseRoomOptions {
787
+ /** `livekit-client`, imported by your app: `import * as livekit from 'livekit-client'`. */
788
+ livekit: LiveKitModule;
789
+ }
790
+ interface UseRoom {
791
+ /**
792
+ * The room (or call) running on the conversation right now, whether
793
+ * or not this user is in it — what a "Join" button reads.
794
+ */
795
+ live: ReturnType<typeof useLiveCall>['call'];
796
+ /** User ids in the room at this moment. */
797
+ presentIds: string[];
798
+ /** This user is in (or connecting to) the room. */
799
+ joined: boolean;
800
+ /** Starting or joining is in flight. */
801
+ joining: boolean;
802
+ /**
803
+ * Drop in: starts the conversation's room if none is live, or joins
804
+ * the one that is (a ringing call included). Nobody's phone rings.
805
+ * `video` turns the camera on as you arrive. `{ ring: true }` rings
806
+ * the other members' phones as well — an opt-in for apps where web
807
+ * calls people (a support desk); rooms never ring otherwise.
808
+ */
809
+ join: (media?: 'audio' | 'video', opts?: {
810
+ ring?: boolean;
811
+ }) => Promise<void>;
812
+ /** Leave. The room carries on for whoever is still in it. */
813
+ leave: () => void;
814
+ /** The media side: participants, mic/camera, devices (see `useCallRoom`). */
815
+ media: UseCallRoom;
816
+ error: Error | null;
817
+ }
818
+ /**
819
+ * A conversation's room — Meet-style: anyone in the conversation drops
820
+ * in for voice or video while the chat carries on, and nothing rings.
821
+ *
822
+ * const room = useRoom(conversationId, { livekit });
823
+ * room.live ? <button onClick={() => room.join()}>Join</button>
824
+ * : <button onClick={() => room.join('video')}>Start a video room</button>;
825
+ *
826
+ * The room ends on its own once it has been empty for a short while,
827
+ * and leaves one line in the conversation's history.
828
+ */
829
+ declare function useRoom(conversationId: string | null, opts: UseRoomOptions): UseRoom;
830
+
831
+ interface UseReadReceipts {
832
+ /**
833
+ * Whether this user sees other people's read state. False → show no
834
+ * check marks anywhere (the server withholds the data anyway).
835
+ */
836
+ visible: boolean;
837
+ /** `on`, `off`, or `user` (each user decides) — the workspace setting. */
838
+ mode: 'on' | 'off' | 'user';
839
+ /** Whether others see when this user reads. */
840
+ shared: boolean;
841
+ /** True only when the workspace lets users decide: offer a switch. */
842
+ canChange: boolean;
843
+ /**
844
+ * Turn this user's read receipts on or off (only where `canChange`).
845
+ * Reciprocal: off also hides other people's.
846
+ */
847
+ setShared: (share: boolean) => Promise<void>;
848
+ }
849
+ /** The workspace's read-receipt setting as it applies to the signed-in user. */
850
+ declare function useReadReceipts(): UseReadReceipts;
851
+ /** Read-receipt state from a `Me`; an older server without it means "on". */
852
+ declare function receiptsOf(me: Me | null): Omit<UseReadReceipts, 'setShared'>;
853
+ /** How long a message stays editable when read receipts are hidden. */
854
+ declare const EDIT_WINDOW_MS: number;
855
+ /**
856
+ * May the viewer edit their own message? With read receipts visible:
857
+ * until someone else has read it (`readState` is `read`), as in the
858
+ * messaging apps. With them hidden there is nothing to show why editing
859
+ * stopped, so it is a plain time window instead: 15 minutes from sending.
860
+ */
861
+ declare function canEditOwnMessage(args: {
862
+ isSelf: boolean;
863
+ deleted: boolean;
864
+ receiptsVisible: boolean;
865
+ readState: 'sent' | 'read' | undefined;
866
+ insertedAt: string;
867
+ now?: number;
868
+ }): boolean;
869
+
870
+ export { type CallPhase, EDIT_WINDOW_MS, type GroupPosition, PoolseProvider, type PoolseProviderProps, type ReactionMap, type UploadItem, type UploadItemStatus, type UseCallRoom, type UseCallRoomOptions, type UseCalls, type UseLiveCall, type UseReadReceipts, type UseRoom, type UseRoomOptions, type UseUserState, type UseVoiceRoom, canEditOwnMessage, computeGroupPosition, formatDayLabel, receiptsOf, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useReadReceipts, useRealtimeStatus, useRoom, useThread, useTyping, useUser, useVoiceRoom };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { PoolseConfig, Poolse, Me, Uuid, Conversation, ConversationCreateRequest, Membership, MemberRole, Message, MessageCreateRequest, RealtimeStatus, Attachment, AttachmentUploadInput, PoolseUserProfile, CallRoomStatus, CallParticipant, CameraFacing, CallDeviceKind, CallDevice, LiveKitModule, VoiceStatus, VoiceParticipant, VoiceRoomOptions, IncomingCall, OutgoingCall, CallMedia, LiveCall } from '@poolse/sdk';
3
- export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, CallEndReason, CallLog, CallLogDisplay, CallLogEntry, CallLogMedia, CallMissedEntry, CallParticipantStatus, Conversation, ConversationCreateRequest, DeviceKind, LiveCall, LiveCallMember, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, RealtimeStatus, Uuid, callLogDisplayFor, describeCallLog, formatCallDuration, formatCallLogPreview, isCallLogMessage, isMessageVisibleTo, parseCallLog, parseCallLogBody } from '@poolse/sdk';
3
+ export { ApiError, Attachment, AttachmentStatus, AttachmentUploadInput, AttachmentUploadRequest, AttachmentUploadResponse, AuthError, CallEndReason, CallLog, CallLogDisplay, CallLogEntry, CallLogMedia, CallMissedEntry, CallParticipantStatus, Conversation, ConversationCreateRequest, DeviceKind, LiveCall, LiveCallMember, Me, MemberRole, Membership, Message, MessageCreateRequest, MessageType, NetworkError, PoolseConfig, PoolseError, PoolseUserProfile, RateLimitedError, ReadReceiptsState, RealtimeStatus, RoomSession, Uuid, callLogDisplayFor, describeCallLog, formatCallDuration, formatCallLogPreview, isCallLogMessage, isMessageVisibleTo, parseCallLog, parseCallLogBody } from '@poolse/sdk';
4
4
  import { ReactNode } from 'react';
5
5
 
6
6
  interface PoolseProviderProps {
@@ -783,4 +783,88 @@ interface UseLiveCall {
783
783
  */
784
784
  declare function useLiveCall(conversationId: string | null): UseLiveCall;
785
785
 
786
- export { type CallPhase, type GroupPosition, PoolseProvider, type PoolseProviderProps, type ReactionMap, type UploadItem, type UploadItemStatus, type UseCallRoom, type UseCallRoomOptions, type UseCalls, type UseLiveCall, type UseUserState, type UseVoiceRoom, computeGroupPosition, formatDayLabel, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useRealtimeStatus, useThread, useTyping, useUser, useVoiceRoom };
786
+ interface UseRoomOptions {
787
+ /** `livekit-client`, imported by your app: `import * as livekit from 'livekit-client'`. */
788
+ livekit: LiveKitModule;
789
+ }
790
+ interface UseRoom {
791
+ /**
792
+ * The room (or call) running on the conversation right now, whether
793
+ * or not this user is in it — what a "Join" button reads.
794
+ */
795
+ live: ReturnType<typeof useLiveCall>['call'];
796
+ /** User ids in the room at this moment. */
797
+ presentIds: string[];
798
+ /** This user is in (or connecting to) the room. */
799
+ joined: boolean;
800
+ /** Starting or joining is in flight. */
801
+ joining: boolean;
802
+ /**
803
+ * Drop in: starts the conversation's room if none is live, or joins
804
+ * the one that is (a ringing call included). Nobody's phone rings.
805
+ * `video` turns the camera on as you arrive. `{ ring: true }` rings
806
+ * the other members' phones as well — an opt-in for apps where web
807
+ * calls people (a support desk); rooms never ring otherwise.
808
+ */
809
+ join: (media?: 'audio' | 'video', opts?: {
810
+ ring?: boolean;
811
+ }) => Promise<void>;
812
+ /** Leave. The room carries on for whoever is still in it. */
813
+ leave: () => void;
814
+ /** The media side: participants, mic/camera, devices (see `useCallRoom`). */
815
+ media: UseCallRoom;
816
+ error: Error | null;
817
+ }
818
+ /**
819
+ * A conversation's room — Meet-style: anyone in the conversation drops
820
+ * in for voice or video while the chat carries on, and nothing rings.
821
+ *
822
+ * const room = useRoom(conversationId, { livekit });
823
+ * room.live ? <button onClick={() => room.join()}>Join</button>
824
+ * : <button onClick={() => room.join('video')}>Start a video room</button>;
825
+ *
826
+ * The room ends on its own once it has been empty for a short while,
827
+ * and leaves one line in the conversation's history.
828
+ */
829
+ declare function useRoom(conversationId: string | null, opts: UseRoomOptions): UseRoom;
830
+
831
+ interface UseReadReceipts {
832
+ /**
833
+ * Whether this user sees other people's read state. False → show no
834
+ * check marks anywhere (the server withholds the data anyway).
835
+ */
836
+ visible: boolean;
837
+ /** `on`, `off`, or `user` (each user decides) — the workspace setting. */
838
+ mode: 'on' | 'off' | 'user';
839
+ /** Whether others see when this user reads. */
840
+ shared: boolean;
841
+ /** True only when the workspace lets users decide: offer a switch. */
842
+ canChange: boolean;
843
+ /**
844
+ * Turn this user's read receipts on or off (only where `canChange`).
845
+ * Reciprocal: off also hides other people's.
846
+ */
847
+ setShared: (share: boolean) => Promise<void>;
848
+ }
849
+ /** The workspace's read-receipt setting as it applies to the signed-in user. */
850
+ declare function useReadReceipts(): UseReadReceipts;
851
+ /** Read-receipt state from a `Me`; an older server without it means "on". */
852
+ declare function receiptsOf(me: Me | null): Omit<UseReadReceipts, 'setShared'>;
853
+ /** How long a message stays editable when read receipts are hidden. */
854
+ declare const EDIT_WINDOW_MS: number;
855
+ /**
856
+ * May the viewer edit their own message? With read receipts visible:
857
+ * until someone else has read it (`readState` is `read`), as in the
858
+ * messaging apps. With them hidden there is nothing to show why editing
859
+ * stopped, so it is a plain time window instead: 15 minutes from sending.
860
+ */
861
+ declare function canEditOwnMessage(args: {
862
+ isSelf: boolean;
863
+ deleted: boolean;
864
+ receiptsVisible: boolean;
865
+ readState: 'sent' | 'read' | undefined;
866
+ insertedAt: string;
867
+ now?: number;
868
+ }): boolean;
869
+
870
+ export { type CallPhase, EDIT_WINDOW_MS, type GroupPosition, PoolseProvider, type PoolseProviderProps, type ReactionMap, type UploadItem, type UploadItemStatus, type UseCallRoom, type UseCallRoomOptions, type UseCalls, type UseLiveCall, type UseReadReceipts, type UseRoom, type UseRoomOptions, type UseUserState, type UseVoiceRoom, canEditOwnMessage, computeGroupPosition, formatDayLabel, receiptsOf, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useReadReceipts, useRealtimeStatus, useRoom, useThread, useTyping, useUser, useVoiceRoom };
package/dist/index.js CHANGED
@@ -68,27 +68,30 @@ function usePoolse() {
68
68
  }
69
69
  function useMe() {
70
70
  const chat = usePoolse();
71
- const [state, setState] = useState({
72
- me: null,
73
- loading: true,
74
- error: null
71
+ const [state, setState] = useState(() => {
72
+ const known = chat.me.peek();
73
+ return { me: known, loading: known === null, error: null };
75
74
  });
76
75
  useEffect(() => {
77
76
  let cancelled = false;
78
77
  const controller = new AbortController();
78
+ const off = chat.me.onChange((me) => {
79
+ if (!cancelled) setState({ me, loading: false, error: null });
80
+ });
79
81
  chat.me.show(controller.signal).then((me) => {
80
82
  if (!cancelled) setState({ me, loading: false, error: null });
81
83
  }).catch((err) => {
82
84
  if (!cancelled) {
83
- setState({
84
- me: null,
85
+ setState((prev) => ({
86
+ me: prev.me,
85
87
  loading: false,
86
88
  error: err instanceof Error ? err : new Error(String(err))
87
- });
89
+ }));
88
90
  }
89
91
  });
90
92
  return () => {
91
93
  cancelled = true;
94
+ off();
92
95
  controller.abort();
93
96
  };
94
97
  }, [chat]);
@@ -1663,7 +1666,84 @@ function useLiveCall(conversationId) {
1663
1666
  }, [chat, conversationId, apply, refresh]);
1664
1667
  return { call, participantIds, refresh };
1665
1668
  }
1669
+ function useRoom(conversationId, opts) {
1670
+ const poolse = usePoolse();
1671
+ const { call: live, participantIds } = useLiveCall(conversationId);
1672
+ const media = useCallRoom(conversationId, { livekit: opts.livekit, aloneGraceMs: 0 });
1673
+ const session = useRef(null);
1674
+ const [joining, setJoining] = useState(false);
1675
+ const [error, setError] = useState(null);
1676
+ useEffect(() => {
1677
+ session.current = null;
1678
+ setError(null);
1679
+ }, [conversationId]);
1680
+ const join = useCallback(
1681
+ async (kind = "audio", joinOpts = {}) => {
1682
+ if (!conversationId) return;
1683
+ setJoining(true);
1684
+ setError(null);
1685
+ try {
1686
+ session.current = joinOpts.ring ? await poolse.rooms.ring(conversationId, kind) : await poolse.rooms.start(conversationId, kind);
1687
+ const connected = await media.join();
1688
+ if (!connected) throw new Error("Rooms need the SFU, which this deployment does not have.");
1689
+ if (kind === "video") await media.setCameraEnabled(true);
1690
+ } catch (err) {
1691
+ setError(err instanceof Error ? err : new Error(String(err)));
1692
+ media.leave();
1693
+ } finally {
1694
+ setJoining(false);
1695
+ }
1696
+ },
1697
+ [poolse, conversationId, media]
1698
+ );
1699
+ const leave = useCallback(() => {
1700
+ media.leave();
1701
+ const s = session.current;
1702
+ session.current = null;
1703
+ if (s) void poolse.rooms.leave(s).catch(() => {
1704
+ });
1705
+ }, [poolse, media]);
1706
+ const joined = media.status === "connected" || media.status === "connecting" || joining;
1707
+ return {
1708
+ live,
1709
+ presentIds: participantIds,
1710
+ joined,
1711
+ joining,
1712
+ join,
1713
+ leave,
1714
+ media,
1715
+ error: error ?? media.error
1716
+ };
1717
+ }
1718
+ function useReadReceipts() {
1719
+ const chat = usePoolse();
1720
+ const { me } = useMe();
1721
+ const state = receiptsOf(me);
1722
+ const setShared = useCallback(
1723
+ async (share) => {
1724
+ await chat.me.update({ read_receipts: share });
1725
+ },
1726
+ [chat]
1727
+ );
1728
+ return { ...state, setShared };
1729
+ }
1730
+ function receiptsOf(me) {
1731
+ const r = me?.read_receipts;
1732
+ return {
1733
+ visible: r?.visible ?? true,
1734
+ mode: r?.mode ?? "on",
1735
+ shared: r?.shared ?? true,
1736
+ canChange: r?.can_change ?? false
1737
+ };
1738
+ }
1739
+ var EDIT_WINDOW_MS = 15 * 60 * 1e3;
1740
+ function canEditOwnMessage(args) {
1741
+ if (!args.isSelf || args.deleted) return false;
1742
+ if (args.receiptsVisible) return (args.readState ?? "sent") === "sent";
1743
+ const sent = Date.parse(args.insertedAt);
1744
+ return Number.isFinite(sent) && (args.now ?? Date.now()) - sent < EDIT_WINDOW_MS;
1745
+ }
1666
1746
 
1667
- export { PoolseProvider, computeGroupPosition, formatDayLabel, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useRealtimeStatus, useThread, useTyping, useUser, useVoiceRoom };
1747
+ export { EDIT_WINDOW_MS, PoolseProvider, canEditOwnMessage, computeGroupPosition, formatDayLabel, receiptsOf, sameDay, sameGroup, useAttachmentUpload, useAttachmentUrl, useCallRoom, useCalls, useConversation, useConversations, useLiveCall, useMe, useMembers, useMessages, usePoolse, usePresence, useReactions, useReadReceipts, useRealtimeStatus, useRoom, useThread, useTyping, useUser, useVoiceRoom };
1668
1748
  //# sourceMappingURL=index.js.map
1669
1749
  //# sourceMappingURL=index.js.map