@poolse/react-native 0.1.1 → 2.0.5

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,7 +1,80 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Uuid, Message, AttachmentUploadInput, Conversation, Membership, Attachment } from '@poolse/sdk';
3
1
  import * as react from 'react';
4
- import { ReactElement, ReactNode } from 'react';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { Conversation, Uuid, Message, AttachmentUploadInput, Membership, Attachment } from '@poolse/sdk';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+
6
+ interface InboxUser {
7
+ /** The user's external_id (the same id you mint JWTs with). */
8
+ externalId: string;
9
+ /** Display name shown in the picker row. */
10
+ name: string;
11
+ /** Optional avatar URL — falls back to initials if missing. */
12
+ avatarUrl?: string | null;
13
+ /** Optional secondary line (email, role, handle…). */
14
+ subtitle?: string;
15
+ }
16
+
17
+ interface PoolseInboxProps {
18
+ /** Header title above the conversation list. Defaults to "Chats". */
19
+ title?: string;
20
+ /**
21
+ * The tenant's user directory. When provided, the inbox shows
22
+ * "New chat" + "New group" buttons in the header. Omit if you
23
+ * don't want the built-in picker UI (e.g. you start chats from
24
+ * elsewhere via the imperative `openDirect()` API).
25
+ */
26
+ users?: InboxUser[];
27
+ /** Display-name resolver for sender labels / chat header titles. */
28
+ labelFor?: (externalId: string) => string;
29
+ /** Avatar URL resolver. */
30
+ avatarFor?: (externalId: string) => string | null;
31
+ /**
32
+ * Right-side slot in the header — typical use is a logout / settings
33
+ * button. Rendered after the New-chat / New-group buttons.
34
+ */
35
+ renderRightAction?: () => ReactNode;
36
+ /**
37
+ * Custom row renderer for the conversation list. Receives the
38
+ * conversation + the caller's external_id (resolved via `useMe`)
39
+ * so you can branch on direct-vs-group display.
40
+ */
41
+ renderConversationRow?: (conv: Conversation, callerExternalId: string | null, selected: boolean) => ReactNode;
42
+ /** Custom empty state when the list is empty. */
43
+ emptyState?: ReactNode | string;
44
+ /** Forwarded to `<ConversationView>` — set if you have a nav header above. */
45
+ keyboardOffset?: number;
46
+ }
47
+ interface PoolseInboxHandle {
48
+ /** Open a known conversation (e.g. from a deep link / push tap). */
49
+ open: (conversationId: Uuid) => void;
50
+ /**
51
+ * Open (or create) a direct conversation with the given external_id.
52
+ * The backend dedupes per pair — returns the existing conversation
53
+ * if one already exists. Use this from a "Message" button on your
54
+ * user profile screens.
55
+ */
56
+ openDirect: (externalId: string) => Promise<void>;
57
+ /** Open (or create) a group conversation. */
58
+ openGroup: (name: string, memberExternalIds: string[]) => Promise<void>;
59
+ /** Open the built-in "new chat" picker programmatically. */
60
+ promptNewChat: () => void;
61
+ /** Open the built-in "new group" picker programmatically. */
62
+ promptNewGroup: () => void;
63
+ /** Pop back to the conversation list. */
64
+ close: () => void;
65
+ }
66
+ /**
67
+ * Drop-in inbox screen: scrollable list of conversations + chat
68
+ * detail, with an iOS-style slide animation between them. Manages
69
+ * its own navigation stack (no react-navigation needed). Picker
70
+ * sheets for "New chat" / "New group" appear when `users` is
71
+ * provided; otherwise the dev wires those flows themselves and
72
+ * uses the imperative ref to open conversations.
73
+ *
74
+ * For the "Message" button on a user profile screen, hold a ref to
75
+ * the inbox and call `inboxRef.current?.openDirect(externalId)`.
76
+ */
77
+ declare const PoolseInbox: react.ForwardRefExoticComponent<PoolseInboxProps & react.RefAttributes<PoolseInboxHandle>>;
5
78
 
