@cometchat/skills 4.2.1 → 4.3.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.
Files changed (28) hide show
  1. package/README.md +33 -12
  2. package/bin/install.js +32 -4
  3. package/package.json +1 -1
  4. package/skills/cometchat/SKILL.md +236 -17
  5. package/skills/cometchat-android-v5-core/SKILL.md +16 -0
  6. package/skills/cometchat-android-v6-calls/SKILL.md +1 -1
  7. package/skills/cometchat-android-v6-core/SKILL.md +202 -2
  8. package/skills/cometchat-android-v6-migration/SKILL.md +1 -1
  9. package/skills/cometchat-angular-core/SKILL.md +22 -0
  10. package/skills/cometchat-astro-patterns/SKILL.md +20 -0
  11. package/skills/cometchat-core/SKILL.md +163 -0
  12. package/skills/cometchat-flutter-v5-core/SKILL.md +17 -0
  13. package/skills/cometchat-flutter-v6-calls/SKILL.md +10 -10
  14. package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +3 -3
  15. package/skills/cometchat-flutter-v6-core/SKILL.md +178 -1
  16. package/skills/cometchat-flutter-v6-push/SKILL.md +2 -2
  17. package/skills/cometchat-flutter-v6-testing/SKILL.md +2 -2
  18. package/skills/cometchat-ios-core/SKILL.md +142 -0
  19. package/skills/cometchat-native-bare-patterns/SKILL.md +22 -1
  20. package/skills/cometchat-native-calls/SKILL.md +63 -5
  21. package/skills/cometchat-native-components/SKILL.md +7 -5
  22. package/skills/cometchat-native-core/SKILL.md +209 -6
  23. package/skills/cometchat-native-expo-patterns/SKILL.md +20 -1
  24. package/skills/cometchat-native-features/SKILL.md +57 -34
  25. package/skills/cometchat-native-troubleshooting/SKILL.md +62 -0
  26. package/skills/cometchat-nextjs-patterns/SKILL.md +28 -0
  27. package/skills/cometchat-react-patterns/SKILL.md +13 -0
  28. package/skills/cometchat-react-router-patterns/SKILL.md +33 -0
