@developer_tribe/react-native-comnyx 0.16.0 → 0.16.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.
Files changed (82) hide show
  1. package/lib/commonjs/components/ChatList.js +39 -45
  2. package/lib/commonjs/components/ChatList.js.map +1 -1
  3. package/lib/commonjs/components/CustomerForm.js +5 -1
  4. package/lib/commonjs/components/CustomerForm.js.map +1 -1
  5. package/lib/commonjs/components/InitFailed.js +77 -21
  6. package/lib/commonjs/components/InitFailed.js.map +1 -1
  7. package/lib/commonjs/components/MediaMessageItem.js +33 -7
  8. package/lib/commonjs/components/MediaMessageItem.js.map +1 -1
  9. package/lib/commonjs/components/MediaViewerModal.js +16 -3
  10. package/lib/commonjs/components/MediaViewerModal.js.map +1 -1
  11. package/lib/commonjs/components/MessageInput.js +20 -2
  12. package/lib/commonjs/components/MessageInput.js.map +1 -1
  13. package/lib/commonjs/hooks/usePolling.js +5 -0
  14. package/lib/commonjs/hooks/usePolling.js.map +1 -1
  15. package/lib/commonjs/index.js +6 -0
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/commonjs/register/login.js +5 -0
  18. package/lib/commonjs/register/login.js.map +1 -1
  19. package/lib/commonjs/support/ComnyxSupport.js +31 -15
  20. package/lib/commonjs/support/ComnyxSupport.js.map +1 -1
  21. package/lib/commonjs/support/SupportConfigContext.js +42 -0
  22. package/lib/commonjs/support/SupportConfigContext.js.map +1 -1
  23. package/lib/commonjs/support/index.js +7 -0
  24. package/lib/commonjs/support/index.js.map +1 -1
  25. package/lib/commonjs/version.js +1 -1
  26. package/lib/module/components/ChatList.js +40 -46
  27. package/lib/module/components/ChatList.js.map +1 -1
  28. package/lib/module/components/CustomerForm.js +5 -1
  29. package/lib/module/components/CustomerForm.js.map +1 -1
  30. package/lib/module/components/InitFailed.js +79 -23
  31. package/lib/module/components/InitFailed.js.map +1 -1
  32. package/lib/module/components/MediaMessageItem.js +33 -8
  33. package/lib/module/components/MediaMessageItem.js.map +1 -1
  34. package/lib/module/components/MediaViewerModal.js +15 -3
  35. package/lib/module/components/MediaViewerModal.js.map +1 -1
  36. package/lib/module/components/MessageInput.js +21 -3
  37. package/lib/module/components/MessageInput.js.map +1 -1
  38. package/lib/module/hooks/usePolling.js +5 -0
  39. package/lib/module/hooks/usePolling.js.map +1 -1
  40. package/lib/module/index.js +2 -0
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/register/login.js +5 -0
  43. package/lib/module/register/login.js.map +1 -1
  44. package/lib/module/support/ComnyxSupport.js +33 -17
  45. package/lib/module/support/ComnyxSupport.js.map +1 -1
  46. package/lib/module/support/SupportConfigContext.js +40 -0
  47. package/lib/module/support/SupportConfigContext.js.map +1 -1
  48. package/lib/module/support/index.js +1 -0
  49. package/lib/module/support/index.js.map +1 -1
  50. package/lib/module/version.js +1 -1
  51. package/lib/typescript/src/components/ChatList.d.ts.map +1 -1
  52. package/lib/typescript/src/components/CustomerForm.d.ts.map +1 -1
  53. package/lib/typescript/src/components/InitFailed.d.ts +5 -2
  54. package/lib/typescript/src/components/InitFailed.d.ts.map +1 -1
  55. package/lib/typescript/src/components/MediaMessageItem.d.ts.map +1 -1
  56. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -1
  57. package/lib/typescript/src/components/MessageInput.d.ts.map +1 -1
  58. package/lib/typescript/src/hooks/usePolling.d.ts.map +1 -1
  59. package/lib/typescript/src/index.d.ts +2 -1
  60. package/lib/typescript/src/index.d.ts.map +1 -1
  61. package/lib/typescript/src/register/login.d.ts.map +1 -1
  62. package/lib/typescript/src/support/ComnyxSupport.d.ts +26 -3
  63. package/lib/typescript/src/support/ComnyxSupport.d.ts.map +1 -1
  64. package/lib/typescript/src/support/SupportConfigContext.d.ts +40 -0
  65. package/lib/typescript/src/support/SupportConfigContext.d.ts.map +1 -1
  66. package/lib/typescript/src/support/index.d.ts +2 -1
  67. package/lib/typescript/src/support/index.d.ts.map +1 -1
  68. package/lib/typescript/src/version.d.ts +1 -1
  69. package/package.json +10 -8
  70. package/src/components/ChatList.tsx +33 -45
  71. package/src/components/CustomerForm.tsx +5 -1
  72. package/src/components/InitFailed.tsx +80 -16
  73. package/src/components/MediaMessageItem.tsx +38 -8
  74. package/src/components/MediaViewerModal.tsx +21 -8
  75. package/src/components/MessageInput.tsx +20 -2
  76. package/src/hooks/usePolling.ts +5 -0
  77. package/src/index.ts +4 -0
  78. package/src/register/login.ts +5 -0
  79. package/src/support/ComnyxSupport.tsx +57 -14
  80. package/src/support/SupportConfigContext.tsx +78 -0
  81. package/src/support/index.ts +4 -0
  82. package/src/version.ts +1 -1