6
79
  interface ConversationViewProps {
7
80
  conversationId: Uuid;
@@ -9,6 +82,17 @@ interface ConversationViewProps {
9
82
  renderMessage?: (msg: Message, currentUserId: string | null) => ReactElement;
10
83
  labelFor?: (externalId: string) => string;
11
84
  avatarFor?: (externalId: string) => string | null;
85
+ /**
86
+ * Renders the default `<ChatHeader>` at the top of the chat surface.
87
+ * * `true` (default) — auto chat header (other user / group name).
88
+ * * `false` — no header (you supply your own above).
89
+ * * `ReactElement` — custom header node, rendered as-is.
90
+ */
91
+ header?: boolean | ReactElement;
92
+ /** Tap on the header's members icon — typically opens a sheet. */
93
+ onHeaderMembersPress?: () => void;
94
+ /** Tap on the header itself — typically opens a conversation profile. */
95
+ onHeaderPress?: () => void;
12
96
  reactions?: boolean;
13
97
  mentions?: boolean;
14
98
  attachments?: boolean;
@@ -21,10 +105,101 @@ interface ConversationViewProps {
21
105
  senderLabels?: 'auto' | 'always' | 'never';
22
106
  /** Show avatars to the left of other-side bubbles in group chats. Same semantics. */
23
107
  avatars?: 'auto' | 'always' | 'never';
24
- /** Fires when auto-mark-read commits — clear sidebar badges via this. */
25
- onMarkedRead?: (conversationId: string) => void;
108
+ /**
109
+ * Fires when auto-mark-read commits a new server-side read cursor.
110
+ * Receives the conversation id + the message id that was marked
111
+ * as the last-read. Wire this to `useConversations().markConversationRead(convId, msgId)`
112
+ * so the sidebar / list view's unread badge clears immediately
113
+ * instead of waiting for the next conversations refetch.
114
+ */
115
+ onMarkedRead?: (conversationId: string, messageId: string) => void;
116
+ /**
117
+ * Fires after every successful message send. Wire to the parent's
118
+ * `useConversations().refetch()` so the conversation list shows
119
+ * the fresh `last_message_preview` / `last_message_at` for the
120
+ * conversation you just sent into. `useConversations` creates
121
+ * isolated state per call site, so ConversationView can't refresh
122
+ * a parent hook's state on its own.
123
+ */
124
+ onSent?: (conversationId: string) => void;
125
+ /**
126
+ * keyboardVerticalOffset passed to the internal KeyboardAvoidingView.
127
+ * On iOS this MUST equal the distance from the screen top to the
128
+ * KAV's top (status bar + safe-area-top + any nav header above
129
+ * the chat). `<PoolseInbox>` computes this for you. Standalone
130
+ * consumers pass it manually.
131
+ */
132
+ keyboardOffset?: number;
133
+ }
134
+ declare function ConversationView({ conversationId, emptyState, renderMessage, labelFor, avatarFor, header, onHeaderMembersPress, onHeaderPress, reactions, mentions, attachments, actions, threads, quotations, readReceipts, maxBodyLength, senderLabels, avatars, onMarkedRead, onSent, keyboardOffset, }: ConversationViewProps): react_jsx_runtime.JSX.Element;
135
+
136
+ interface ChatHeaderProps {
137
+ conversationId: Uuid;
138
+ /**
139
+ * Override the title resolver — useful when you want to derive the
140
+ * direct-chat name from your own user directory instead of going
141
+ * through the SDK's `userResolver`. When omitted, falls back to:
142
+ * * direct → the other member's display name (via `useUser`)
143
+ * * group → `conversation.name`
144
+ */
145
+ labelFor?: (externalId: string) => string;
146
+ /** Optional avatar URL resolver for the other party (direct only). */
147
+ avatarFor?: (externalId: string) => string | null;
148
+ /** Right-side icon button — typically opens a member-list sheet. */
149
+ onMembersPress?: () => void;
150
+ /**
151
+ * Tap handler for the whole header. Behavior:
152
+ * * Direct chat → calls this if provided, otherwise no-op.
153
+ * * Group chat → calls this if provided. If omitted, the header
154
+ * opens a built-in group-details sheet showing the member
155
+ * roster with live presence.
156
+ */
157
+ onPress?: () => void;
158
+ /**
159
+ * When provided, renders a back arrow on the LEFT of the header
160
+ * that calls this handler on tap. Used by `<PoolseInbox>` to
161
+ * navigate back to the conversation list; safe to wire from your
162
+ * own navigator too.
163
+ */
164
+ onBack?: () => void;
165
+ }
166
+ /**
167
+ * Top-of-chat header bar.
168
+ * * 1:1 direct → other user's display name + avatar + presence dot.
169
+ * * Group → conversation name + "N members" + group-tile avatar.
170
+ *
171
+ * Sits above `<MessageList>` inside `<ConversationView>` by default;
172
+ * pass `header={false}` to `<ConversationView>` if you mount your own.
173
+ */
174
+ declare function ChatHeader({ conversationId, labelFor, avatarFor, onMembersPress, onPress, onBack, }: ChatHeaderProps): react_jsx_runtime.JSX.Element;
175
+
176
+ interface GroupDetailsSheetProps {
177
+ visible: boolean;
178
+ onClose: () => void;
179
+ conversationId: Uuid;
180
+ /** Resolve external_id → display name. Same shape as MemberList. */
181
+ labelFor?: (externalId: string) => string;
182
+ /** Resolve external_id → avatar URL. Same shape as MemberList. */
183
+ avatarFor?: (externalId: string) => string | null;
184
+ /**
185
+ * Predicate for the per-row remove button. Defaults to "never show"
186
+ * — wire it on the parent for owners/admins. Receives the
187
+ * Membership row so you can scope to roles or exclude self.
188
+ */
189
+ canRemoveMember?: (m: {
190
+ user_id: string;
191
+ external_id: string;
192
+ role: string;
193
+ }) => boolean;
26
194
  }
27
- declare function ConversationView({ conversationId, emptyState, renderMessage, labelFor, avatarFor, reactions, mentions, attachments, actions, threads, quotations, readReceipts, maxBodyLength, senderLabels, avatars, onMarkedRead, }: ConversationViewProps): react_jsx_runtime.JSX.Element;
195
+ /**
196
+ * Bottom-sheet (85% of screen) that opens when the user taps the
197
+ * group chat header. Shows group name + member count at the top,
198
+ * then a scrollable roster with avatars, display names (resolved
199
+ * through `labelFor` / `userResolver`), role badges, and live
200
+ * presence dots (driven by `MemberList` internally).
201
+ */
202
+ declare function GroupDetailsSheet({ visible, onClose, conversationId, labelFor, avatarFor, canRemoveMember, }: GroupDetailsSheetProps): react_jsx_runtime.JSX.Element;
28
203
 
29
204
  type BubbleGroupPosition = 'first' | 'middle' | 'last' | 'standalone';
30
205
  interface MessageBubbleProps {
@@ -114,6 +289,15 @@ interface MessageComposerProps {
114
289
  attachments?: boolean;
115
290
  /** Tap on the paperclip — wire to `<AttachmentPicker>` from the same package. */
116
291
  onAttachPress?: () => void;
292
+ /**
293
+ * Distance from the top of the screen to the top of the
294
+ * KeyboardAvoidingView — typically the height of any navigation
295
+ * header sitting above the chat. Pass this when the iOS keyboard
296
+ * suggestions strip ends up covering the composer (a sign the
297
+ * computed offset is wrong). Defaults to 0; on Android it's
298
+ * ignored.
299
+ */
300
+ keyboardOffset?: number;
117
301
  }
118
302
  /**
119
303
  * Imperative handle for parents that want to add files to the
@@ -132,19 +316,27 @@ interface MessageActionsProps {
132
316
  visible: boolean;
133
317
  onClose: () => void;
134
318
  isSelf: boolean;
319
+ /**
320
+ * Quick-reaction emoji row rendered at the top of the sheet. Tap
321
+ * one to fire `onPickReaction(emoji)` and immediately close. When
322
+ * omitted (or `onPickReaction` is omitted) the row is hidden.
323
+ */
324
+ emojis?: string[];
325
+ onPickReaction?: (emoji: string) => void;
135
326
  onReply?: () => void;
136
327
  onReplyInThread?: () => void;
137
328
  onQuote?: () => void;
138
329
  onEdit?: () => void;
139
330
  onDelete?: () => void;
140
331
  onCopy?: () => void;
141
- onReact?: () => void;
142
332
  }
143
333
  /**
144
- * Long-press action menu — rendered as a modal bottom sheet (RN's
145
- * native equivalent of the web hover popover).
334
+ * Long-press action menu — modal bottom sheet. Renders a row of
335
+ * quick-react emojis at the top (one tap reacts + closes) plus the
336
+ * action items below. Matches WhatsApp / iMessage's "long-press
337
+ * shows everything inline" pattern.
146
338
  */
147
- declare function MessageActions({ visible, onClose, isSelf, onReply, onReplyInThread, onQuote, onEdit, onDelete, onCopy, onReact, }: MessageActionsProps): react_jsx_runtime.JSX.Element;
339
+ declare function MessageActions({ visible, onClose, isSelf, emojis, onPickReaction, onReply, onReplyInThread, onQuote, onEdit, onDelete, onCopy, }: MessageActionsProps): react_jsx_runtime.JSX.Element;
148
340
 
149
341
  interface MessageListProps {
150
342
  messages: Message[];
@@ -191,13 +383,21 @@ interface ThreadViewProps {
191
383
  conversationId: Uuid;
192
384
  rootMessage: Message;
193
385
  labelFor?: (externalId: string) => string;
386
+ avatarFor?: (externalId: string) => string | null;
194
387
  }
195
388
  /**
196
- * Modal screen for thread replies. Mirrors the web `<ThreadView>` —
197
- * root message preview at the top, oldest-first reply list, composer
198
- * at the bottom.
389
+ * Bottom-sheet modal for thread replies. Pull the drag handle / header
390
+ * down to dismiss; tap the backdrop or close button too. The composer
391
+ * inside the sheet supports attachments (paperclip → picker, shared
392
+ * upload queue) and the reply rows support reactions, swipe-to-reply,
393
+ * long-press actions, edit, delete — same surface as ConversationView.
394
+ *
395
+ * Keyboard handling: inside a Modal the OS doesn't auto-resize the
396
+ * sheet's children, so the composer needs its own KeyboardAvoidingView
397
+ * with `keyboardVerticalOffset` set to the distance between the screen
398
+ * top and the sheet top (the gutter above the sheet).
199
399
  */
200
- declare function ThreadView({ visible, onClose, conversationId, rootMessage, labelFor, }: ThreadViewProps): react_jsx_runtime.JSX.Element;
400
+ declare function ThreadView({ visible, onClose, conversationId, rootMessage, labelFor, avatarFor, }: ThreadViewProps): react_jsx_runtime.JSX.Element;
201
401
 
202
402
  interface UserNameProps {
203
403
  /** The user's external_id. Pass null to render the fallback. */
@@ -257,9 +457,15 @@ interface AttachmentPreviewProps {
257
457
  declare function AttachmentPreview({ attachment, onPress, maxWidth }: AttachmentPreviewProps): react_jsx_runtime.JSX.Element;
258
458
 
259
459
  /**
260
- * Horizontal strip of in-flight upload chips with cancel + remove
261
- * affordances. Reads from `useAttachmentUpload()` so it auto-clears
262
- * when the composer resets the queue post-send.
460
+ * Horizontal scrolling preview row of pending / in-flight / ready
461
+ * uploads. Image attachments render as 64×64 thumbnails sourced from
462
+ * the original local file URI; file attachments render as a row card
463
+ * with icon + name + size. A small × overlay on each tile lets the
464
+ * user drop the upload before send.
465
+ *
466
+ * Reads queue state from the shared `<UploadProvider>` (mounted by
467
+ * `<ConversationView>`), so the composer's "ready attachment ids"
468
+ * pickup and the strip's display always agree.
263
469
  */
264
470
  declare function UploadQueueStrip(): react_jsx_runtime.JSX.Element | null;
265
471
 
@@ -301,17 +507,16 @@ interface AttachmentPickerProps {
301
507
  visible: boolean;
302
508
  onClose: () => void;
303
509
  /**
304
- * Optional adapter that produces an `AttachmentUploadInput` from a
305
- * picked RN file. Default reads `uri`/`name`/`type`/`size` and wraps
306
- * in a fetch-compatible `Blob`. Override to plug in custom storage
307
- * or transcoding.
510
+ * Optional adapter that produces upload inputs from a picked file.
511
+ * Override to plug in custom storage / transcoding. Default reads
512
+ * from `expo-image-picker` / `expo-document-picker`.
308
513
  */
309
514
  onPickInputs?: (kind: 'image' | 'document') => Promise<{
310
- body: Blob | {
515
+ body: {
311
516
  uri: string;
312
517
  name: string;
313
518
  type: string;
314
- } | unknown;
519
+ } | Blob | unknown;
315
520
  contentType: string;
316
521
  byteSize: number;
317
522
  filename?: string;
@@ -319,16 +524,39 @@ interface AttachmentPickerProps {
319
524
  }
320
525
  /**
321
526
  * Picker sheet wrapping `expo-image-picker` and `expo-document-picker`.
322
- * Both are OPTIONAL peer dependencies — install only the ones you
323
- * actually use. If a picker is mounted without its dep installed,
324
- * the corresponding button is disabled with a tooltip explaining
325
- * which install command to run.
326
- *
327
- * Default implementation dynamically `import()`s the Expo modules
328
- * at mount; the caller can pass `onPickInputs` to skip Expo entirely.
527
+ * Both must be installed (`npx expo install expo-image-picker
528
+ * expo-document-picker`) when this component is mounted — they're
529
+ * declared as optional peers because consumers who pass
530
+ * `attachments={false}` to ConversationView never render this file.
329
531
  */
330
532
  declare function AttachmentPicker({ visible, onClose, onPickInputs }: AttachmentPickerProps): react_jsx_runtime.JSX.Element;
331
533
 
534
+ interface ImageMosaicProps {
535
+ images: Attachment[];
536
+ /**
537
+ * Pixel ceiling on the mosaic's width. The mosaic itself fills its
538
+ * parent (`width: '100%'`) and caps at this value, so it always
539
+ * fits inside whatever the bubble's actual content area is at
540
+ * render time — no more hardcoded 280px overflowing on small
541
+ * screens or inside narrow bubbles.
542
+ */
543
+ maxWidth?: number;
544
+ radius?: number;
545
+ }
546
+ /**
547
+ * WhatsApp-style multi-image grid for inside the message bubble.
548
+ * Uses flex-based widths (with `%` and `aspectRatio`) so the layout
549
+ * scales to whatever the bubble's content area is — no fixed pixel
550
+ * positioning that can overflow when the bubble is narrower than
551
+ * the configured max.
552
+ *
553
+ * * n = 1 → single tile, 16:10 aspect.
554
+ * * n = 2 → row of 2 squares.
555
+ * * n = 3 → 16:9 hero on top + row of 2 squares below.
556
+ * * n ≥ 4 → 2×2 grid with "+N more" overlay on the 4th tile.
557
+ */
558
+ declare function ImageMosaic({ images, maxWidth, radius }: ImageMosaicProps): react_jsx_runtime.JSX.Element | null;
559
+
332
560
  declare const ICONS: {
333
561
  readonly attachment: {
334
562
  readonly vb: "0 0 24 24";
@@ -669,4 +897,4 @@ declare const defaultDarkTheme: PoolseRNTheme;
669
897
  */
670
898
  declare function useAppStateLifecycle(): void;
671
899
 
672
- export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, EditableMessageBubble, type EditableMessageBubbleProps, type IconName, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };
900
+ export { AttachmentPicker, type AttachmentPickerProps, AttachmentPreview, type AttachmentPreviewProps, Avatar, type AvatarProps, ChatHeader, type ChatHeaderProps, ConversationList, type ConversationListProps, ConversationView, type ConversationViewProps, EditableMessageBubble, type EditableMessageBubbleProps, GroupDetailsSheet, type GroupDetailsSheetProps, type IconName, ImageMosaic, type ImageMosaicProps, type InboxUser, MemberList, type MemberListProps, MentionInput, type MentionInputProps, MessageActions, type MessageActionsProps, MessageBubble, type MessageBubbleProps, MessageComposer, type MessageComposerProps, MessageList, type MessageListProps, MessageRow, type MessageRowProps, PoolseIcon, type PoolseIconProps, PoolseInbox, type PoolseInboxHandle, type PoolseInboxProps, PoolseLogo, type PoolseLogoProps, type PoolseLogoVariant, type PoolseRNTheme, type PoolseRNThemeOverrides, PoolseTheme, type PoolseThemeProps, primitives as Primitives, ReactionPicker, type ReactionPickerProps, ReactionStrip, type ReactionStripProps, type ThemeColors, type ThemeRadii, type ThemeShadows, type ThemeSpacing, type ThemeType, ThreadView, type ThreadViewProps, TypingIndicator, type TypingIndicatorProps, UploadQueueStrip, UserName, type UserNameProps, defaultDarkTheme, defaultLightTheme, useAppStateLifecycle, useDisplayName, usePoolseTheme };