@@ -80,12 +80,19 @@ Put the init call in a top-level `useEffect` (preferred — the provider pattern
80
80
  ### Development mode
81
81
 
82
82
  ```tsx
83
- const user = await CometChatUIKit.getLoggedInUser();
83
+ let user;
84
+ try {
85
+ user = await CometChatUIKit.getLoggedInUser();
86
+ } catch (e: any) {
87
+ if (e?.code !== "NOT_FOUND") throw e; // no-session is the expected "first run" path
88
+ }
84
89
  if (!user) {
85
90
  await CometChatUIKit.login({ uid: "cometchat-uid-1" }); // note: OBJECT form
86
91
  }
87
92
  ```
88
93
 
94
+ **⚠️ `getLoggedInUser()` THROWS `code: "NOT_FOUND"` when there's no session** — it does NOT return `null`. An uncaught throw here is the #1 cause of "app stuck on splash screen" — the provider's `setReady(true)` never fires. Always wrap in try/catch and treat `NOT_FOUND` as the normal first-run path. (Validated on `@cometchat/chat-uikit-react-native@5.3.5`, kit source `CometChatUIKit.getLoggedInUser`.)
95
+
89
96
  **⚠️ `login()` takes an object `{ uid: "..." }` on React Native**, not a bare string like on the web. Passing `"cometchat-uid-1"` directly silently fails.
90
97
 
91
98
  Every new CometChat app ships 5 pre-seeded test users — `cometchat-uid-1` through `cometchat-uid-5`. Use one for development.
@@ -104,7 +111,12 @@ Guard with a module-level in-flight promise, same pattern as the web skill:
104
111
  let loginInFlight: Promise<unknown> | null = null;
105
112
 
106
113
  async function ensureLoggedIn(uid: string, authToken?: string): Promise<void> {
107
- const existing = await CometChatUIKit.getLoggedInUser();
114
+ let existing;
115
+ try {
116
+ existing = await CometChatUIKit.getLoggedInUser();
117
+ } catch (e: any) {
118
+ if (e?.code !== "NOT_FOUND") throw e; // first-run path
119
+ }
108
120
  if (existing) return;
109
121
  if (loginInFlight) {
110
122
  await loginInFlight; // reuse the pending promise
@@ -253,7 +265,12 @@ let initialized = false;
253
265
  let loginInFlight: Promise<unknown> | null = null;
254
266
 
255
267
  async function ensureLoggedIn(uid: string, authToken?: string): Promise<void> {
256
- const existing = await CometChatUIKit.getLoggedInUser();
268
+ let existing;
269
+ try {
270
+ existing = await CometChatUIKit.getLoggedInUser();
271
+ } catch (e: any) {
272
+ if (e?.code !== "NOT_FOUND") throw e; // first-run path
273
+ }
257
274
  if (existing) return;
258
275
  if (loginInFlight) {
259
276
  await loginInFlight;
@@ -376,17 +393,26 @@ npm install \
376
393
  @cometchat/chat-sdk-react-native \
377
394
  @cometchat/chat-uikit-react-native \
378
395
  react-native-gesture-handler \
379
- react-native-safe-area-context
396
+ react-native-safe-area-context \
397
+ punycode
380
398
  ```
381
399
 
400
+ > **Why `punycode`?** The kit's `CometChatAIAssistantMessageBubble` pulls in `markdown-it`, which does `require('punycode')`. Node 22+ removed `punycode` from core, so Metro can't resolve it without the userland package. Missing it = bundle 500 with `Unable to resolve module punycode`. (Validated 2026-05-26 on `@cometchat/chat-uikit-react-native@5.3.5` + Expo SDK 56 + RN 0.85.3.)
401
+
382
402
  > Note: `react-native-reanimated` is NOT a peer dependency of the kit (verified against `@cometchat/chat-uikit-react-native@5.x` `peerDependencies`). Add it only if your own app uses it for other animations.
383
403
 
384
- Expo adds `expo-av` / `expo-image-picker` depending on which features you enable. Calls require the separate package:
404
+ Expo adds `expo-av` / `expo-image-picker` depending on which features you enable. Calls require the separate package PLUS four polyfill peers the calls-sdk imports but doesn't declare:
385
405
 
386
406
  ```bash
387
- npm install @cometchat/calls-sdk-react-native
407
+ npm install @cometchat/calls-sdk-react-native \
408
+ react-native-background-timer \
409
+ react-native-url-polyfill \
410
+ react-native-performance \
411
+ valibot
388
412
  ```
389
413
 
414
+ > The calls-sdk `dist/polyfills/browser.js` imports `react-native-background-timer`, `react-native-url-polyfill/auto`, and `react-native-performance` at module top; `valibot` is consumed deeper in the calls state machine. None are in the calls-sdk `peerDependencies` array — they fail at bundle resolution if missing. (Validated 2026-05-26 on `@cometchat/calls-sdk-react-native@5.0.0`.) Then run `npx expo prebuild` (Expo) or `cd ios && pod install` (bare) so the three native modules get autolinked into the next debug build.
415
+
390
416
  See `cometchat-native-features` for when to add the calls SDK.
391
417
 
392
418
  ---
@@ -405,3 +431,180 @@ See `cometchat-native-features` for when to add the calls SDK.
405
431
  | `cometchat-native-customization` | When customizing components (text formatters, events, DataSource) |
406
432
  | `cometchat-native-production` | When setting up server-side auth + user management |
407
433
  | `cometchat-native-troubleshooting` | When diagnosing build errors, runtime failures, permission issues |
434
+
435
+ ## Visual Builder integration
436
+
437
+ When the dispatcher's Step 3.1 sets `customize=visual` and the framework maps to builder platform `react-native`, skills runs **`cometchat builder export --platform react-native`** — a single CLI command that downloads the canonical static template ZIP from `preview.cometchat.com/downloads/cometchat-builder-react-native.zip`, fetches the per-builder settings JSON via `GET /vcb/builders/{id}`, applies F3 + F10 missing-field defaults, and writes the result to `--output` (default: `src/config/`).
438
+
439
+ The canonical app uses a **Zustand-backed config store** (`src/config/store.ts`) that exposes `useConfig(selector)` — components read theme tokens and feature flags reactively. The exported `config.json` carries the **envelope shape** `{ builderId, name, settings: {...} }` — the store reads `config.settings.*` from it (F9 envelope finding, verified 2026-05-22 against the canonical Zustand store).
440
+
441
+ This is intentionally lighter than the React web copy (full `src/CometChat/` directory). The RN builder repo is a QR-driven sample with custom navigation that doesn't fit cleanly into the customer's existing navigator. So `builder export` extracts the **configuration plumbing only** (per the repo's own README §"Integration in Your Existing React Native App"), then skills writes a minimal wrapper that consumes the config in the customer's existing four-wrapper chain.
442
+
443
+ ### 1. Run `cometchat builder export`
444
+
445
+ ```bash
446
+ cometchat builder export --platform react-native --json
447
+ ```
448
+
449
+ Defaults to `--output src/config/`. The command writes:
450
+
451
+ | File | Content |
452
+ |---|---|
453
+ | `src/config/store.ts` | Zustand store with full `AppConfig` typings, AsyncStorage persistence, `useConfig<T>(selector)` hook, `useConfigStore`. Verbatim from canonical ZIP. |
454
+ | `src/config/config.json` | **Envelope-shape JSON** `{ builderId, name: ..., settings: { chatFeatures: ..., callFeatures: ..., theme: ..., agent: ... } }`. Settings come from `GET /vcb/builders/{id}`; missing fields (`inAppSounds`, `mentionAll`) defaulted by the CLI. **No SKILLS-AUTO-GENERATED sentinel** (JSON forbids `//` comments). |
455
+
456
+ Resync = re-run the same command with `--force` (full re-download + replace). See `cometchat-core` §11.6 for the resync contract.
457
+
458
+ ### Files patched
459
+
460
+ | Path | Patch |
461
+ |---|---|
462
+ | `package.json` | `npm install zustand @react-native-async-storage/async-storage` — required by the copied `store.ts`. Then the normal `cometchat-native-{bare,expo}-patterns` deps (11 explicit peers on bare, `npx expo install` list on Expo). If `useConfig(state => state.settings.callFeatures.*).oneOnOne*` returns true, also add `@cometchat/calls-sdk-react-native@5.0.0` + the Cloudsmith `@cometchat/calls-lib-webrtc` tarball per `cometchat-native-calls`. |
463
+ | Entry — `App.tsx` (bare) / `app/_layout.tsx` (Expo Router) | Init UI Kit + wrap the four-wrapper chain with `<CometChatThemeProvider theme={builderTheme}>` derived from `useConfig`. Template below. |
464
+ | `index.js` (bare) or app entry (Expo Router managed) | `import 'react-native-gesture-handler';` on line 1 (this skill's four-wrapper rule — non-negotiable, applies to every RN integration regardless of customization mode). |
465
+ | `src/utils/AppConstants.tsx` (canonical pattern) OR `.env` (Step 2c convention) | Credentials. Skills writes the canonical path the customer already had from §2 (Expo: `process.env.EXPO_PUBLIC_*`; bare: `@env` via `react-native-dotenv`). |
466
+ | `ios/Podfile` + `ios/<App>/Info.plist` (bare) or `app.json` plugins (Expo) | Camera + microphone usage descriptions if any `callFeatures.voiceAndVideoCalling.*` is true. |
467
+
468
+ ### Entry-file init pattern (bare RN / Expo)
469
+
470
+ ```tsx
471
+ // App.tsx
472
+ import './gesture-handler'; // bare RN: line 1, before any other import
473
+ import React, { useEffect, useState } from 'react';
474
+ import { Platform } from 'react-native';
475
+ import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
476
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
477
+ import {
478
+ CometChatUIKit,
479
+ UIKitSettings,
480
+ CometChatThemeProvider,
481
+ CometChatI18nProvider,
482
+ CometChatTheme,
483
+ } from '@cometchat/chat-uikit-react-native';
484
+ import { CometChat } from '@cometchat/chat-sdk-react-native';
485
+ import type { DeepPartial } from '@cometchat/chat-uikit-react-native/src/shared/helper/types';
486
+
487
+ import { useConfig } from './src/config/store';
488
+ import RootStackNavigator from './src/navigation/RootStackNavigator'; // your existing navigator
489
+
490
+ // Map builder font name → platform-specific PostScript / asset name.
491
+ // Verbatim from the canonical `App.tsx` inside the React Native Visual Builder
492
+ // ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-react-native.zip).
493
+ const FONT_MAP: Record<string, { regular: string; medium: string; bold: string }> = {
494
+ 'times new roman': {
495
+ regular: Platform.OS === 'ios' ? 'TimesNewRomanPSMT' : 'times_new_roman_regular',
496
+ medium: Platform.OS === 'ios' ? 'TimesNewRomanPSMT' : 'times_new_roman_medium',
497
+ bold: Platform.OS === 'ios' ? 'TimesNewRomanPS-BoldMT' : 'times_new_roman_bold',
498
+ },
499
+ inter: {
500
+ regular: Platform.OS === 'ios' ? 'Inter-Regular' : 'inter_regular',
501
+ medium: Platform.OS === 'ios' ? 'Inter-Medium' : 'inter_medium',
502
+ bold: Platform.OS === 'ios' ? 'Inter-Bold' : 'inter_bold',
503
+ },
504
+ roboto: {
505
+ regular: Platform.OS === 'ios' ? 'Roboto-Regular' : 'roboto_regular',
506
+ medium: Platform.OS === 'ios' ? 'Roboto-Medium' : 'roboto_medium',
507
+ bold: Platform.OS === 'ios' ? 'Roboto-Bold' : 'roboto_bold',
508
+ },
509
+ };
510
+
511
+ export default function App() {
512
+ const styleConfig = useConfig(state => state.settings.style);
513
+ const [isReady, setIsReady] = useState(false);
514
+
515
+ useEffect(() => {
516
+ const settings = new UIKitSettings.UIKitSettingsBuilder()
517
+ .setAppId(process.env.EXPO_PUBLIC_COMETCHAT_APP_ID!)
518
+ .setRegion(process.env.EXPO_PUBLIC_COMETCHAT_REGION!)
519
+ .setAuthKey(process.env.EXPO_PUBLIC_COMETCHAT_AUTH_KEY!)
520
+ .subscribePresenceForAllUsers()
521
+ .build();
522
+ CometChatUIKit.init(settings).then(() => setIsReady(true)).catch(console.error);
523
+ }, []);
524
+
525
+ const fontKey = styleConfig.typography.font.toLowerCase().trim();
526
+ const fontVariants = FONT_MAP[fontKey] ?? FONT_MAP.inter;
527
+
528
+ const theme: { light: DeepPartial<CometChatTheme>; dark: DeepPartial<CometChatTheme> } = {
529
+ light: {
530
+ color: {
531
+ primary: styleConfig.color.brandColor,
532
+ textPrimary: styleConfig.color.primaryTextLight,
533
+ textSecondary: styleConfig.color.secondaryTextLight,
534
+ },
535
+ typography: { fontFamily: fontVariants.regular },
536
+ },
537
+ dark: {
538
+ color: {
539
+ primary: styleConfig.color.brandColor,
540
+ textPrimary: styleConfig.color.primaryTextDark,
541
+ textSecondary: styleConfig.color.secondaryTextDark,
542
+ },
543
+ typography: { fontFamily: fontVariants.regular },
544
+ },
545
+ };
546
+
547
+ if (!isReady) return null;
548
+
549
+ return (
550
+ <GestureHandlerRootView style={{ flex: 1 }}>
551
+ <SafeAreaProvider>
552
+ <SafeAreaView edges={['top', 'bottom']} style={{ flex: 1 }}>
553
+ <CometChatThemeProvider theme={theme}>
554
+ <CometChatI18nProvider>
555
+ <RootStackNavigator />
556
+ </CometChatI18nProvider>
557
+ </CometChatThemeProvider>
558
+ </SafeAreaView>
559
+ </SafeAreaProvider>
560
+ </GestureHandlerRootView>
561
+ );
562
+ }
563
+ ```
564
+
565
+ **Critical:**
566
+
567
+ - `useConfig(state => state.settings.style)` is the canonical hook — **not** a static `import` of the JSON. The store hydrates from AsyncStorage on first read; importing the JSON directly would freeze the initial values and skip QR-update / resync flows that may follow.
568
+ - `CometChatThemeProvider`'s `theme` prop takes a `{ light, dark }` object (NOT a string like `"dark"`). The string-form `theme="dark"` was a v4-era shape and was removed in `chat-uikit-react-native@5+`.
569
+ - The four-wrapper rule still applies: `GestureHandlerRootView → SafeAreaProvider → CometChatThemeProvider → CometChatI18nProvider`. Skipping any of the four breaks gestures, safe areas, theming, or i18n — and fails silently in dev.
570
+ - `CometChatUIKit.init(settings)` returns a Promise — `isReady` gate before render prevents `RootStackNavigator` from mounting chat components before init resolves.
571
+ - The canonical RN builder app also registers a `CometChat.addCallListener` at the App level (handles incoming calls / busy / cancelled / ended). When `callFeatures.voiceAndVideoCalling.*` is true, copy that listener block verbatim from the canonical `App.tsx` inside the React Native Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-react-native.zip) (look for `'app'` listener id).
572
+
573
+ ### Feature flag access
574
+
575
+ Components throughout the customer's app can read flags reactively:
576
+
577
+ ```tsx
578
+ const reactionsEnabled = useConfig(s => s.settings.chatFeatures.deeperEngagement.reactions);
579
+ const audioCallsEnabled = useConfig(
580
+ s => s.settings.callFeatures.voiceAndVideoCalling.oneOnOneVoiceCalling,
581
+ );
582
+ ```
583
+
584
+ Hide buttons / disable composer actions / skip mounting components based on these. The full `AppConfig` typings are in the copied `src/config/store.ts`.
585
+
586
+ ### Resync flow
587
+
588
+ The "Re-sync visual builder" iteration menu option (see `cometchat/SKILL.md § Step 7`) is a one-command re-run:
589
+
590
+ ```bash
591
+ cometchat builder export --platform react-native --force --json
592
+ ```
593
+
594
+ `--force` is required (it explicitly authorizes replacing the existing `src/config/`). The command re-downloads the canonical static template, re-fetches the per-builder settings, and replaces the directory entirely.
595
+
596
+ Per the SKILLS-AUTO-GENERATED contract (see `cometchat-core` §11.6): customer hand-edits inside `src/config/` are lost on resync. Override via `App.tsx` (outside `src/config/`) or via the `useConfig` selector pattern documented in §"Theme derivation".
597
+
598
+ The customer reloads the dev build (`r` in Metro) — `useConfig` rehydrates from AsyncStorage on next mount.
599
+
600
+ ### Calls + builder
601
+
602
+ If `callFeatures.voiceAndVideoCalling.*` is true:
603
+ 1. Add `@cometchat/calls-sdk-react-native@5.0.0` + the Cloudsmith `@cometchat/calls-lib-webrtc` tarball (per `cometchat-native-calls`).
604
+ 2. Wire `CometChat.addCallListener` + `CometChatUIEventHandler.addCallListener` in `App.tsx` — copy the listener block verbatim from the canonical app's `App.tsx`.
605
+ 3. Mount `<CometChatIncomingCall>` between `<CometChatI18nProvider>` and `<RootStackNavigator>` when an `incomingCall` ref is set. The full pattern is in the canonical `App.tsx`.
606
+ 4. Configure iOS PushKit + Android FCM data-message wiring — defer to `cometchat-native-push` and invoke it after the Visual Builder section completes.
607
+
608
+ ### What is NOT honored in v1
609
+
610
+ `noCode.docked` (floating-widget shape) and `layout.withSideBar` don't have RN-native equivalents — RN uses tabs / stacks, not sidebars. The canonical `RootStackNavigator` from the builder repo IS NOT copied — the customer's existing navigator stays. Layout-tab features like `layout.tabs: ['chats','calls','users','groups']` need the customer's existing `bottom-tabs` navigator to add those tabs manually (skills can do this in a follow-up `cometchat-native-placement` flow). Theme + typography + chat features + call features ARE honored via `useConfig`.
@@ -90,7 +90,10 @@ npm install dayjs punycode
90
90
  If the user's flow includes voice / video calls (the `cometchat-native-features` skill's § Calls gates this):
91
91
 
92
92
  ```bash
93
- npm install @cometchat/calls-sdk-react-native
93
+ npm install @cometchat/calls-sdk-react-native \
94
+ react-native-url-polyfill \
95
+ react-native-performance \
96
+ valibot
94
97
  npx expo install \
95
98
  @react-native-community/netinfo \
96
99
  react-native-background-timer \
@@ -98,6 +101,8 @@ npx expo install \
98
101
  react-native-webrtc
99
102
  ```
100
103
 
104
+ > **`react-native-url-polyfill`, `react-native-performance`, and `valibot` are not in the calls-sdk `peerDependencies` array** — but the calls-sdk's `dist/polyfills/browser.js` imports them at module top, so Metro fails the bundle without them. Omitting any of the three yields `Unable to resolve module …` at startup with no app render. (Validated 2026-05-26 on `@cometchat/calls-sdk-react-native@5.0.0` + Expo SDK 56.)
105
+
101
106
  Skip these until the user actually wants calls. Adding WebRTC to an Expo project bloats the prebuild and requires extra permissions — don't speculatively enable it.
102
107
 
103
108
  ---
@@ -500,3 +505,17 @@ Push notifications need additional setup (APNs + FCM + maybe `expo-notifications
500
505
  | `cometchat-native-customization` | Text formatters, events, custom views |
501
506
  | `cometchat-native-production` | Server-side auth tokens + user management |
502
507
  | `cometchat-native-troubleshooting` | Prebuild failures, Expo Go errors, keyboard issues, blank chat |
508
+
509
+ ## Visual Builder integration (v4.3)
510
+
511
+ If the customer picks **Visually** in dispatcher Step 3.1, the Expo recipe diverges from the standard provider chain. Skills runs `cometchat builder export --platform react-native --json` to emit `src/config/{store.ts, config.json}` (the Zustand-backed config store + 7-field envelope JSON), then patches `App.tsx` with `useConfig` + theme derivation.
512
+
513
+ **Full recipe lives in `cometchat-native-core` §"Visual Builder integration".** Expo-specific notes:
514
+
515
+ - Use `npx expo install` for the deps (not raw `npm install`) — Expo SDK pins compatible versions.
516
+ - Env via `process.env.EXPO_PUBLIC_COMETCHAT_*` (no extra Babel plugin needed; Expo handles this).
517
+ - Required additional deps beyond the standard provider set: `zustand`, `@react-native-async-storage/async-storage`.
518
+ - `useConfig(s => s.settings.style)` — note the selector takes `AppConfig` directly, NOT `s.config.settings.style` (the `.config.` wrapper is internal to the Zustand store — Finding F6, 2026-05-21).
519
+ - A2 smoke validated: Metro bundles 10.8 MB iOS export clean on Expo SDK 54.
520
+
521
+ If the customer picks **In code**, ignore this section — the standard four-wrapper chain + provider pattern from §"Provider chain" applies.
@@ -207,63 +207,86 @@ android {
207
207
  }
208
208
  ```
209
209
 
210
- ### 3d — Register the call listener at app root
210
+ ### 3d — Register the call listeners at app root (incoming + outgoing + ongoing)
211
211
 
212
- The incoming-call UI only shows up if you've registered a listener to pick up call events. Add this once at the app root (typically in `App.tsx` or Expo Router's `_layout.tsx`):
212
+ All three call surfaces — `<CometChatIncomingCall>`, `<CometChatOutgoingCall>`, `<CometChatOngoingCall>` — are parent-controlled. None auto-mount the others. The parent must register **both** `CometChat.addCallListener` (SDK socket; surfaces incoming calls from the network) **and** `CometChatUIEventHandler.addCallListener` (UI event bus; surfaces outgoing calls fired by `<CometChatCallButtons>` / `<CometChatMessageHeader>`). Add this once at the app root (typically in `App.tsx` or Expo Router's `_layout.tsx`). Validated 2026-05-26 against `@cometchat/chat-uikit-react-native@5.3.5` end-to-end on Pixel 3 ([[project_v4_3_f75_rn_call_ui_missing]]).
213
213
 
214
214
  ```tsx
215
- import React, { useEffect, useRef, useState } from "react";
215
+ import React, { useEffect, useState } from "react";
216
+ import { StyleSheet, View } from "react-native";
216
217
  import { CometChat } from "@cometchat/chat-sdk-react-native";
217
- import { CometChatIncomingCall } from "@cometchat/chat-uikit-react-native";
218
+ import {
219
+ CometChatIncomingCall,
220
+ CometChatOutgoingCall,
221
+ CometChatOngoingCall,
222
+ CometChatUIEventHandler,
223
+ } from "@cometchat/chat-uikit-react-native";
224
+
225
+ const CALL_LISTENER_ID = "APP_CALL_LISTENER";
218
226
 
219
227
  function CallEventsProvider({ children }: { children: React.ReactNode }) {
220
- const [callReceived, setCallReceived] = useState(false);
221
- const incomingCall = useRef<CometChat.Call | null>(null);
222
- const LISTENER_ID = "APP_CALL_LISTENER";
228
+ const [incomingCall, setIncomingCall] = useState<CometChat.Call | null>(null);
229
+ const [outgoingCall, setOutgoingCall] = useState<CometChat.Call | null>(null);
230
+ const [ongoingCall, setOngoingCall] = useState<CometChat.Call | null>(null);
223
231
 
224
232
  useEffect(() => {
233
+ // SDK socket — incoming side
225
234
  CometChat.addCallListener(
226
- LISTENER_ID,
235
+ CALL_LISTENER_ID,
227
236
  new CometChat.CallListener({
228
- onIncomingCallReceived: (call) => {
229
- incomingCall.current = call;
230
- setCallReceived(true);
231
- },
232
- onOutgoingCallAccepted: (call) => {
233
- // navigate to the ongoing-call screen
234
- },
235
- onOutgoingCallRejected: () => {
236
- incomingCall.current = null;
237
- setCallReceived(false);
238
- },
239
- onIncomingCallCancelled: () => {
240
- incomingCall.current = null;
241
- setCallReceived(false);
242
- },
243
- onCallEndedMessageReceived: () => {
244
- incomingCall.current = null;
245
- setCallReceived(false);
246
- },
237
+ onIncomingCallReceived: (call: CometChat.Call) => setIncomingCall(call),
238
+ onIncomingCallCancelled: () => setIncomingCall(null),
239
+ onOutgoingCallAccepted: () => {}, // kit owns the transition
240
+ onOutgoingCallRejected: () => setOutgoingCall(null),
247
241
  })
248
242
  );
249
- return () => CometChat.removeCallListener(LISTENER_ID);
243
+ // UI event bus — outgoing side (fired by CallButtons / MessageHeader)
244
+ CometChatUIEventHandler.addCallListener(CALL_LISTENER_ID, {
245
+ ccOutgoingCall: ({ call }) => setOutgoingCall(call),
246
+ ccCallEnded: () => {
247
+ setOutgoingCall(null);
248
+ setIncomingCall(null);
249
+ setOngoingCall(null);
250
+ },
251
+ ccShowOngoingCall: ({ call }) => setOngoingCall(call),
252
+ });
253
+ return () => {
254
+ CometChat.removeCallListener(CALL_LISTENER_ID);
255
+ CometChatUIEventHandler.removeCallListener(CALL_LISTENER_ID);
256
+ };
250
257
  }, []);
251
258
 
252
259
  return (
253
260
  <>
254
261
  {children}
255
- {callReceived && incomingCall.current && (
256
- <CometChatIncomingCall
257
- call={incomingCall.current}
258
- onAccept={() => { /* navigate to ongoing-call screen */ }}
259
- onDecline={() => setCallReceived(false)}
260
- />
262
+ {incomingCall && (
263
+ <View style={StyleSheet.absoluteFill}>
264
+ <CometChatIncomingCall
265
+ call={incomingCall}
266
+ onDecline={() => setIncomingCall(null)}
267
+ onError={() => setIncomingCall(null)}
268
+ />
269
+ </View>
270
+ )}
271
+ {outgoingCall && (
272
+ <View style={StyleSheet.absoluteFill}>
273
+ <CometChatOutgoingCall call={outgoingCall} />
274
+ </View>
275
+ )}
276
+ {ongoingCall && (
277
+ <View style={StyleSheet.absoluteFill}>
278
+ <CometChatOngoingCall call={ongoingCall} />
279
+ </View>
261
280
  )}
262
281
  </>
263
282
  );
264
283
  }
265
284
  ```
266
285
 
286
+ > **DO NOT pass `onAccept` to `<CometChatIncomingCall>`** — short-circuits the kit's internal `acceptCall` + OngoingCall transition. The kit fires `ccShowOngoingCall` after `acceptCall` resolves; the listener above wires that into `setOngoingCall`, which mounts `<CometChatOngoingCall>`. Handle only `onDecline` + `onError` here. See `cometchat-native-calls` §1.8.c.
287
+
288
+ > **DO NOT skip `CometChatUIEventHandler.addCallListener`.** Without it, tapping video/voice on `<CometChatMessageHeader>` fires WebRTC + camera at the native layer but no overlay UI ever mounts — the user sees nothing change after the tap. This was [[project_v4_3_f75_rn_call_ui_missing]] (F75).
289
+
267
290
  Wrap the app (inside the existing provider chain, below `CometChatProvider`):
268
291
 
269
292
  ```tsx
@@ -286,6 +286,68 @@ After updating:
286
286
  2. Rebuild the archive
287
287
  3. Resubmit
288
288
 
289
+ ### 3bb. `react-native-document-picker` build failure on RN 0.85+ (F70)
290
+
291
+ **Symptom:** Android debug build fails with a hard Java compile error inside `react-native-document-picker`'s source — typically `cannot find symbol class GuardedResultAsyncTask`.
292
+
293
+ **Root cause:** `react-native-document-picker` references `GuardedResultAsyncTask`, which React Native removed from its Android internals in 0.85. The package is unmaintained — the last useful release predates RN 0.85.
294
+
295
+ **Fix:** uninstall it and switch to a maintained alternative if document picking is needed.
296
+
297
+ ```bash
298
+ # Uninstall the broken package
299
+ npm uninstall react-native-document-picker
300
+
301
+ # For Expo apps — official maintained pick
302
+ npx expo install expo-document-picker
303
+
304
+ # For bare RN — maintained community fork
305
+ npm install @react-native-documents/picker
306
+ ```
307
+
308
+ `cometchat verify` flags this combination automatically (`rn_doc_picker_compat` check) — runs as part of every verify since v4.3.0.
309
+
310
+ ### 3bc. `Cannot read property 'CometChatThemeProvider' of undefined` on bare RN (F78 — chat-sdk 4.0.22 packaging regression)
311
+
312
+ **Symptom (bare RN only):** the app builds cleanly (`BUILD SUCCESSFUL`) but crashes at JS startup with `TypeError: Cannot read property 'CometChatThemeProvider' of undefined`. The error often links to RN's own AsyncStorage troubleshooting text ("Make sure your project's `package.json` depends on `@react-native-async-storage/async-storage`…").
313
+
314
+ **This is NOT the §3b Maven-repo build failure** — that one fails the Gradle build. F78 builds fine, then crashes at runtime.
315
+
316
+ **Root cause (confirmed):** `@cometchat/chat-sdk-react-native` **4.0.22** declares `react`, `react-native@0.64.2`, and `@react-native-async-storage/async-storage@^1.13.4` as **hard `dependencies`** (4.0.21 had none). npm therefore installs **nested duplicate copies inside the SDK**:
317
+
318
+ ```
319
+ node_modules/@cometchat/chat-sdk-react-native/node_modules/
320
+ ├── react-native/ → 0.64.2 (duplicate of your app's 0.85.x)
321
+ └── @react-native-async-storage/async-storage/ → 1.24.0
322
+ ```
323
+
324
+ Two react-native copies = two native-module registries. The SDK's persistence code resolves AsyncStorage against its nested 1.24.0 copy, whose native module isn't the one your app autolinked → `RCTAsyncStorage` not found → the kit's `theme` module throws during evaluation → `CometChatThemeProvider` ends up `undefined`. **Expo is unaffected** because its resolver dedupes react-native to a single copy; bare RN installs the nested copy.
325
+
326
+ Confirm you're hit by it:
327
+ ```bash
328
+ ls node_modules/@cometchat/chat-sdk-react-native/node_modules/react-native/package.json && echo "F78: nested RN present"
329
+ ```
330
+
331
+ **Fix — pick one (both verified to remove the nested install):**
332
+
333
+ 1. **npm `overrides`** (keep chat-sdk 4.0.22) — add to the app's `package.json`, then reinstall:
334
+ ```jsonc
335
+ "overrides": {
336
+ "@cometchat/chat-sdk-react-native": {
337
+ "react": "$react",
338
+ "react-native": "$react-native",
339
+ "@react-native-async-storage/async-storage": "$@react-native-async-storage/async-storage"
340
+ }
341
+ }
342
+ ```
343
+ yarn uses the equivalent top-level `resolutions`.
344
+ 2. **Pin the known-good SDK:** `npm i @cometchat/chat-sdk-react-native@4.0.21` (zero deps → no nesting).
345
+ 3. **Prefer Expo** for greenfield — the Expo cohort is fresh-validated and never hits this.
346
+
347
+ After applying (1) or (2): `rm -rf node_modules && npm install`, then verify the nested `react-native` is gone with the `ls` check above. Tried and confirmed **NOT** to fix it: downgrading async-storage, `newArchEnabled=false`, clean rebuild, Metro `--reset-cache` — none address the duplicate-RN root cause.
348
+
349
+ The permanent fix is SDK-side (move react/react-native to `peerDependencies`) — tracked in **ENG-35653**.
350
+
289
351
  ### 3c. Metro cache issues (post-dep-install "not found" errors)
290
352
 
291
353
  Happens when you `npm install` a native module and Metro's bundler still has the old module graph cached.
@@ -855,3 +855,31 @@ If the project has a custom `pages/_document.tsx` for font preloading or third-p
855
855
  6. Create `pages/messages.tsx` with dynamic import (section 6)
856
856
  7. Add a `<Link href="/messages">Messages</Link>` to the layout's nav
857
857
  8. Verify: `npm run build` should succeed without SSR errors
858
+
859
+ ## 14. Visual Builder integration (v4.3)
860
+
861
+ If the customer picks **Visually** in dispatcher Step 3.1, the Next.js recipe diverges based on App Router vs Pages Router. Skills runs `cometchat builder export --platform react --output <target>` to download the canonical `src/CometChat/` + patch settings in one step.
862
+
863
+ **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + Next.js-specific gotchas:
864
+
865
+ ### App Router (recommended for Visual Builder)
866
+
867
+ - Run `cometchat builder export --platform react --output src/app/CometChat --json`.
868
+ - Create `src/app/CometChatNoSSR/CometChatNoSSR.tsx` (client component, init + login + render).
869
+ - Create `src/app/CometChatAppWrapper.tsx` with `"use client"` + `dynamic(() => import("../app/CometChatNoSSR/CometChatNoSSR"), { ssr: false })`.
870
+ - Import the wrapper in `src/app/page.tsx`.
871
+ - **Patch `src/app/CometChat/context/CometChatContext.tsx`** to use `'../../../../package.json'` (4 levels up) instead of canonical's `'../../../package.json'` (3 levels). **Finding F16** — depth differs because the directory moved into `src/app/`.
872
+
873
+ ### Pages Router (NOT recommended)
874
+
875
+ **Finding F17** (2026-05-22): Next.js Pages Router enforces "global CSS imports only in `pages/_app.tsx`". The canonical `src/CometChat/` has 25+ component-level CSS imports — Pages Router rejects the build. App Router tolerates this; Pages Router does not. Recommend App Router instead.
876
+
877
+ If a customer insists on Pages Router + Visual Builder, the only workaround is to convert all 25+ canonical CSS files into CSS Modules — heavy customer-side work. Not validated in v4.3.0.
878
+
879
+ ### Both routers
880
+
881
+ - Use `process.env.NEXT_PUBLIC_COMETCHAT_*` (NOT `import.meta.env.*` which is Vite-only).
882
+ - Pin `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5`.
883
+ - `package.json` needs `cometChatCustomConfig` block (Finding F2).
884
+
885
+ If the customer picks **In code**, ignore this section.
@@ -573,3 +573,16 @@ When integrating CometChat into a React (Vite/CRA) project, follow these steps i
573
573
  9. Add a "Messages" link to the existing nav
574
574
 
575
575
  **Do not skip step 6.** The provider must wrap the app root so init happens once, regardless of which route or modal opens chat.
576
+
577
+ ## 9. Visual Builder integration (v4.3)
578
+
579
+ If the customer picks **Visually** in dispatcher Step 3.1, the React-on-Vite/CRA recipe diverges from the code-driven path described above. Instead of authoring a `CometChatProvider`, skills runs `cometchat builder export --platform react --json` to download the canonical `src/CometChat/` directory + patch `CometChatSettings.ts` with the customer's per-builder configuration. Then patches `src/main.tsx` (Vite) or `src/index.tsx` (CRA) to mount `<CometChatApp />`.
580
+
581
+ **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + Vite/CRA-specific gotchas:
582
+
583
+ - **`cometchat builder export --platform react`** is the single command — replaces the v4.2 era "fetch JSON + manually copy" pattern. Defaults to `--output src/CometChat`. Resync = same command with `--force`. See `cometchat-core` §11.1.
584
+ - **Vite 7+ tsconfig is auto-relaxed by `cometchat apply`** (Finding F35, 2026-05-22) — `verbatimModuleSyntax`, `noUnusedLocals`, `noUnusedParameters`, `erasableSyntaxOnly` all set to `false`. CRA's defaults are already permissive — no change needed for CRA. For Visually path, also set `resolveJsonModule: true` + `allowJs: true` per cometchat-core §11.2.
585
+ - **`package.json` needs `cometChatCustomConfig`** block (Finding F2). The canonical context reads `packageJson.cometChatCustomConfig.{name, version, production}`.
586
+ - **Pinned versions**: `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5` (per the canonical README; newer versions may drift from the copied CometChat/ API surface).
587
+
588
+ If the customer picks **In code**, ignore this section — sections 1-8 above are the path. (`cometchat apply` auto-patches tsconfig for both paths per F35.)
@@ -257,6 +257,8 @@ This works because `useNavigate` returns a stable function that CometChat's call
257
257
 
258
258
  React Router v7 in framework mode is a full meta-framework with SSR, file-system routing, loaders, and actions. It has the same SSR concerns as Next.js: CometChat components cannot run on the server.
259
259
 
260
+ > **Visual Builder support on RR v7**: as of v4.3.0 (F19 fix), `cometchat builder export --platform react` post-extract codemod patches the canonical's type-as-value imports so Rolldown (Vite 7+ / RR v7's bundler) builds clean. Previously this combo failed with `[MISSING_EXPORT] "CometChatSettingsInterface" is not exported`. The patched canonical drops into `app/CometChat/` and builds in <1s (691ms client + 59ms server). No additional config required.
261
+
260
262
  ### SSR prevention
261
263
 
262
264
  v7 renders components on the server by default. CometChat components will crash during server rendering. Use a `ClientOnly` wrapper:
@@ -717,3 +719,34 @@ Do not mix v6 and v7 patterns. Detect the mode (section 1) and use the correct p
717
719
  8. Create `app/routes/messages.tsx` with `ClientOnly` + lazy import (section 3)
718
720
  9. Add a "Messages" link to the layout's nav
719
721
  10. Verify: loaders/actions contain NO CometChat imports
722
+
723
+ ## 10. Visual Builder integration (v4.3)
724
+
725
+ If the customer picks **Visually** in dispatcher Step 3.1, skills runs `cometchat builder export --platform react --output <target>` to download the canonical `src/CometChat/` + patch settings in one step.
726
+
727
+ **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + React Router-specific gotchas:
728
+
729
+ ### Framework mode (v7 default)
730
+
731
+ - Run `cometchat builder export --platform react --output app/CometChat --json`.
732
+ - Create the chat route as `app/routes/chat.client.tsx` — the `.client.tsx` suffix skips SSR for this file.
733
+ - Register the route in `app/routes.ts` via `route("chat", "routes/chat.client.tsx")`.
734
+
735
+ ### Data mode (library, v6/v7)
736
+
737
+ - Run `cometchat builder export --platform react --output src/CometChat --json` (default).
738
+ - Same Vite-based init pattern as plain Vite + React (see `cometchat-react-patterns` §9).
739
+
740
+ ### ✓ Rolldown handled (Finding F19, fixed in v4.3.0)
741
+
742
+ React Router v7's Vite 7+ build uses **Rolldown**, which rejects the canonical CometChat/'s type-as-value imports (e.g. `import { CometChatSettingsInterface } from "../context/CometChatContext"` used in type-only positions) with `[MISSING_EXPORT] "CometChatSettingsInterface" is not exported`. Webpack-based bundlers (CRA, Next.js classic) and rollup (Astro, older Vite) tolerate it as a warning.
743
+
744
+ **This is fixed in v4.3.0.** After `cometchat builder export --platform react`, the CLI runs a post-extract codemod (`applyReactRolldownFix`) that rewrites the affected imports with the inline `type` modifier so Rolldown builds clean (verified <1s: 691ms client + 59ms server). No customer action or config required — RR v7 + Visual Builder builds out of the box.
745
+
746
+ ### Both modes
747
+
748
+ - Pin `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5`.
749
+ - `package.json` needs `cometChatCustomConfig` block (Finding F2).
750
+ - Vite 7+ `tsconfig.app.json` requires the relaxation set from `cometchat-core` §11.2.
751
+
752
+ If the customer picks **In code**, ignore this section.