@@ -1,6 +1,9 @@
1
+ import type { LocalizationKeys } from '../types/LocalizationKeys';
1
2
  interface InitFailedProps {
2
- setInitFailed: (value: boolean) => void;
3
+ onRetry: () => void;
4
+ onBack?: () => void;
5
+ localization?: keyof LocalizationKeys;
3
6
  }
4
- export declare function InitFailed({ setInitFailed }: InitFailedProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function InitFailed({ onRetry, onBack, localization, }: InitFailedProps): import("react/jsx-runtime").JSX.Element;
5
8
  export {};
6
9
  //# sourceMappingURL=InitFailed.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InitFailed.d.ts","sourceRoot":"","sources":["../../../../src/components/InitFailed.tsx"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,UAAU,CAAC,EAAE,aAAa,EAAE,EAAE,eAAe,2CAmB5D"}
1
+ {"version":3,"file":"InitFailed.d.ts","sourceRoot":"","sources":["../../../../src/components/InitFailed.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAKlE,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,gBAAgB,CAAC;CACvC;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,MAAM,EACN,YAAgC,GACjC,EAAE,eAAe,2CAqDjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"MediaMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/components/MediaMessageItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA6FpE,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,2CA2KA"}
1
+ {"version":3,"file":"MediaMessageItem.d.ts","sourceRoot":"","sources":["../../../../src/components/MediaMessageItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AA8GpE,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,sBAAsB,CAAC;IAC7B,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB,2CA2KA"}
@@ -1 +1 @@
1
- {"version":3,"file":"MediaViewerModal.d.ts","sourceRoot":"","sources":["../../../../src/components/MediaViewerModal.tsx"],"names":[],"mappings":"AAcA,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,GACb,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,2CA0EA"}
1
+ {"version":3,"file":"MediaViewerModal.d.ts","sourceRoot":"","sources":["../../../../src/components/MediaViewerModal.tsx"],"names":[],"mappings":"AAeA,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,GACb,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,2CAsFA"}
@@ -1 +1 @@
1
- {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageInput.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,YAAY,CAAC,EAC3B,cAAc,GACf,EAAE;IACD,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,2CAmbA"}
1
+ {"version":3,"file":"MessageInput.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageInput.tsx"],"names":[],"mappings":"AA2BA,wBAAgB,YAAY,CAAC,EAC3B,cAAc,GACf,EAAE;IACD,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,2CAkcA"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePolling.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePolling.ts"],"names":[],"mappings":"AAMA,wBAAgB,UAAU,SAiDzB"}
1
+ {"version":3,"file":"usePolling.d.ts","sourceRoot":"","sources":["../../../../src/hooks/usePolling.ts"],"names":[],"mappings":"AAOA,wBAAgB,UAAU,SAqDzB"}
@@ -5,7 +5,8 @@ export { ComnyxSupport } from './support';
5
5
  export { ComnyxNotifications } from './notifications';
6
6
  export { NotificationPermissionStatus } from './NativeComnyx';
7
7
  export type { ThemeColors, ThemeColorsOverride, ThemeOverrideConfig, } from './types/Theme';
8
- export type { SupportConfig, SupportHeaderRenderProps, SupportErrorRenderProps, SupportMessageRenderProps, SupportSendPayload, } from './support';
8
+ export type { SupportConfig, SupportHeaderRenderProps, SupportErrorRenderProps, SupportMessageRenderProps, SupportSendPayload, SupportErrorSection, SupportErrorContext, SupportErrorReporter, } from './support';
9
+ export { ComnyxErrorBoundary } from './support';
9
10
  export { registerOneSignalForComnyx } from './register/collectData';
10
11
  export { useIsComnyxRegistered } from './hooks/useIsComnyxRegistered';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,YAAY,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAC9D,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,sCAAsC,CAAC;AAC1F,YAAY,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAC9D,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../../src/register/login.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,KAAK,CAAC,YAAY,EAAE,YAAY,QAqB/C"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../../src/register/login.ts"],"names":[],"mappings":"AAOA,UAAU,YAAY;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,KAAK,CAAC,YAAY,EAAE,YAAY,QAyB/C"}
@@ -6,8 +6,13 @@ import { type SupportConfig } from './SupportConfigContext';
6
6
  interface SupportComnyxProps {
7
7
  /** Language code used for localised UI strings. Defaults to `'en'`. */
8
8
  language?: LanguageCode;
9
- /** Colour palette mode. Defaults to `'dark'`. */
10
- theme?: 'light' | 'dark';
9
+ /**
10
+ * Colour palette mode. Defaults to `'dark'`.
11
+ *
12
+ * Pass `'system'` to follow the OS colour scheme — the palette flips
13
+ * automatically when the user toggles dark/light at the system level.
14
+ */
15
+ theme?: 'light' | 'dark' | 'system';
11
16
  /** Render the chat in fake/demo mode (no network). */
12
17
  fake?: boolean;
13
18
  /** Called when the user taps the close button. */
@@ -62,7 +67,25 @@ interface SupportComnyxProps {
62
67
  * Use for analytics, rate limiting, moderation or injecting extra context.
63
68
  */
64
69
  onBeforeSend?: SupportConfig['onBeforeSend'];
70
+ /**
71
+ * Receives non-render operational errors caught inside the SDK (network
72
+ * failures, polling errors, upload crashes, customer-form submit errors).
73
+ * Wire to your host's error tracker — typically `Bugsnag.notify`:
74
+ *
75
+ * ```tsx
76
+ * onError={(err, ctx) => {
77
+ * if (err instanceof Error) {
78
+ * Bugsnag.notify(err, (event) => event.addMetadata('comnyx', ctx));
79
+ * }
80
+ * }}
81
+ * ```
82
+ *
83
+ * Render-time exceptions intentionally bubble to your root React error
84
+ * boundary (e.g. `@bugsnag/plugin-react`) so the existing crash reporting
85
+ * path keeps working unchanged.
86
+ */
87
+ onError?: SupportConfig['onError'];
65
88
  }
66
- export declare function ComnyxSupport(props: SupportComnyxProps): import("react/jsx-runtime").JSX.Element;
89
+ export declare function ComnyxSupport({ language, theme, fake, onBack, themes, themeOverride, containerStyle, renderHeader, renderEmptyState, renderErrorState, renderMessage, onBeforeSend, onError, }: SupportComnyxProps): import("react/jsx-runtime").JSX.Element;
67
90
  export {};
68
91
  //# sourceMappingURL=ComnyxSupport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ComnyxSupport.d.ts","sourceRoot":"","sources":["../../../../src/support/ComnyxSupport.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQ1D,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAIhC,UAAU,kBAAkB;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC;;;OAGG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC7C,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACrD;;;OAGG;IACH,gBAAgB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACrD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CAC9C;AAoID,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CAMtD"}
1
+ {"version":3,"file":"ComnyxSupport.d.ts","sourceRoot":"","sources":["../../../../src/support/ComnyxSupport.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQ1D,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,wBAAwB,CAAC;AAGhC,UAAU,kBAAkB;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpC,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kDAAkD;IAClD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC;;;OAGG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC7C,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACrD;;;OAGG;IACH,gBAAgB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACrD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC7C;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACpC;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAe,EACf,KAAc,EACd,IAAY,EACZ,MAAM,EACN,MAAM,EACN,aAAa,EACb,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,OAAO,GACR,EAAE,kBAAkB,2CA4IpB"}
@@ -21,6 +21,38 @@ export interface SupportSendPayload {
21
21
  /** Types of the attached media. Empty for text-only messages. */
22
22
  mediaTypes: Array<'image' | 'video'>;
23
23
  }
24
+ /**
25
+ * Where inside the SDK an error originated. Helpful for dashboard grouping
26
+ * and for deciding which errors are user-visible vs silent.
27
+ */
28
+ export type SupportErrorSection = 'init' | 'pagination' | 'polling' | 'send' | 'upload' | 'media-picker' | 'customer-form' | 'accumulator';
29
+ export interface SupportErrorContext {
30
+ /** Which subsystem the error came from. */
31
+ section: SupportErrorSection;
32
+ /**
33
+ * True when the SDK already recovered (e.g. surfaced a retry affordance to
34
+ * the user). False for fire-and-forget failures worth investigating.
35
+ */
36
+ recoverable: boolean;
37
+ /** Additional structured context — usage varies per section. */
38
+ extras?: Record<string, unknown>;
39
+ }
40
+ /**
41
+ * Called whenever the SDK catches an operational error it cannot surface
42
+ * itself. Wire this to your error tracker — for Bugsnag:
43
+ *
44
+ * ```tsx
45
+ * onError={(err, ctx) => {
46
+ * if (err instanceof Error) {
47
+ * Bugsnag.notify(err, (event) => event.addMetadata('comnyx', ctx));
48
+ * }
49
+ * }}
50
+ * ```
51
+ *
52
+ * Render-time exceptions are intentionally NOT funnelled here — they bubble
53
+ * to the host's React error boundary so `@bugsnag/plugin-react` sees them.
54
+ */
55
+ export type SupportErrorReporter = (error: unknown, context: SupportErrorContext) => void;
24
56
  export interface SupportConfig {
25
57
  /**
26
58
  * Replace the built-in top header (close button + "Support team / Live" row).
@@ -49,10 +81,18 @@ export interface SupportConfig {
49
81
  * Use for analytics, rate limiting, moderation, or injecting extra context.
50
82
  */
51
83
  onBeforeSend?: (payload: SupportSendPayload) => void | Promise<void>;
84
+ /**
85
+ * Receives non-render operational errors the SDK catches internally. Route
86
+ * to your host's error tracker (e.g. `Bugsnag.notify`) so failures are not
87
+ * silently swallowed to the console.
88
+ */
89
+ onError?: SupportErrorReporter;
52
90
  }
53
91
  export declare function SupportConfigProvider({ value, children, }: {
54
92
  value: SupportConfig;
55
93
  children: ReactNode;
56
94
  }): import("react/jsx-runtime").JSX.Element;
57
95
  export declare function useSupportConfig(): SupportConfig;
96
+ export declare function setGlobalSupportErrorReporter(reporter: SupportErrorReporter | undefined): void;
97
+ export declare function reportSupportError(error: unknown, context: SupportErrorContext): void;
58
98
  //# sourceMappingURL=SupportConfigContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SupportConfigContext.d.ts","sourceRoot":"","sources":["../../../../src/support/SupportConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,sBAAsB,CAAC;IAChC,yEAAyE;IACzE,WAAW,EAAE,SAAS,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,UAAU,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAC9D,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,SAAS,CAAC;IACnC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IACjE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,SAAS,CAAC;IAChE;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAMD,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAMA;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD"}
1
+ {"version":3,"file":"SupportConfigContext.d.ts","sourceRoot":"","sources":["../../../../src/support/SupportConfigContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,sBAAsB,CAAC;IAChC,yEAAyE;IACzE,WAAW,EAAE,SAAS,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,UAAU,EAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,MAAM,GACN,YAAY,GACZ,SAAS,GACT,MAAM,GACN,QAAQ,GACR,cAAc,GACd,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,2CAA2C;IAC3C,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,mBAAmB,KACzB,IAAI,CAAC;AAEV,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAC9D,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,SAAS,CAAC;IACnC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IACjE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,SAAS,CAAC;IAChE;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE;;;;OAIG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAMD,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAMA;AAED,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAQD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,oBAAoB,GAAG,SAAS,QAG3C;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,mBAAmB,QAU7B"}
@@ -1,3 +1,4 @@
1
1
  export { ComnyxSupport } from './ComnyxSupport';
2
- export type { SupportConfig, SupportHeaderRenderProps, SupportErrorRenderProps, SupportMessageRenderProps, SupportSendPayload, } from './SupportConfigContext';
2
+ export type { SupportConfig, SupportHeaderRenderProps, SupportErrorRenderProps, SupportMessageRenderProps, SupportSendPayload, SupportErrorSection, SupportErrorContext, SupportErrorReporter, } from './SupportConfigContext';
3
+ export { ComnyxErrorBoundary } from '../components/ComnyxErrorBoundary';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/support/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/support/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.16.0";
1
+ export declare const VERSION = "0.16.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@developer_tribe/react-native-comnyx",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "React Native chat component with integrated support panel, enabling real-time customer communication and efficient agent workflow management.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./src/index.ts",
@@ -66,6 +66,7 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@commitlint/config-conventional": "^19.6.0",
69
+ "@d11/react-native-fast-image": "^8.13.0",
69
70
  "@evilmartians/lefthook": "^1.5.0",
70
71
  "@react-native-community/cli": "15.0.1",
71
72
  "@react-native/eslint-config": "^0.73.1",
@@ -81,28 +82,29 @@
81
82
  "eslint-plugin-prettier": "^5.0.1",
82
83
  "jest": "^29.7.0",
83
84
  "prettier": "^3.0.3",
85
+ "react-hook-form": "^7.66.0",
86
+ "react-native": "^0.85.1",
84
87
  "react-native-builder-bob": "^0.33.1",
88
+ "react-native-mmkv": "^4.3.1",
89
+ "react-native-safe-area-context": "^5.5.2",
85
90
  "release-it": "^17.10.0",
86
91
  "ts-jest": "^29.4.5",
87
92
  "ts-node": "^10.9.2",
88
93
  "typescript": "5.1.6",
89
94
  "use-sync-external-store": "^1.5.0",
90
- "zustand": "^5.0.3",
91
- "react-hook-form": "^7.66.0",
92
- "react-native-mmkv": "^4.3.1",
93
- "react-native-safe-area-context": "^5.5.2",
94
- "react-native": "^0.85.1"
95
+ "zustand": "^5.0.3"
95
96
  },
96
97
  "resolutions": {
97
98
  "@types/react": "^19.2.0",
98
99
  "react": "19.2.3"
99
100
  },
100
101
  "peerDependencies": {
102
+ "@d11/react-native-fast-image": ">=8.6.0",
101
103
  "react": ">=19.0.0",
104
+ "react-hook-form": ">=7.66.0",
102
105
  "react-native": ">=0.81.0",
103
106
  "react-native-mmkv": ">=4.0.0",
104
- "react-native-safe-area-context": ">=5.5.2",
105
- "react-hook-form": ">=7.66.0"
107
+ "react-native-safe-area-context": ">=5.5.2"
106
108
  },
107
109
  "commitlint": {
108
110
  "extends": [
@@ -31,7 +31,10 @@ import { ScaledSheet } from './ScaledSheet';
31
31
  import { formatDate, getDateKey } from '../utils/formatDate';
32
32
  import { activeOpacity } from '../constants/activeOpacity';
33
33
  import { useAppStore } from '../store/store';
34
- import { useSupportConfig } from '../support/SupportConfigContext';
34
+ import {
35
+ useSupportConfig,
36
+ reportSupportError,
37
+ } from '../support/SupportConfigContext';
35
38
 
36
39
  const headphonesIcon = require('../assets/headphones-01.png');
37
40
  const closeIcon = require('../assets/x-close.png');
@@ -211,11 +214,12 @@ export function ChatList({
211
214
  };
212
215
  }, []);
213
216
 
214
- const { data, setData, customer, language } = useAppStore((s) => ({
217
+ const { data, setData, customer, language, theme } = useAppStore((s) => ({
215
218
  data: s.data,
216
219
  setData: s.setData,
217
220
  customer: s.customer,
218
221
  language: s.language,
222
+ theme: s.theme,
219
223
  }));
220
224
  const ref = useRef<SectionList<AppConversationMessage>>(null);
221
225
  const [page, setPage] = useState(1);
@@ -404,7 +408,11 @@ export function ChatList({
404
408
  }
405
409
  }
406
410
 
407
- console.error('Mesajı yeniden gönderme hatası:', error);
411
+ reportSupportError(error, {
412
+ section: 'send',
413
+ recoverable: true,
414
+ extras: { retry: true, contentLength: selectedMessage.length },
415
+ });
408
416
  setSelectedMessage('');
409
417
  });
410
418
  }
@@ -463,9 +471,14 @@ export function ChatList({
463
471
  }
464
472
  });
465
473
  })
466
- .catch((_) => {
474
+ .catch((err) => {
467
475
  nextPageStatus.current = 'fail';
468
476
  setNexPageFailed(true);
477
+ reportSupportError(err, {
478
+ section: 'pagination',
479
+ recoverable: true,
480
+ extras: { page, perPage: MESSAGES_PER_PAGE },
481
+ });
469
482
  })
470
483
  .finally(() => {
471
484
  setLoading(false);
@@ -592,7 +605,11 @@ export function ChatList({
592
605
  })
593
606
  .catch((e) => {
594
607
  setInitFailed(true);
595
- console.error(e);
608
+ reportSupportError(e, {
609
+ section: 'init',
610
+ recoverable: true,
611
+ extras: { perPage: MESSAGES_PER_PAGE },
612
+ });
596
613
  })
597
614
  .finally(() => {
598
615
  setLoading(false);
@@ -624,27 +641,15 @@ export function ChatList({
624
641
 
625
642
  if (nexPageFailed) {
626
643
  return (
627
- <View
628
- style={[
629
- styles.retryContainer,
630
- { backgroundColor: themeColors.background },
631
- ]}
632
- >
633
- <TouchableOpacity
634
- style={[styles.retryButton, { backgroundColor: themeColors.primary }]}
635
- onPress={() => {
636
- setNexPageFailed(false);
637
- nextPageStatus.current = undefined;
638
- nextPage();
639
- }}
640
- activeOpacity={activeOpacity}
641
- >
642
- <AppText
643
- localization="chat.load.error"
644
- style={[styles.retryText, { color: themeColors.background }]}
645
- />
646
- </TouchableOpacity>
647
- </View>
644
+ <InitFailed
645
+ localization="chat.load.error"
646
+ onBack={onBack}
647
+ onRetry={() => {
648
+ setNexPageFailed(false);
649
+ nextPageStatus.current = undefined;
650
+ nextPage();
651
+ }}
652
+ />
648
653
  );
649
654
  }
650
655
 
@@ -654,13 +659,13 @@ export function ChatList({
654
659
  <>{renderErrorStateOverride({ retry: () => setInitFailed(false) })}</>
655
660
  );
656
661
  }
657
- return <InitFailed setInitFailed={setInitFailed} />;
662
+ return <InitFailed onRetry={() => setInitFailed(false)} onBack={onBack} />;
658
663
  }
659
664
 
660
665
  return (
661
666
  <>
662
667
  <StatusBar
663
- barStyle={'dark-content'}
668
+ barStyle={theme === 'dark' ? 'light-content' : 'dark-content'}
664
669
  backgroundColor={themeColors.background}
665
670
  animated={false}
666
671
  translucent
@@ -860,23 +865,6 @@ const styles = ScaledSheet.create({
860
865
  width: '18@vs',
861
866
  height: '18@vs',
862
867
  },
863
- retryContainer: {
864
- flex: 1,
865
- alignItems: 'center',
866
- justifyContent: 'center',
867
- paddingHorizontal: '20@s',
868
- paddingVertical: '20@vs',
869
- },
870
- retryButton: {
871
- padding: 15,
872
- borderRadius: '8@s',
873
- alignItems: 'center',
874
- justifyContent: 'center',
875
- },
876
- retryText: {
877
- fontSize: '16@vs',
878
- fontWeight: '500',
879
- },
880
868
  headphonesIcon: {
881
869
  width: '24@vs',
882
870
  height: '24@vs',
@@ -23,6 +23,7 @@ import { ScaledSheet } from './ScaledSheet';
23
23
  import type { LocalizationKeys } from '../types/LocalizationKeys';
24
24
  import { activeOpacity } from '../constants/activeOpacity';
25
25
  import { useAppStore } from '../store/store';
26
+ import { reportSupportError } from '../support/SupportConfigContext';
26
27
 
27
28
  interface CustomerFormData {
28
29
  name: string;
@@ -84,7 +85,10 @@ export function CustomerForm({
84
85
  useAppStore.getState().setForm(res.customer);
85
86
  }
86
87
  } catch (error) {
87
- console.error('Error creating customer:', error);
88
+ reportSupportError(error, {
89
+ section: 'customer-form',
90
+ recoverable: true,
91
+ });
88
92
  }
89
93
  };
90
94
 
@@ -1,28 +1,73 @@
1
- import { TouchableOpacity, View } from 'react-native';
1
+ import { Image, TouchableOpacity, View } from 'react-native';
2
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
2
3
  import { useThemeColors } from '../hooks/useThemeColors';
3
4
  import { AppText } from './AppText';
4
5
  import { ScaledSheet } from './ScaledSheet';
5
6
  import { activeOpacity } from '../constants/activeOpacity';
7
+ import type { LocalizationKeys } from '../types/LocalizationKeys';
8
+
9
+ const closeIcon = require('../assets/x-close.png');
10
+ const errorIcon = require('../assets/x-circle.png');
6
11
 
7
12
  interface InitFailedProps {
8
- setInitFailed: (value: boolean) => void;
13
+ onRetry: () => void;
14
+ onBack?: () => void;
15
+ localization?: keyof LocalizationKeys;
9
16
  }
10
17
 
11
- export function InitFailed({ setInitFailed }: InitFailedProps) {
18
+ export function InitFailed({
19
+ onRetry,
20
+ onBack,
21
+ localization = 'chat.init.error',
22
+ }: InitFailedProps) {
12
23
  const themeColors = useThemeColors();
24
+ const insets = useSafeAreaInsets();
13
25
 
14
26
  return (
15
27
  <View
16
28
  style={[styles.container, { backgroundColor: themeColors.background }]}
17
29
  >
30
+ {onBack ? (
31
+ <TouchableOpacity
32
+ activeOpacity={1}
33
+ style={[styles.closeButton, { top: Math.max(insets.top, 16) + 16 }]}
34
+ onPress={onBack}
35
+ accessibilityRole="button"
36
+ accessibilityLabel="Close support"
37
+ hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
38
+ >
39
+ <Image
40
+ source={closeIcon}
41
+ style={[styles.closeIcon, { tintColor: themeColors.text }]}
42
+ />
43
+ </TouchableOpacity>
44
+ ) : null}
45
+
18
46
  <TouchableOpacity
19
47
  activeOpacity={activeOpacity}
20
- style={[styles.retryButton, { backgroundColor: themeColors.primary }]}
21
- onPress={() => setInitFailed(false)}
48
+ style={styles.card}
49
+ onPress={onRetry}
50
+ accessibilityRole="button"
51
+ accessibilityLabel="Retry"
22
52
  >
53
+ <View
54
+ style={[
55
+ styles.iconCircle,
56
+ {
57
+ backgroundColor: themeColors.ghost,
58
+ borderColor: themeColors.lavender,
59
+ },
60
+ ]}
61
+ >
62
+ <Image
63
+ source={errorIcon}
64
+ style={[styles.errorIcon, { tintColor: themeColors.error }]}
65
+ />
66
+ </View>
67
+
23
68
  <AppText
24
- localization="chat.init.error"
25
- style={[styles.retryText, { color: themeColors.background }]}
69
+ localization={localization}
70
+ style={[styles.description, { color: themeColors.text }]}
26
71
  />
27
72
  </TouchableOpacity>
28
73
  </View>
@@ -34,18 +79,37 @@ const styles = ScaledSheet.create({
34
79
  flex: 1,
35
80
  alignItems: 'center',
36
81
  justifyContent: 'center',
37
- paddingHorizontal: '20@s',
38
- paddingVertical: '20@vs',
82
+ paddingHorizontal: '24@s',
83
+ },
84
+ closeButton: {
85
+ position: 'absolute',
86
+ left: '24@s',
39
87
  },
40
- retryButton: {
41
- paddingHorizontal: '15@s',
42
- paddingVertical: '15@vs',
43
- borderRadius: '8@s',
88
+ closeIcon: {
89
+ width: '24@vs',
90
+ height: '24@vs',
91
+ },
92
+ card: {
93
+ alignItems: 'center',
94
+ gap: '16@vs',
95
+ maxWidth: '340@s',
96
+ },
97
+ iconCircle: {
98
+ width: '72@vs',
99
+ height: '72@vs',
100
+ borderRadius: '36@vs',
101
+ borderWidth: 1,
44
102
  alignItems: 'center',
45
103
  justifyContent: 'center',
46
104
  },
47
- retryText: {
48
- fontSize: '16@vs',
49
- fontWeight: '500',
105
+ errorIcon: {
106
+ width: '36@vs',
107
+ height: '36@vs',
108
+ },
109
+ description: {
110
+ fontSize: '15@vs',
111
+ lineHeight: '22@vs',
112
+ textAlign: 'center',
113
+ opacity: 0.85,
50
114
  },
51
115
  });
@@ -1,4 +1,5 @@
1
- import { View, Image, TouchableOpacity } from 'react-native';
1
+ import { View, Image, TouchableOpacity, ActivityIndicator } from 'react-native';
2
+ import FastImage from '@d11/react-native-fast-image';
2
3
  import { useState, useEffect } from 'react';
3
4
  import type { AppConversationMessage } from '../types/Conversation';
4
5
  import { useThemeColors } from '../hooks/useThemeColors';
@@ -45,11 +46,8 @@ function MediaThumbnail({
45
46
  }, [isVideo, file.thumbnail_uri, isUploading, displayUri]);
46
47
 
47
48
  const effectiveThumb = file.thumbnail_uri || generatedThumb;
48
- const thumbSource = isVideo
49
- ? effectiveThumb
50
- ? { uri: effectiveThumb }
51
- : undefined
52
- : { uri: displayUri };
49
+ const thumbUri = isVideo ? effectiveThumb : displayUri;
50
+ const isRemote = !!thumbUri && /^https?:\/\//i.test(thumbUri);
53
51
 
54
52
  const handlePress = () => {
55
53
  if (isUploading) return;
@@ -79,8 +77,23 @@ function MediaThumbnail({
79
77
  activeOpacity={activeOpacity}
80
78
  disabled={isUploading}
81
79
  >
82
- {thumbSource ? (
83
- <Image source={thumbSource} style={imageStyle} resizeMode="cover" />
80
+ {thumbUri ? (
81
+ isRemote ? (
82
+ <FastImage
83
+ source={{
84
+ uri: thumbUri,
85
+ cache: FastImage.cacheControl.immutable,
86
+ }}
87
+ style={imageStyle}
88
+ resizeMode={FastImage.resizeMode.cover}
89
+ />
90
+ ) : (
91
+ <Image
92
+ source={{ uri: thumbUri }}
93
+ style={imageStyle}
94
+ resizeMode="cover"
95
+ />
96
+ )
84
97
  ) : (
85
98
  <View style={placeholderStyle} />
86
99
  )}
@@ -89,6 +102,11 @@ function MediaThumbnail({
89
102
  <AppText style={styles.playIcon}>▶</AppText>
90
103
  </View>
91
104
  )}
105
+ {isUploading && (
106
+ <View style={styles.uploadOverlay}>
107
+ <ActivityIndicator size="small" color="#FFFFFF" />
108
+ </View>
109
+ )}
92
110
  </TouchableOpacity>
93
111
  );
94
112
  }
@@ -336,6 +354,18 @@ const styles = ScaledSheet.create({
336
354
  alignItems: 'center',
337
355
  } as any),
338
356
  },
357
+ uploadOverlay: {
358
+ ...({
359
+ position: 'absolute',
360
+ top: 0,
361
+ left: 0,
362
+ right: 0,
363
+ bottom: 0,
364
+ justifyContent: 'center',
365
+ alignItems: 'center',
366
+ backgroundColor: 'rgba(0,0,0,0.35)',
367
+ } as any),
368
+ },
339
369
  playIcon: {
340
370
  fontSize: '20@vs',
341
371
  color: '#E0E0E0',