@poolse/react-native 2.1.0 → 2.1.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/dist/index.d.cts CHANGED
@@ -578,7 +578,7 @@ interface AttachmentPickerProps {
578
578
  * Override to plug in custom storage / transcoding. Default reads
579
579
  * from `expo-image-picker` / `expo-document-picker`.
580
580
  */
581
- onPickInputs?: (kind: 'image' | 'document') => Promise<{
581
+ onPickInputs?: (kind: 'image' | 'document' | 'camera') => Promise<{
582
582
  body: {
583
583
  uri: string;
584
584
  name: string;
@@ -588,6 +588,14 @@ interface AttachmentPickerProps {
588
588
  byteSize: number;
589
589
  filename?: string;
590
590
  }[]>;
591
+ /**
592
+ * Show the Camera item alongside Photo and File. Defaults true.
593
+ * Disable if your app explicitly doesn't want camera access (some
594
+ * App Store reviewers require an explicit usage description even
595
+ * for the picker entry). Add `NSCameraUsageDescription` to your
596
+ * iOS Info.plist when leaving this on.
597
+ */
598
+ camera?: boolean;
591
599
  }
592
600
  /**
593
601
  * Picker sheet wrapping `expo-image-picker` and `expo-document-picker`.
@@ -596,7 +604,7 @@ interface AttachmentPickerProps {
596
604
  * declared as optional peers because consumers who pass
597
605
  * `attachments={false}` to ConversationView never render this file.
598
606
  */
599
- declare function AttachmentPicker({ visible, onClose, onPickInputs }: AttachmentPickerProps): react_jsx_runtime.JSX.Element;
607
+ declare function AttachmentPicker({ visible, onClose, onPickInputs, camera, }: AttachmentPickerProps): react_jsx_runtime.JSX.Element;
600
608
 
601
609
  interface ImageMosaicProps {
602
610
  images: Attachment[];
@@ -633,6 +641,10 @@ declare const ICONS: {
633
641
  readonly vb: "0 0 24 24";
634
642
  readonly d: "<path d=\"M6 9.2a6 6 0 0 1 12 0c0 4.8 1.4 6.3 1.9 6.8H4.1C4.6 15.5 6 14 6 9.2Z\"/><path d=\"M9.6 19.8a2.5 2.5 0 0 0 4.8 0\"/>";
635
643
  };
644
+ readonly camera: {
645
+ readonly vb: "0 0 24 24";
646
+ readonly d: "<path d=\"M3 8.5a2 2 0 0 1 2-2h2.4l1.4-2h6.4l1.4 2H19a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9Z\"/><circle cx=\"12\" cy=\"13\" r=\"3.5\"/>";
647
+ };
636
648
  readonly 'check-double': {
637
649
  readonly vb: "0 0 24 24";
638
650
  readonly d: "<path d=\"M2.5 12.8 6.9 17 15.4 7.6\"/><path d=\"M11.4 16.4 12.3 17.2 21 7.6\"/>";
package/dist/index.d.ts CHANGED
@@ -578,7 +578,7 @@ interface AttachmentPickerProps {
578
578
  * Override to plug in custom storage / transcoding. Default reads
579
579
  * from `expo-image-picker` / `expo-document-picker`.
580
580
  */
581
- onPickInputs?: (kind: 'image' | 'document') => Promise<{
581
+ onPickInputs?: (kind: 'image' | 'document' | 'camera') => Promise<{
582
582
  body: {
583
583
  uri: string;
584
584
  name: string;
@@ -588,6 +588,14 @@ interface AttachmentPickerProps {
588
588
  byteSize: number;
589
589
  filename?: string;
590
590
  }[]>;
591
+ /**
592
+ * Show the Camera item alongside Photo and File. Defaults true.
593
+ * Disable if your app explicitly doesn't want camera access (some
594
+ * App Store reviewers require an explicit usage description even
595
+ * for the picker entry). Add `NSCameraUsageDescription` to your
596
+ * iOS Info.plist when leaving this on.
597
+ */
598
+ camera?: boolean;
591
599
  }
592
600
  /**
593
601
  * Picker sheet wrapping `expo-image-picker` and `expo-document-picker`.
@@ -596,7 +604,7 @@ interface AttachmentPickerProps {
596
604
  * declared as optional peers because consumers who pass
597
605
  * `attachments={false}` to ConversationView never render this file.
598
606
  */
599
- declare function AttachmentPicker({ visible, onClose, onPickInputs }: AttachmentPickerProps): react_jsx_runtime.JSX.Element;
607
+ declare function AttachmentPicker({ visible, onClose, onPickInputs, camera, }: AttachmentPickerProps): react_jsx_runtime.JSX.Element;
600
608
 
601
609
  interface ImageMosaicProps {
602
610
  images: Attachment[];
@@ -633,6 +641,10 @@ declare const ICONS: {
633
641
  readonly vb: "0 0 24 24";
634
642
  readonly d: "<path d=\"M6 9.2a6 6 0 0 1 12 0c0 4.8 1.4 6.3 1.9 6.8H4.1C4.6 15.5 6 14 6 9.2Z\"/><path d=\"M9.6 19.8a2.5 2.5 0 0 0 4.8 0\"/>";
635
643
  };
644
+ readonly camera: {
645
+ readonly vb: "0 0 24 24";
646
+ readonly d: "<path d=\"M3 8.5a2 2 0 0 1 2-2h2.4l1.4-2h6.4l1.4 2H19a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9Z\"/><circle cx=\"12\" cy=\"13\" r=\"3.5\"/>";
647
+ };
636
648
  readonly 'check-double': {
637
649
  readonly vb: "0 0 24 24";
638
650
  readonly d: "<path d=\"M2.5 12.8 6.9 17 15.4 7.6\"/><path d=\"M11.4 16.4 12.3 17.2 21 7.6\"/>";
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import { useUser, useMe, useConversations, useAttachmentUpload, useConversation, useMembers, usePresence, useMessages, useTyping, useRealtimeStatus, computeGroupPosition, formatDayLabel, useReactions, sameDay, useThread, useAttachmentUrl, usePoolse } from '@poolse/react';
2
2
  import { createContext, forwardRef, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useContext, createElement } from 'react';
3
- import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Linking, Dimensions } from 'react-native';
3
+ import { StyleSheet, Platform, Keyboard, View, Pressable, TextInput, Text, useWindowDimensions, Animated, Easing, PanResponder, Image, ScrollView, ActivityIndicator, FlatList, AppState, KeyboardAvoidingView, Modal, Dimensions, Linking } from 'react-native';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { SvgXml, Svg, Path, Circle, G } from 'react-native-svg';
6
6
  import * as ImagePicker from 'expo-image-picker';
7
7
  import * as DocumentPicker from 'expo-document-picker';
8
8
  import { manipulateAsync, SaveFormat } from 'expo-image-manipulator';
9
+ import * as Clipboard from 'expo-clipboard';
10
+ import Markdown from 'react-native-markdown-display';
9
11
 
10
12
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
11
13
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -293,6 +295,10 @@ var ICONS = {
293
295
  vb: "0 0 24 24",
294
296
  d: '<path d="M6 9.2a6 6 0 0 1 12 0c0 4.8 1.4 6.3 1.9 6.8H4.1C4.6 15.5 6 14 6 9.2Z"/><path d="M9.6 19.8a2.5 2.5 0 0 0 4.8 0"/>'
295
297
  },
298
+ camera: {
299
+ vb: "0 0 24 24",
300
+ d: '<path d="M3 8.5a2 2 0 0 1 2-2h2.4l1.4-2h6.4l1.4 2H19a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9Z"/><circle cx="12" cy="13" r="3.5"/>'
301
+ },
296
302
  "check-double": {
297
303
  vb: "0 0 24 24",
298
304
  d: '<path d="M2.5 12.8 6.9 17 15.4 7.6"/><path d="M11.4 16.4 12.3 17.2 21 7.6"/>'
@@ -1112,7 +1118,12 @@ function useUploadPreview() {
1112
1118
  getPreview: ctx?.getPreview ?? (() => null)
1113
1119
  };
1114
1120
  }
1115
- function AttachmentPicker({ visible, onClose, onPickInputs }) {
1121
+ function AttachmentPicker({
1122
+ visible,
1123
+ onClose,
1124
+ onPickInputs,
1125
+ camera = true
1126
+ }) {
1116
1127
  const theme = usePoolseTheme();
1117
1128
  const upload = useSharedUpload();
1118
1129
  const { setPreview } = useUploadPreview();
@@ -1146,8 +1157,12 @@ function AttachmentPicker({ visible, onClose, onPickInputs }) {
1146
1157
  theme.shadows.lg
1147
1158
  ],
1148
1159
  children: [
1160
+ camera ? /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("camera"), children: [
1161
+ /* @__PURE__ */ jsx(PoolseIcon, { name: "camera", size: 20, color: theme.colors.ink2 }),
1162
+ /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Camera" })
1163
+ ] }) : null,
1149
1164
  /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("image"), children: [
1150
- /* @__PURE__ */ jsx(PoolseIcon, { name: "attachment", size: 20, color: theme.colors.ink2 }),
1165
+ /* @__PURE__ */ jsx(PoolseIcon, { name: "image", size: 20, color: theme.colors.ink2 }),
1151
1166
  /* @__PURE__ */ jsx(Text, { style: [styles6.itemText, { color: theme.colors.ink }], children: "Photo or video" })
1152
1167
  ] }),
1153
1168
  /* @__PURE__ */ jsxs(Pressable, { style: styles6.item, onPress: () => pickFromKind("document"), children: [
@@ -1206,7 +1221,52 @@ async function compressImage({
1206
1221
  filename: `${base}.webp`
1207
1222
  };
1208
1223
  }
1224
+ async function assetsToInputs(assets) {
1225
+ return Promise.all(
1226
+ assets.map(async (asset) => {
1227
+ try {
1228
+ const compressed = await compressImage({
1229
+ uri: asset.uri,
1230
+ width: asset.width,
1231
+ height: asset.height,
1232
+ filename: asset.fileName ?? `image-${Date.now()}`
1233
+ });
1234
+ return {
1235
+ body: compressed.blob,
1236
+ contentType: compressed.contentType,
1237
+ byteSize: compressed.blob.size,
1238
+ filename: compressed.filename,
1239
+ _previewUri: asset.uri
1240
+ };
1241
+ } catch (err) {
1242
+ console.warn("[poolse/picker] image compression failed, sending original:", err);
1243
+ const blob = await uriToBlob(asset.uri);
1244
+ const contentType = asset.mimeType ?? blob.type ?? "image/jpeg";
1245
+ return {
1246
+ body: blob,
1247
+ contentType,
1248
+ byteSize: asset.fileSize ?? blob.size,
1249
+ filename: asset.fileName ?? `image-${Date.now()}.jpg`,
1250
+ _previewUri: asset.uri
1251
+ };
1252
+ }
1253
+ })
1254
+ );
1255
+ }
1209
1256
  async function defaultAdapter(kind) {
1257
+ if (kind === "camera") {
1258
+ const perm = await ImagePicker.requestCameraPermissionsAsync();
1259
+ if (!perm.granted) {
1260
+ console.warn("[poolse/picker] camera permission denied");
1261
+ return [];
1262
+ }
1263
+ const res2 = await ImagePicker.launchCameraAsync({
1264
+ mediaTypes: "images",
1265
+ quality: 1
1266
+ });
1267
+ if (res2.canceled) return [];
1268
+ return assetsToInputs(res2.assets);
1269
+ }
1210
1270
  if (kind === "image") {
1211
1271
  const res2 = await ImagePicker.launchImageLibraryAsync({
1212
1272
  mediaTypes: "images",
@@ -1216,36 +1276,7 @@ async function defaultAdapter(kind) {
1216
1276
  quality: 1
1217
1277
  });
1218
1278
  if (res2.canceled) return [];
1219
- return Promise.all(
1220
- res2.assets.map(async (asset) => {
1221
- try {
1222
- const compressed = await compressImage({
1223
- uri: asset.uri,
1224
- width: asset.width,
1225
- height: asset.height,
1226
- filename: asset.fileName ?? `image-${Date.now()}`
1227
- });
1228
- return {
1229
- body: compressed.blob,
1230
- contentType: compressed.contentType,
1231
- byteSize: compressed.blob.size,
1232
- filename: compressed.filename,
1233
- _previewUri: asset.uri
1234
- };
1235
- } catch (err) {
1236
- console.warn("[poolse/picker] image compression failed, sending original:", err);
1237
- const blob = await uriToBlob(asset.uri);
1238
- const contentType = asset.mimeType ?? blob.type ?? "image/jpeg";
1239
- return {
1240
- body: blob,
1241
- contentType,
1242
- byteSize: asset.fileSize ?? blob.size,
1243
- filename: asset.fileName ?? `image-${Date.now()}.jpg`,
1244
- _previewUri: asset.uri
1245
- };
1246
- }
1247
- })
1248
- );
1279
+ return assetsToInputs(res2.assets);
1249
1280
  }
1250
1281
  const res = await DocumentPicker.getDocumentAsync({
1251
1282
  copyToCacheDirectory: true,
@@ -2580,31 +2611,7 @@ var styles14 = StyleSheet.create({
2580
2611
  borderRadius: 20
2581
2612
  }
2582
2613
  });
2583
- var MarkdownModule;
2584
- var warned = false;
2585
- function loadMarkdown() {
2586
- if (MarkdownModule === void 0) {
2587
- try {
2588
- MarkdownModule = __require("react-native-markdown-display");
2589
- } catch {
2590
- MarkdownModule = null;
2591
- }
2592
- }
2593
- const fn = MarkdownModule?.default;
2594
- if (!fn) {
2595
- if (!warned) {
2596
- warned = true;
2597
- console.warn(
2598
- "[@poolse/react-native] Markdown rendering requires `react-native-markdown-display`. Install it to enable bold/italic/code/links in message bubbles. Falling back to plain text."
2599
- );
2600
- }
2601
- return null;
2602
- }
2603
- return fn;
2604
- }
2605
2614
  function renderMarkdown(body, args) {
2606
- const Markdown = loadMarkdown();
2607
- if (!Markdown) return null;
2608
2615
  const baseText = {
2609
2616
  color: args.textColor,
2610
2617
  fontFamily: args.fontFamily,
@@ -2747,53 +2754,46 @@ function MessageBubble({
2747
2754
  ) : null,
2748
2755
  renderAttachments && images.length > 0 ? /* @__PURE__ */ jsx(ImageMosaic, { images, maxWidth: contentMaxWidth }) : null,
2749
2756
  renderAttachments && files.length > 0 ? /* @__PURE__ */ jsx(View, { style: { gap: theme.spacing.xs, marginBottom: theme.spacing.xs }, children: files.map((att) => /* @__PURE__ */ jsx(AttachmentPreview, { attachment: att, maxWidth: contentMaxWidth }, att.id)) }) : null,
2750
- body.length > 0 ? (() => {
2751
- if (isDeleted) {
2752
- return /* @__PURE__ */ jsx(
2753
- Text,
2757
+ body.length > 0 ? isDeleted ? /* @__PURE__ */ jsx(
2758
+ Text,
2759
+ {
2760
+ style: [
2761
+ styles15.body,
2754
2762
  {
2755
- style: [
2756
- styles15.body,
2757
- {
2758
- color: theme.colors.ink3,
2759
- fontFamily: theme.type.fontBody,
2760
- fontSize: theme.type.bodySize,
2761
- lineHeight: theme.type.lineHeight,
2762
- fontStyle: "italic"
2763
- }
2764
- ],
2765
- children: "Message deleted"
2763
+ color: theme.colors.ink3,
2764
+ fontFamily: theme.type.fontBody,
2765
+ fontSize: theme.type.bodySize,
2766
+ lineHeight: theme.type.lineHeight,
2767
+ fontStyle: "italic"
2766
2768
  }
2767
- );
2769
+ ],
2770
+ children: "Message deleted"
2768
2771
  }
2769
- const md = markdown ? renderMarkdown(truncated, {
2770
- textColor,
2771
- linkColor: isSelf ? theme.colors.onBrand : theme.colors.brand,
2772
- fontFamily: theme.type.fontBody,
2773
- fontSize: theme.type.bodySize,
2774
- lineHeight: theme.type.lineHeight,
2775
- onLinkPress: (url) => {
2776
- void Linking.openURL(url).catch(() => void 0);
2777
- return false;
2778
- }
2779
- }) : null;
2780
- if (md) return md;
2781
- return /* @__PURE__ */ jsx(
2782
- Text,
2783
- {
2784
- style: [
2785
- styles15.body,
2786
- {
2787
- color: textColor,
2788
- fontFamily: theme.type.fontBody,
2789
- fontSize: theme.type.bodySize,
2790
- lineHeight: theme.type.lineHeight
2791
- }
2792
- ],
2793
- children: truncated
2794
- }
2795
- );
2796
- })() : null,
2772
+ ) : markdown ? renderMarkdown(truncated, {
2773
+ textColor,
2774
+ linkColor: isSelf ? theme.colors.onBrand : theme.colors.brand,
2775
+ fontFamily: theme.type.fontBody,
2776
+ fontSize: theme.type.bodySize,
2777
+ lineHeight: theme.type.lineHeight,
2778
+ onLinkPress: (url) => {
2779
+ void Linking.openURL(url).catch(() => void 0);
2780
+ return false;
2781
+ }
2782
+ }) : /* @__PURE__ */ jsx(
2783
+ Text,
2784
+ {
2785
+ style: [
2786
+ styles15.body,
2787
+ {
2788
+ color: textColor,
2789
+ fontFamily: theme.type.fontBody,
2790
+ fontSize: theme.type.bodySize,
2791
+ lineHeight: theme.type.lineHeight
2792
+ }
2793
+ ],
2794
+ children: truncated
2795
+ }
2796
+ ) : null,
2797
2797
  maxBodyLength > 0 && body.length > maxBodyLength ? /* @__PURE__ */ jsx(Pressable, { onPress: () => setExpanded((e) => !e), children: /* @__PURE__ */ jsx(
2798
2798
  Text,
2799
2799
  {
@@ -3330,7 +3330,7 @@ function MessageRow({
3330
3330
  ...msg.body && !msg.deleted_at ? {
3331
3331
  onCopy: () => {
3332
3332
  setActionsOpen(false);
3333
- void copyToClipboard(msg.body ?? "");
3333
+ void Clipboard.setStringAsync(msg.body ?? "");
3334
3334
  }
3335
3335
  } : {},
3336
3336
  ...onStartEdit ? {
@@ -3422,27 +3422,6 @@ var styles17 = StyleSheet.create({
3422
3422
  marginTop: 2
3423
3423
  }
3424
3424
  });
3425
- var clipboardModule;
3426
- var clipboardWarned = false;
3427
- async function copyToClipboard(text) {
3428
- if (clipboardModule === void 0) {
3429
- try {
3430
- clipboardModule = __require("expo-clipboard");
3431
- } catch {
3432
- clipboardModule = null;
3433
- }
3434
- }
3435
- if (!clipboardModule?.setStringAsync) {
3436
- if (!clipboardWarned) {
3437
- clipboardWarned = true;
3438
- console.warn(
3439
- "[@poolse/react-native] Copy action requires `expo-clipboard`. Install it to enable copy-to-clipboard."
3440
- );
3441
- }
3442
- return;
3443
- }
3444
- await clipboardModule.setStringAsync(text);
3445
- }
3446
3425
  var SHEET_HEIGHT_FRACTION = 0.88;
3447
3426
  function ThreadView({
3448
3427
  visible,