@cometchat/skills
Advanced tools
+32
-4
@@ -655,7 +655,35 @@ #!/usr/bin/env node | ||
| // F42 (2026-05-22) + F79 (2026-05-28): on Windows, `npx` is `npx.cmd` / | ||
| // `npx.ps1` and Node's spawn can't resolve the shim cleanly. The original | ||
| // F42 fix used `shell: true`, but that still fails in some Windows envs | ||
| // (nested npx, PowerShell execution-policy restrictions) with | ||
| // `spawn npx ENOENT` — reported by a customer on 2026-05-28. | ||
| // | ||
| // F79: on Windows, resolve npm's bundled `npx-cli.js` next to the running | ||
| // Node binary and invoke it directly via `process.execPath`. This bypasses | ||
| // the .cmd/.ps1 shim entirely. Falls back to the F42 `shell: true` path if | ||
| // npx-cli.js can't be located (pnpm / yarn / custom Node installs without | ||
| // a bundled npm). macOS/Linux keep the plain `spawn("npx")` path unchanged. | ||
| const isWindows = process.platform === "win32"; | ||
| let spawnCmd = "npx"; | ||
| let spawnArgs = npxArgs; | ||
| let spawnOpts = { stdio: "inherit" }; | ||
| if (isWindows) { | ||
| // npm ships npx-cli.js at <node-dir>/node_modules/npm/bin/npx-cli.js | ||
| const nodeDir = path.dirname(process.execPath); | ||
| const npxCli = path.join(nodeDir, "node_modules", "npm", "bin", "npx-cli.js"); | ||
| if (fs.existsSync(npxCli)) { | ||
| spawnCmd = process.execPath; // the current node binary | ||
| spawnArgs = [npxCli, ...npxArgs]; | ||
| spawnOpts = { stdio: "inherit" }; // no shell needed — direct node invocation | ||
| } else { | ||
| // Fallback: F42 path (shim via shell). Covers pnpm/yarn/custom installs. | ||
| spawnOpts = { stdio: "inherit", shell: true }; | ||
| } | ||
| } | ||
| return new Promise((resolve) => { | ||
| const child = spawn("npx", npxArgs, { | ||
| stdio: "inherit", | ||
| shell: false, | ||
| }); | ||
| const child = spawn(spawnCmd, spawnArgs, spawnOpts); | ||
| child.on("close", (code) => { | ||
@@ -662,0 +690,0 @@ if (code === 0) { |
+1
-1
| { | ||
| "name": "@cometchat/skills", | ||
| "version": "4.2.1", | ||
| "version": "4.3.0", | ||
| "publishConfig": { | ||
@@ -5,0 +5,0 @@ "access": "public" |
+33
-12
@@ -77,2 +77,17 @@ # cometchat-skills | ||
| Or add voice & video calling (new in v4.2): | ||
| ``` | ||
| /cometchat add voice and video calls to my app | ||
| ``` | ||
| When integrating chat, the dispatcher now asks **how** you want to customize (new in v4.3): | ||
| ``` | ||
| ◉ Visually — drag-and-drop in browser | ||
| ○ In code — code-driven defaults | ||
| ``` | ||
| Pick **Visually** and the dispatcher opens CometChat's Visual Builder in your browser, waits while you customize colors / layout / features, then fetches the config and emits the integration into your existing app — no ZIP downloads, no manual file copying. Works for React, React Native, iOS, Android v6, and Flutter v6. Angular customers auto-route to the In-code path (Visual Builder doesn't ship Angular code yet). | ||
| ## What happens | ||
@@ -91,15 +106,21 @@ | ||
| | Framework | Status | | ||
| |---|---| | ||
| | React.js / Vite / CRA | ✅ (`@cometchat/skills`) | | ||
| | Next.js (App Router + Pages Router) | ✅ (`@cometchat/skills`) | | ||
| | React Router v6 / v7 | ✅ (`@cometchat/skills`) | | ||
| | Astro (React islands) | ✅ (`@cometchat/skills`) | | ||
| | Expo (managed + Expo Router) | ✅ (`@cometchat/skills`) | | ||
| | Bare React Native (CLI) | ✅ (`@cometchat/skills`) | | ||
| | Angular 12-15 | ✅ (`@cometchat/skills`) | | ||
| | Android (V5 stable + V6 beta) | ✅ (`@cometchat/skills`) | | ||
| | Flutter (V5 stable + V6 beta) | ✅ (`@cometchat/skills`) | | ||
| | iOS (V5 stable) | ✅ (`@cometchat/skills`) | | ||
| | Framework | Chat | Voice & Video Calls | Visual Builder | | ||
| |---|---|---|---| | ||
| | React.js / Vite / CRA | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | Next.js (App Router + Pages Router) | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | React Router v6 / v7 | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | Astro (React islands) | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | Expo (managed + Expo Router) | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | Bare React Native (CLI) | ✅ | ✅ Ringing + Session | ✅ (v4.3) | | ||
| | Angular 12-15 | ✅ | ✅ Ringing + Session | ❌ (auto-falls back to In code) | | ||
| | Android (V5 stable + V6 beta) | ✅ | ✅ Ringing + Session | ✅ V6 (v4.3) | | ||
| | Flutter (V5 stable + V6 beta) | ✅ | ✅ Ringing + Session | ✅ V6 chat-only (v4.3) | | ||
| | iOS (V5 stable) | ✅ | ✅ Ringing + Session | ⏸ Pending Xcode 26 vendor fix | | ||
| **Calling modes:** | ||
| - **Ringing** — kit-driven incoming/outgoing call surfaces, system-level VoIP push (CallKit on iOS, ConnectionService on Android, web push fallback on browsers). Production-grade for 1:1 + group calls. | ||
| - **Session** — both peers join a shared `/meet/:sessionId` URL. No ringing. For embedded meetings, scheduled calls, support flows, broadcast use-cases. | ||
| The dispatcher asks Ringing vs Session up front in Step 3.0. | ||
| ## After the first integration | ||
@@ -106,0 +127,0 @@ |
@@ -469,1 +469,17 @@ --- | ||
| - **`gradle.properties` MUST contain `android.useAndroidX=true` AND `android.enableJetifier=true`.** Both lines, no exceptions. The CometChat V5 Android SDK transitively depends on the legacy `com.android.support:support-compat`. Without Jetifier rewriting those references to `androidx.*` at build time, Gradle hits "Duplicate class android.support.v4.os.ResultReceiver$1" and the build fails. Modern Android Studio scaffolds set `useAndroidX=true` by default but leave Jetifier off — the integration must add the Jetifier line. Idempotent — if both lines are already present, no change. | ||
| ## Visual Builder integration | ||
| **Android V5 is the primary home for Visual Builder integration.** The canonical repo at the Android Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-android.zip) ships **V5-shaped code** — `com.cometchat:chat-uikit-android:5.2.6` + `com.cometchat:calls-sdk-android:4.3.1`. The Gradle plugin `com.cometchat.builder.settings:5.0.1` auto-generates a `CometChatBuilderSettings` constants class from `cometchat-builder-settings.json` at build time. The plugin's output is plain Kotlin `object` declarations — usable from both V5 Views (the canonical path) and V6 Compose / Kotlin Views code, though V6 deps need to be added separately to a V6 project. | ||
| **The full recipe lives in `cometchat-android-v6-core` §"Visual Builder integration"** because that's where the V6-prep restructure originally landed the validated content. Both skills reference the same canonical; the V6 page carries a "V5-shaped code" warning at the top. V5 customers should follow that recipe AS-IS — it targets V5 deps natively (no shim needed). | ||
| Validated 2026-05-21 against builder-plugin 5.0.1: `./gradlew :chat-builder:assembleDebug` produces `chat-builder-debug.apk` after applying: | ||
| - Envelope-wrapped `cometchat-builder-settings.json` (`{ builderId, name, settings: {...} }` — NOT raw settings blob) | ||
| - Two missing-field defaults injected pre-write: `chatFeatures.deeperUserEngagement.mentionAll: true` + `chatFeatures.inAppSounds: { incomingMessageSound: true, outgoingMessageSound: true }` | ||
| - `android.enableJetifier=true` in `gradle.properties` | ||
| - `@style/CometChat.Builder.Theme` set on `<application>` in `AndroidManifest.xml` | ||
| Differences from the V6 page's recipe text: | ||
| - V5 calls integration uses `cometchat-android-v5-calls` patterns (see [[project_v6_calls_sdk_still_required]] for context — V5 customers don't hit the V6-specific workarounds, but the calls SDK requirement is the same). | ||
| - The Compose stack split (`chatuikit-compose-android` vs `chatuikit-kotlin-android`) doesn't apply to V5 — the V5 UI Kit is Kotlin Views only. |
| --- | ||
| name: cometchat-android-v6-calls | ||
| description: CometChat Calls v6 integration for native Android (V6 beta — Compose + Kotlin Views). Works end-to-end on chatuikit-compose-android:6.0.0 (validated 2026-05-12 against web peer) — but only with FIVE non-obvious workarounds the kit itself doesn't ship: (1) explicit `calls-sdk-android:5.0.+` peer dep, (2) `annotations-java5` exclude, (3) stub classes for legacy `com.cometchat.calls.{CometChatRTCView, model.RTCUser, model.RTCReceiver, model.RTCCallback}` to satisfy chat-sdk's CallManager bytecode, (4) AVOID `CometChatCallButtons` (broken — captures first-rendered user globally; ignores per-row prop) — instead wire your own button → `CometChat.initiateCall` → `CometChatCallActivity.Companion.launchOutgoingCallScreen(context, call, null)`, (5) `Call` constructor arg order CHANGED in chat-sdk 5.x: `(receiverUid, receiverType, type)` not `(receiverUid, type, receiverType)`. Covers UIKitSettings calling configuration, surface-aware Compose+Views routing, foreground service correctness on Android 14+, ConnectionService + FCM VoIP push. | ||
| description: "CometChat Calls v6 integration for native Android (V6 beta — Compose + Kotlin Views). Works end-to-end on chatuikit-compose-android:6.0.0 (validated 2026-05-12 against web peer) — but only with FIVE non-obvious workarounds the kit itself doesn't ship — (1) explicit calls-sdk-android:5.0.+ peer dep, (2) annotations-java5 exclude, (3) stub classes for legacy com.cometchat.calls.{CometChatRTCView, model.RTCUser, model.RTCReceiver, model.RTCCallback} to satisfy chat-sdk's CallManager bytecode, (4) AVOID CometChatCallButtons (broken — captures first-rendered user globally; ignores per-row prop) — instead wire your own button to CometChat.initiateCall then CometChatCallActivity.Companion.launchOutgoingCallScreen(context, call, null), (5) Call constructor arg order CHANGED in chat-sdk 5.x — (receiverUid, receiverType, type) not (receiverUid, type, receiverType). Covers UIKitSettings calling configuration, surface-aware Compose+Views routing, foreground service correctness on Android 14+, ConnectionService + FCM VoIP push." | ||
| license: "MIT" | ||
@@ -5,0 +5,0 @@ compatibility: "Android Studio Hedgehog+, JDK 17, Gradle 8+, AGP 8+, minSdk 28+ (V6 raised the floor); chatuikit-compose-android:6.0.+ OR chatuikit-kotlin-android:6.0.+ PAIRED with com.cometchat:calls-sdk-android:5.0.+ (peer dep required despite the V6 marketing — see §1.0)" |
@@ -93,5 +93,6 @@ --- | ||
| **Add these two lines to `gradle.properties` at the project root** before any UI Kit code is wired in: | ||
| **Add these three lines to `gradle.properties` at the project root** before any UI Kit code is wired in: | ||
| ```properties | ||
| org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m | ||
| android.useAndroidX=true | ||
@@ -101,4 +102,6 @@ android.enableJetifier=true | ||
| Both are **mandatory**. Jetifier rewrites the legacy `android.support.*` references in the CometChat SDK's transitive deps to their `androidx.*` equivalents at build time, so the duplicate-class error doesn't happen. | ||
| All three are **mandatory**. Jetifier rewrites the legacy `android.support.*` references in the CometChat SDK's transitive deps to their `androidx.*` equivalents at build time, so the duplicate-class error doesn't happen. | ||
| **Why the heap setting matters (F47, 2026-05-22)**: a fresh Android Studio scaffold's default `org.gradle.jvmargs=-Xmx2048m` is **insufficient** when calls features are enabled — `com.cometchat:calls-sdk-android:4.+` transitively pulls `react-native` and other heavy deps that Jetifier has to rewrite. The first `assembleDebug` OOMs partway through with `OutOfMemoryError: Java heap space`. Bumping to `4096m` + `MaxMetaspaceSize=1024m` is the validated minimum. If you skip this, the customer sees a confusing mid-build crash with no actionable error. | ||
| A freshly-created Android Studio project usually has `android.useAndroidX=true` already (Arctic Fox+) but **Jetifier is OFF by default** since it's deprecated in newer SDK landscapes. Both V5 and V6 CometChat SDKs still need it. If `gradle.properties` doesn't have either line, append both. If it has `useAndroidX=true` but no Jetifier line, add the Jetifier line. Idempotent. | ||
@@ -357,1 +360,198 @@ | ||
| - `gradle.properties` MUST contain `android.useAndroidX=true` AND `android.enableJetifier=true` — see § 1.3a. Without Jetifier, the build fails with "Duplicate class android.support.v4.os.ResultReceiver$1" because the CometChat SDK's transitive `com.android.support:support-compat` collides with `androidx.core` in any modern Android Studio project | ||
| ## Visual Builder integration | ||
| > **⚠ The Visual Builder emits V5-shaped code.** The canonical builder repo at the Android Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-android.zip) uses `com.cometchat:chat-uikit-android:5.2.6` — the **V5** Android UI Kit. There is no V6-native Visual Builder canonical from vendor side yet ([F22 finding, 2026-05-22](https://github.com/cometchat/cometchat-skills/issues)). When a V6 project picks the Visually path, skills emits V5 deps (`chat-uikit-android:5.+`) into `app/build.gradle` — your V6 Compose / Kotlin Views code in the rest of the project is untouched, but the Visual-Builder-emitted screens are V5-flavored. Treat as transitional until vendor publishes a V6 canonical. V6 customers preferring a single-version dep tree should use the In-code path. | ||
| > | ||
| > The recipe below is identical to what `cometchat-android-v5-core` §"Visual Builder integration" prescribes (both reference the same canonical). This page kept for V6 customers who still hit the Visually flow. | ||
| When the dispatcher's Step 3.1 sets `customize=visual` and the platform resolves to `android`, skills runs **`cometchat builder export --platform android`** — a single CLI command that downloads the canonical static template ZIP from `preview.cometchat.com/downloads/cometchat-builder-android.zip`, fetches the per-builder settings JSON, applies F3 + F10 missing-field defaults, and writes 2 files to `--output` (default: `cometchat/`): | ||
| - `BuilderSettingsHelper.kt` — verbatim helper (with original `package com.cometchat.builder` declaration; **skills patches the package to the customer's app package** before final placement at `app/src/main/java/<package>/cometchat/`) | ||
| - `cometchat-builder-settings.json` — **envelope-shape JSON** `{ builderId, name, settings: {...} }` (no sentinel — JSON forbids `//` comments) | ||
| ### 1. Run `cometchat builder export` | ||
| ```bash | ||
| cometchat builder export --platform android --json | ||
| ``` | ||
| Defaults to `--output cometchat/`. The CLI emits the helper with the original `com.cometchat.builder` package declaration. **You must then move the files** to the customer's project + rewrite the package, per the table below: | ||
| ### 2. Place + patch (after `builder export`) | ||
| | Source (from `--output`) | Destination | Notes | | ||
| |---|---|---| | ||
| | `cometchat/cometchat-builder-settings.json` | `app/cometchat-builder-settings.json` | Move to app module root (sibling of `build.gradle.kts`). The Gradle plugin requires the envelope shape — already provided by the CLI. | | ||
| | `cometchat/BuilderSettingsHelper.kt` | `app/src/main/java/<customer-package>/cometchat/BuilderSettingsHelper.kt` | **Move + 3 transforms** — see F50/F51 below. | | ||
| | `cometchat/font/*` | `app/src/main/res/font/*` | 12 font files (Arial / Inter / Roboto / Times — regular/medium/bold). Auto-emitted by `builder export --platform android` since F48 (2026-05-22). Just move them. | | ||
| | (skills-emitted, not from ZIP) | `app/src/main/java/<customer-package>/cometchat/CometChatApp.kt` | Compose wrapper that mounts kit View components via `AndroidView` and applies `BuilderSettingsHelper.applySettings*` to each | | ||
| #### F50 + F51 — BuilderSettingsHelper.kt requires 3 transforms on move (NON-NEGOTIABLE) | ||
| After moving `cometchat/BuilderSettingsHelper.kt` to | ||
| `app/src/main/java/<customer-package>/cometchat/BuilderSettingsHelper.kt`, | ||
| the file needs these 3 transforms before it compiles: | ||
| **1. Rewrite the package line:** | ||
| ```kotlin | ||
| package com.cometchat.builder // before | ||
| package com.example.myapp.cometchat // after (match customer's applicationId + .cometchat suffix) | ||
| ``` | ||
| **2. Add 3 explicit imports** under the existing import block: | ||
| ```kotlin | ||
| import com.cometchat.builder.CometChatBuilderSettings // auto-generated constants class — stays at original package | ||
| import com.example.myapp.BuildConfig // customer's BuildConfig — must use their applicationId | ||
| import com.example.myapp.R // customer's R class — same applicationId | ||
| ``` | ||
| Without these, the file fails to compile with `Unresolved reference 'R'`, `Unresolved reference 'BuildConfig'`, `Unresolved reference 'CometChatBuilderSettings'`. (When the file was in the original `com.cometchat.builder` package, these resolved implicitly via same-package; after the rewrite, they need explicit imports.) | ||
| **3. Strip the entire `applySettingsToBottomNavigationView` method** (per README option 2 — skills doesn't emit the bottom-nav shape). The method references `R.id.nav_chats`, `R.id.nav_calls`, `R.id.nav_users`, `R.id.nav_groups` which don't exist in the customer's `res/menu/`. Removing the method removes the references. | ||
| ```kotlin | ||
| // Delete the entire function from `fun applySettingsToBottomNavigationView(...)` to its closing `}` — | ||
| // roughly lines 27-54 in the canonical file. Leave a comment in its place so future readers | ||
| // understand why: | ||
| // | ||
| // // applySettingsToBottomNavigationView removed per cometchat-android-v6-core | ||
| // // SKILL.md (README option 2 — no bottom-nav module in app integration). | ||
| ``` | ||
| Validated on `/Users/swapnil/Downloads/builder-demo/my-android-app/` 2026-05-25 — after all 3 transforms, `./gradlew :app:assembleDebug` → `BUILD SUCCESSFUL in 6s`. F50 + F51 findings. | ||
| The Gradle plugin REQUIRES the `{ builderId, settings: {...} }` envelope — writing the raw settings blob produces an empty `CometChatBuilderSettings` constants class and Kotlin compile fails with `Unresolved reference 'ChatFeatures'` / `'CallFeatures'`. The CLI always writes the envelope shape. | ||
| Resync = re-run `cometchat builder export --platform android --force`. Re-apply the move+package-rewrite each time (Skills should automate this in a future release). | ||
| ### Files patched | ||
| | Path | Patch | | ||
| |---|---| | ||
| | `settings.gradle.kts` | Add `maven("https://dl.cloudsmith.io/public/cometchat/cometchat/maven/")` to BOTH `pluginManagement.repositories` AND `dependencyResolutionManagement.repositories` | | ||
| | `app/build.gradle.kts` | Add `id("com.cometchat.builder.settings") version "5.0.1"` to the `plugins { }` block. Add `implementation("com.cometchat:chat-uikit-android:5.1.+")`. Add `implementation("com.cometchat:calls-sdk-android:4.1.+")` if `cometchat-builder-settings.json` has any call feature enabled | | ||
| | `gradle.properties` | `android.useAndroidX=true` + `android.enableJetifier=true` per §1.3a (non-negotiable) | | ||
| | `AndroidManifest.xml` | Set `android:theme="@style/CometChat.Builder.Theme"` on `<application>`. Add `RECORD_AUDIO` + `CAMERA` permissions if any `CallFeatures.*` flag is true | | ||
| | `Application` subclass | Call `CometChatUIKit.init(this, uiKitSettings)` in `onCreate()` per §2 — credentials from `BuildConfig.COMETCHAT_*` via §1.4 | | ||
| ### Init flow (build-time + runtime) | ||
| **Build time** — the Gradle plugin: | ||
| 1. Reads `app/cometchat-builder-settings.json` | ||
| 2. Generates `com.cometchat.builder.CometChatBuilderSettings` (typed Kotlin constants: `ChatFeatures.CoreMessagingExperience.PHOTOSSHARING`, `Style.Color.BRANDCOLOR`, etc.) | ||
| 3. Injects style values into `@style/CometChat.Builder.Theme` so the kit's `CometChatTheme` resolves builder tokens automatically | ||
| **Runtime** — customer code accesses both: | ||
| ```kotlin | ||
| import com.cometchat.builder.CometChatBuilderSettings // generated by plugin | ||
| import <package>.cometchat.BuilderSettingsHelper // copied helper | ||
| if (CometChatBuilderSettings.ChatFeatures.CoreMessagingExperience.PHOTOSSHARING) { | ||
| // photo attachment enabled | ||
| } | ||
| BuilderSettingsHelper.applySettingsToMessageList(binding.messageList) | ||
| ``` | ||
| ### The wrapper template | ||
| ```kotlin | ||
| // app/src/main/java/<package>/cometchat/CometChatApp.kt | ||
| package <package>.cometchat | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.mutableStateOf | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.runtime.setValue | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.viewinterop.AndroidView | ||
| import com.cometchat.chat.models.Group | ||
| import com.cometchat.chat.models.User | ||
| import com.cometchat.chatuikit.conversations.CometChatConversations | ||
| import com.cometchat.chatuikit.messagecomposer.CometChatMessageComposer | ||
| import com.cometchat.chatuikit.messageheader.CometChatMessageHeader | ||
| import com.cometchat.chatuikit.messagelist.CometChatMessageList | ||
| /** | ||
| * Top-level chat surface emitted by the Visual Builder Visually path. | ||
| * | ||
| * Hosts the kit's View-based components via AndroidView; BuilderSettingsHelper | ||
| * (copied from the builder repo) wires CometChatBuilderSettings → component | ||
| * visibility on each instance. | ||
| * | ||
| * CometChatUIKit.init(...) must have been called in Application.onCreate() | ||
| * BEFORE this composable mounts — see §2. | ||
| */ | ||
| @Composable | ||
| fun CometChatApp() { | ||
| var selectedUser by remember { mutableStateOf<User?>(null) } | ||
| var selectedGroup by remember { mutableStateOf<Group?>(null) } | ||
| val hasSelection = selectedUser != null || selectedGroup != null | ||
| if (!hasSelection) { | ||
| AndroidView( | ||
| modifier = Modifier.fillMaxSize(), | ||
| factory = { ctx -> | ||
| CometChatConversations(ctx).apply { | ||
| BuilderSettingsHelper.applySettingsToConversations(this) | ||
| setOnItemClickListener { _, conversation -> | ||
| when (val entity = conversation.conversationWith) { | ||
| is User -> { selectedUser = entity; selectedGroup = null } | ||
| is Group -> { selectedUser = null; selectedGroup = entity } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| ) | ||
| return | ||
| } | ||
| Column(Modifier.fillMaxSize()) { | ||
| AndroidView(factory = { ctx -> | ||
| CometChatMessageHeader(ctx).apply { | ||
| user = selectedUser | ||
| group = selectedGroup | ||
| BuilderSettingsHelper.applySettingsToMessageHeader(this) | ||
| } | ||
| }) | ||
| AndroidView(modifier = Modifier.weight(1f), factory = { ctx -> | ||
| CometChatMessageList(ctx).apply { | ||
| user = selectedUser | ||
| group = selectedGroup | ||
| BuilderSettingsHelper.applySettingsToMessageList(this) | ||
| } | ||
| }) | ||
| AndroidView(factory = { ctx -> | ||
| CometChatMessageComposer(ctx).apply { | ||
| user = selectedUser | ||
| group = selectedGroup | ||
| BuilderSettingsHelper.applySettingsToMessageComposer(this) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| ``` | ||
| `BuilderSettingsHelper.kt` is copied verbatim from the builder repo. Methods used above: | ||
| - `applySettingsToConversations(...)` → user-status / receipts / search-box visibility | ||
| - `applySettingsToMessageHeader(...)` → voice/video call buttons + user-status visibility (group vs user-aware) | ||
| - `applySettingsToMessageList(...)` → edit/delete/reply-in-thread/reactions/translation/conversation-starter/smart-replies/message-privately visibility | ||
| - `applySettingsToMessageComposer(...)` → attachment/voice-note/poll/sticker/etc. visibility | ||
| For a Compose-native stack (`chatuikit-compose-android` instead of `chatuikit-android` Views), skip the `AndroidView` + `BuilderSettingsHelper` indirection and read `CometChatBuilderSettings.ChatFeatures.*` constants directly into the Compose components' visibility/feature props — see `cometchat-android-v6-compose-components`. The Views-via-AndroidView path above matches the canonical builder repo, which is Views-based. | ||
| ### Calls + builder | ||
| If any `CometChatBuilderSettings.CallFeatures.*` flag is true: | ||
| 1. Init Calls SDK in `Application.onCreate()` alongside UI Kit (see `cometchat-android-v6-calls`) | ||
| 2. Mount `CometChatIncomingCall` overlay — the builder repo's `BuilderApplication.kt` registers `ActivityLifecycleCallbacks` and shows a `Snackbar`-hosted incoming-call view at the top of the foreground activity. Copy that pattern verbatim from `chat-builder/src/main/java/com/cometchat/builder/utils/BuilderApplication.kt` (inside the Android Visual Builder ZIP at https://preview.cometchat.com/downloads/cometchat-builder-android.zip) | ||
| 3. FCM data-message wiring — defer to `cometchat-android-v6-push` | ||
| ### What is NOT honored in v1 | ||
| The builder repo's `HomeActivity` exposes a `BottomNavigationView` with up to 4 tabs (Chats / Calls / Users / Groups) driven by `CometChatBuilderSettings.Layout.TABS`. Skills emits a single Compose conversations surface, not the tabbed shape. The corresponding `applySettingsToBottomNavigationView` method is also **intentionally removed** when emitting `BuilderSettingsHelper.kt` (per README option 2). Theme + typography + chat-feature toggles ARE honored. For the full tabbed shape, copy the builder repo's `HomeActivity` + per-tab fragments alongside skills' emission (or use README option 1 — "Import as module"). |
@@ -15,3 +15,3 @@ --- | ||
| Migration recipes for moving from CometChat Android UIKit V5 (`chat-uikit-android:5.x`) to V6 (`chatuikit-{compose,kotlin}-android:6.0.0-beta2`). V6 is beta — most production apps should stay on V5 today; this skill is for teams evaluating V6 or planning the eventual migration. | ||
| Migration recipes for moving from CometChat Android UIKit V5 (`chat-uikit-android:5.x`) to V6 (`chatuikit-{compose,kotlin}-android:6.0.0-beta2`). V6 is stable (GA 2026-05-25) — most production apps should stay on V5 today; this skill is for teams evaluating V6 or planning the eventual migration. | ||
@@ -18,0 +18,0 @@ V6 is a different SDK, not a drop-in replacement. The migration is roughly the size of jumping from React Native UIKit v5 to v6 — package coordinates, builder APIs, theme system, calls handling all change. |
@@ -432,2 +432,24 @@ --- | ||
| ## 11. Visual Builder integration — not available for Angular | ||
| When the dispatcher's Step 3.1 (Customization preference) runs on an Angular project, **it auto-routes to the code-driven path**. The dashboard's Visual Builder export pipeline at `https://preview.cometchat.com/downloads/cometchat-builder-{platform}.zip` ships ZIPs for `react`, `react-native`, `ios`, `android`, and `flutter` — there's no `angular` emitter. Skills can't bridge that gap by translating React/JSON output into Angular code because the kit's Angular package (`@cometchat/chat-uikit-angular`) has different selectors (`<cometchat-conversations>`), module shapes (`CometChatConversationsModule`), and content-projection slot APIs than React. | ||
| For comparison, the other family core skills have a `## Visual Builder integration` section that documents per-platform copy-the-canonical-app recipes. Angular has no equivalent; this section is the intentional empty entry. | ||
| **What the dispatcher does on an Angular project:** | ||
| 1. Skips the Visually-vs-In-code prompt entirely. | ||
| 2. Surfaces a one-time message in the chat: | ||
| > *"The Visual Builder doesn't ship Angular code yet (the dashboard's export covers React / React Native / iOS / Android / Flutter today). I'll set up the code-driven Angular integration instead — you can theme via `CometChatThemeService` later. Want to be notified when an Angular Visual Builder lands? Drop a 👍 on https://github.com/cometchat/cometchat-skills/discussions/categories/feature-requests."* | ||
| 3. Sets `customize=code` in `.cometchat/config.json` via `npx @cometchat/skills-cli config save --customize code --json`. | ||
| 4. Continues to the standard Angular flow (§3a intent → §3b recommendation → §3c placement → §5 code emission via this skill + `cometchat-angular-{components,placement,patterns,theming}`). | ||
| If a customer arrives at an Angular project with a stale `customize=visual` value (carried over from a previous run on a React/Flutter/etc. project), the dispatcher OVERWRITES it to `code` before routing — calling `builder create --platform angular` would fail at the CLI layer (rejected: "Missing or invalid --platform"). The override prevents the customer from seeing a confusing error instead of the explanatory note above. | ||
| **For when Visual Builder Angular support lands** — track [issue link TBD] on the public repo. At that point, this section gets a full canonical-app recipe (parallel to `cometchat-core` §11) and the dispatcher's §3.1 table flips `angular` from "not supported" to a `platform: angular` row. | ||
| --- | ||
| ## Skill routing reference | ||
@@ -434,0 +456,0 @@ |
@@ -688,1 +688,21 @@ --- | ||
| 3. Each island wraps its own `CometChatProvider` -- there is no global provider at the Astro level | ||
| ## 11. Visual Builder integration (v4.3) | ||
| If the customer picks **Visually** in dispatcher Step 3.1, skills runs `cometchat builder export --platform react --output src/CometChat --json` to download the canonical + patch settings in one step. | ||
| **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + Astro-specific gotchas: | ||
| - Run `cometchat builder export --platform react --output src/CometChat --json`. | ||
| - Create a React island wrapper at `src/components/CometChatIsland.tsx` (or similar) that does init + login + renders `<CometChatProvider><CometChatApp /></CometChatProvider>`. | ||
| - Mount in `src/pages/chat.astro` as `<CometChatIsland client:only="react" />` — **never** `client:load`. The canonical CometChat/ uses `window` / `document` at module scope; `client:load` will SSR the import resolution and crash. | ||
| - Use `import.meta.env.PUBLIC_COMETCHAT_*` (Astro's public-env prefix). | ||
| - Astro's Vite-based build uses rollup (not Rolldown), so it tolerates the canonical's type-as-value imports as warning — Astro is **the most reliable Visual Builder host** in the v4.3.0 web matrix (validated 2026-05-22). | ||
| ### Both Astro and other Vite hosts | ||
| - Pin `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5`. | ||
| - `package.json` needs `cometChatCustomConfig` block (Finding F2). | ||
| - Vite 7+ `tsconfig.app.json` requires the relaxation set from `cometchat-core` §11.2. | ||
| If the customer picks **In code**, ignore this section. |
@@ -648,1 +648,164 @@ --- | ||
| ``` | ||
| ## 11. Visual Builder integration | ||
| When the dispatcher's Step 3.1 sets `customize=visual`, skills runs **`cometchat builder export --platform react`** — a single CLI command that mirrors the dashboard's Export-button workflow. It downloads the canonical static template ZIP from `preview.cometchat.com/downloads/cometchat-builder-react.zip`, fetches the per-builder settings JSON via `GET /vcb/builders/{id}`, unzips the template, patches `CometChatSettings.ts` with the fetched JSON + missing-field defaults + a sentinel comment, and writes the result to `--output` (default: `src/CometChat/`). | ||
| The `src/CometChat/` directory contains `CometChatApp.tsx`, the repo's own `CometChatProvider`-style context, `CometChatHome` with tabs (Chats / Calls / Users / Groups), theme hooks (`useThemeStyles`, `useSystemColorScheme`), login listener wiring, and 13 supporting components. Skills does NOT hand-roll a wrapper — the canonical app IS the wrapper. | ||
| This is the same pattern iOS (verbatim `MessagesVC.swift`), Android v6 (verbatim `BuilderSettingsHelper.kt`), and Flutter v6 (verbatim `chat_builder/` package) use. React just happens to copy a directory of TSX files instead of a single class. | ||
| ### 11.1 Run `cometchat builder export` | ||
| After Step 3.1.v step 4 (customer says "Done" + skills caches the builderId in `.cometchat/builder.json`), run: | ||
| ```bash | ||
| cometchat builder export --platform react --json | ||
| ``` | ||
| This produces the full per-builder integration in one shot: | ||
| | What | Where | | ||
| |---|---| | ||
| | Downloads static template ZIP | `https://preview.cometchat.com/downloads/cometchat-builder-react.zip` | | ||
| | Fetches per-builder settings | `GET /vcb/builders/{builderId}` via the same `Bearer` token used elsewhere | | ||
| | Applies F3 + F10 missing-field defaults | `chatFeatures.inAppSounds` + `chatFeatures.deeperUserEngagement.mentionAll` | | ||
| | Unzips template into temp dir | `/tmp/cometchat-builder-export-XXXX/extracted/` | | ||
| | Patches `CometChatSettings.ts` | Per-builder JSON + sentinel comment ("SKILLS-AUTO-GENERATED — do not edit by hand. Last sync: <ISO>") | | ||
| | Copies to `--output` | Default `src/CometChat/` | | ||
| | Reports JSON | `{ status: "exported", builderId, appId, platform, output, settings_file, builder_name }` | | ||
| **For Next.js App Router**, pass `--output src/app/CometChat`. For React Router v7 framework mode, pass `--output app/CometChat`. The CLI's F25 case-collision pre-check warns if a lowercase `src/cometchat/` exists with In-code-shape files (init.ts / CometChatProvider.tsx). | ||
| **For resync** (Step 7 iteration menu → Re-sync visual builder), re-run the SAME command with `--force`. This re-downloads the latest canonical template + re-fetches the latest settings + replaces the `--output` directory entirely. Customer hand-edits inside the `CometChat/` directory are lost — matches the "SKILLS-AUTO-GENERATED" contract on the sentinel. | ||
| ### 11.2 Files patched (after export) | ||
| The `builder export` command writes the canonical files. Skills then patches the customer's existing project to wire it in: | ||
| | Path | Patch | | ||
| |---|---| | ||
| | `package.json` | (1) `npm install @cometchat/chat-uikit-react@6.4.3 @cometchat/calls-sdk-javascript@4.2.5` — **pinned versions from the canonical repo's README**. Older/newer versions of `chat-uikit-react` may drift from the exported `src/CometChat/` directory's expected API surface. (2) **Add a top-level `cometChatCustomConfig` block** — the canonical context reads it via `import packageJson from "../../../package.json"` and accesses `packageJson.cometChatCustomConfig.name` / `.version` / `.production` for init wiring. Without it, the build fails with `TS2339: Property 'cometChatCustomConfig' does not exist`. Shape: `"cometChatCustomConfig": { "name": "<your-app-name>", "version": "<your-app-version>", "production": true }`. | | ||
| | Entry file — `src/main.tsx` (Vite) / `src/index.tsx` (CRA) / new client component (Next.js) / route file (React Router) / `.astro` page (Astro) | Init UI Kit + render `<CometChatProvider><App /></CometChatProvider>`. Pattern below — varies by framework. | | ||
| | `tsconfig.app.json` (Vite 7+) or `tsconfig.json` (CRA / older Vite) | **Multiple non-negotiable adjustments** beyond `resolveJsonModule` + `jsx`. The canonical `src/CometChat/` was authored under CRA's looser TS settings; Vite 7+ template defaults are too strict and will fail the build with dozens of `TS6133` / `TS1484` errors:<br>• `"resolveJsonModule": true` — non-negotiable (`utils/utils.ts` imports a JSON locale)<br>• `"jsx": "react-jsx"` — non-negotiable<br>• `"verbatimModuleSyntax": false` — Vite 7+ default is `true`; canonical code uses mixed value + type imports without the `type` modifier<br>• `"noUnusedLocals": false` — Vite 7+ default is `true`; canonical code has many unused-by-default destructured listener args (e.g. `({ groupOwner, kickedUser, ... })`)<br>• `"noUnusedParameters": false` — same rationale<br>• `"erasableSyntaxOnly": false` — Vite 7+ template flag; canonical code uses const enums / namespace patterns<br>• `"allowJs": true` — canonical app's tsconfig sets this; some kit internals may rely on JS fallthrough<br>Validated 2026-05-21 against `create-vite@8` + canonical `uikit-builder-app-master` + `@cometchat/chat-uikit-react@6.4.3`. | | ||
| | `.env` (framework-prefixed) | Already written by Step 2c provision. Skip if present; warn if missing. | | ||
| The `builder export` command handles the JSON patching + sentinel comment automatically. Skills only needs to patch the four files above (package.json, entry file, tsconfig, .env). | ||
| ### 11.3 Entry-file init pattern (Vite + React) | ||
| ```tsx | ||
| // src/main.tsx | ||
| import { createRoot } from "react-dom/client"; | ||
| import "./index.css"; | ||
| import App from "./App.tsx"; | ||
| import { | ||
| UIKitSettingsBuilder, | ||
| CometChatUIKit, | ||
| } from "@cometchat/chat-uikit-react"; | ||
| import { CometChat } from "@cometchat/chat-sdk-javascript"; | ||
| import { setupLocalization } from "./CometChat/utils/utils.ts"; | ||
| import { CometChatProvider } from "./CometChat/context/CometChatContext.tsx"; | ||
| export const COMETCHAT_CONSTANTS = { | ||
| APP_ID: import.meta.env.VITE_COMETCHAT_APP_ID!, | ||
| REGION: import.meta.env.VITE_COMETCHAT_REGION!, | ||
| AUTH_KEY: import.meta.env.VITE_COMETCHAT_AUTH_KEY!, | ||
| }; | ||
| const uiKitSettings = new UIKitSettingsBuilder() | ||
| .setAppId(COMETCHAT_CONSTANTS.APP_ID) | ||
| .setRegion(COMETCHAT_CONSTANTS.REGION) | ||
| .setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY) | ||
| .subscribePresenceForAllUsers() | ||
| .build(); | ||
| CometChatUIKit.init(uiKitSettings)?.then(() => { | ||
| setupLocalization(); | ||
| createRoot(document.getElementById("root")!).render( | ||
| <CometChatProvider> | ||
| <App /> | ||
| </CometChatProvider> | ||
| ); | ||
| }); | ||
| ``` | ||
| Then in `src/App.tsx`: | ||
| ```tsx | ||
| import CometChatApp from "./CometChat/CometChatApp"; | ||
| export default function App() { | ||
| return ( | ||
| // CometChatApp requires an explicit width and height to render. Adjust as needed | ||
| // for your Step 3c placement (full route, drawer, modal, embedded panel). | ||
| <div style={{ width: "100vw", height: "100dvh" }}> | ||
| <CometChatApp /> | ||
| </div> | ||
| ); | ||
| } | ||
| ``` | ||
| **Critical:** | ||
| - `CometChatProvider` is the **repo's own context** from `./CometChat/context/CometChatContext`, NOT the kit's `CometChatUIKit` export. It manages the builder's `styleFeatures` / `chatFeatures` state and is required for `CometChatHome`, `useThemeStyles`, and the customization toggles to work. | ||
| - `setupLocalization()` from `./CometChat/utils/utils` is required before render — it wires the builder's i18n catalog into the kit. Skipping it leaves UI strings empty. | ||
| - `CometChatUIKit.init(...)` returns a Promise — render only AFTER it resolves. Rendering before init resolves causes `CometChatHome` to throw on first listener attach. | ||
| - Login is handled by `CometChatApp` itself (the canonical component uses `CometChat.addLoginListener` + `CometChatUIKit.getLoggedinUser`). For dev mode, the customer's `App.tsx` should call `CometChatUIKit.login("cometchat-uid-1")` after init resolves but BEFORE rendering — see §2's login order. The canonical app shows a `LoginPlaceholder` until a user is present. | ||
| ### 11.4 Per-framework variants | ||
| | Framework | Where to put `CometChat/` | Entry-file pattern | SSR notes | | ||
| |---|---|---|---| | ||
| | **Vite + React** | `src/CometChat/` | `src/main.tsx` (above) | N/A | | ||
| | **Create React App** | `src/CometChat/` | `src/index.tsx` — same as Vite but use `ReactDOM.createRoot` from `react-dom/client` | N/A | | ||
| | **Next.js App Router** | `src/app/CometChat/` | Create `src/app/CometChatNoSSR/CometChatNoSSR.tsx` (client component) that does init + login + renders `<CometChatProvider><CometChatApp /></CometChatProvider>`. Then create `src/app/CometChatAppWrapper.tsx` with `"use client"` + `dynamic(() => import("../app/CometChatNoSSR/CometChatNoSSR"), { ssr: false })`. Import the wrapper in `src/app/page.tsx`. | The canonical `src/CometChat/` uses `window` / `document` / WebSocket APIs at module scope. `{ ssr: false }` on the wrapper is **non-negotiable** — direct import from a server component causes hydration errors. Use `process.env.NEXT_PUBLIC_COMETCHAT_*` instead of `import.meta.env.*`. | | ||
| | **Next.js Pages Router** | `src/CometChat/` | `pages/chat.tsx` — `const CometChatApp = dynamic(() => import("../src/CometChat/CometChatApp"), { ssr: false });` Init in `pages/_app.tsx` inside `useEffect`. | Same SSR rationale as App Router. | | ||
| | **React Router v7** | `app/CometChat/` (framework mode) or `src/CometChat/` (data mode) | Framework mode: use a `.client.tsx` suffix or `<ClientOnly>` from `remix-utils/client-only`. Data mode: same as Vite. | Framework mode SSRs by default — `.client.tsx` suffix OR `<ClientOnly>` is the only safe pattern. | | ||
| | **Astro** | `src/CometChat/` | `<CometChatApp client:only="react" />` inside an `.astro` page. Init runs in a sibling `.tsx` component that mounts before `CometChatApp`. | `client:only="react"` — never `client:load` (Astro will still SSR the import resolution and crash). | | ||
| ### 11.5 Calls + builder | ||
| If `CometChatSettings.callFeatures` has any `true` value (`oneOnOneVoiceCalling`, `oneOnOneVideoCalling`, `groupVideoConference`, `groupVoiceConference`): | ||
| 1. The canonical `src/CometChat/` already wires `CometChatIncomingCall` inside `CometChatHome` — no extra mount required. | ||
| 2. Skills patches `package.json` to add `@cometchat/calls-sdk-javascript@4.2.5` (already in the canonical install command above) and the Cloudsmith-hosted `@cometchat/calls-lib-webrtc` per `cometchat-react-calls`. | ||
| 3. Calls SDK init runs alongside UI Kit init — pattern in `cometchat-react-calls § 2`. | ||
| Invoke `cometchat-react-calls` after this section with `{ mode: "additive" }` so it adds Calls SDK init + lib-webrtc without duplicating the kit-level wiring already present in the copied `src/CometChat/`. | ||
| ### 11.6 Resync flow | ||
| The "Re-sync visual builder" iteration menu option (see `cometchat/SKILL.md § Step 7`) is a **one-command re-run**: | ||
| ```bash | ||
| cometchat builder export --platform react --force | ||
| ``` | ||
| The `--force` flag is mandatory: it explicitly authorizes replacing the existing `src/CometChat/` directory. Without it, the CLI bails with *"--output directory \`src/CometChat\` already exists. Pass --force to replace it (full re-download per the resync flow), or pick a different --output path."* | ||
| This matches the product contract for step 7 of the UI Kit Builder workflow: | ||
| 1. Re-download the canonical static template ZIP (in case vendor has shipped fixes) | ||
| 2. Re-fetch the customer's current settings JSON (in case they tweaked in browser) | ||
| 3. Apply the F3 + F10 missing-field defaults | ||
| 4. Replace the `src/CometChat/` directory entirely | ||
| **Customer hand-edits inside `src/CometChat/` are lost on resync.** This is intentional — the SKILLS-AUTO-GENERATED sentinel comment on `CometChatSettings.ts` documents the "do not edit by hand" contract. | ||
| If a customer needs to override beyond what the Visual Builder exposes, the supported escape hatches are: | ||
| - Edit the entry file (e.g., `src/main.tsx`) — outside `src/CometChat/`, never touched by resync | ||
| - Edit `src/App.tsx` to wrap `<CometChatApp />` with additional providers / styling | ||
| - Use `cometchat apply-feature <id>` for extension toggles (server-side, survives resync) | ||
| - For one-off CSS overrides, edit `src/index.css` or equivalent — also outside `src/CometChat/` | ||
| The `cometchat-core` §11.7 "Override hook pattern" documents the recommended places to override without touching the canonical. | ||
| `verify --builder` runs after resync to confirm the new export is structurally sound. | ||
| ### 11.7 What this section does NOT emit | ||
| The canonical `src/CometChat/` honors every Builder setting it supports — theme colors, typography, dark/light, sidebar toggle, layout tabs, `chatFeatures.*`, `callFeatures.*`, `agent.*` (per the repo's `CometChatHome` + `styleConfig.ts`). The only setting that isn't auto-applied is `noCode.docked` (the floating-widget shape) — that's a runtime DOM injection that requires the customer to mount `<CometChatApp />` inside a docked overlay container. Surface this in the post-emit summary: | ||
| > Builder settings honored: theme, typography, layout/tabs, sidebar, chat features (mentions/reactions/threads/media/etc.), call features, agent UI. | ||
| > Builder settings deferred: `noCode.docked` floating-widget mode — requires manual mount inside a positioned overlay; see `cometchat-placement § Floating widget`. |
@@ -222,1 +222,18 @@ --- | ||
| - [ ] Imports: `package:cometchat_chat_uikit/cometchat_chat_uikit.dart` always; ADD `package:cometchat_calls_uikit/cometchat_calls_uikit.dart` if you use voice/video | ||
| ## Visual Builder integration | ||
| **Flutter V5 is the primary home for Visual Builder integration.** The canonical repo at the `chat_builder/` directory inside the Flutter Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-flutter.zip) ships **V5-shaped code** — `cometchat_chat_uikit: ^5.2.12` + `cometchat_calls_uikit: ^5.0.13`. The integration copies the entire `chat_builder/` directory as a `path:` dependency, then `BuilderSettingsHelper.loadFromAsset()` reads `chat_builder/assets/sample_app/cometchat-builder-settings.json` and configures the bundled chat UI accordingly. | ||
| **The full recipe lives in `cometchat-flutter-v6-core` §"Visual Builder integration"** because that's where the V6-prep restructure originally landed the validated content. Both skills reference the same canonical; the V6 page carries a "V5-shaped code" warning at the top. V5 customers should follow that recipe AS-IS — the canonical IS V5-targeted. | ||
| Validated 2026-05-21 against Flutter 3.38.3: `flutter build apk --debug` produces `app-debug.apk` after applying: | ||
| - Envelope-wrapped JSON at `chat_builder/assets/sample_app/cometchat-builder-settings.json` (`{ builderId, name, settings: {...} }`) | ||
| - Two missing-field defaults injected pre-write (mentionAll + inAppSounds — same as Android) | ||
| - `android.enableJetifier=true` in `android/gradle.properties` (the chat SDK pulls `com.android.support` transitively) | ||
| - `await BuilderSettingsHelper.loadFromAsset()` in `lib/main.dart` before `runApp()` | ||
| - `chat_builder: { path: ./chat_builder }` in host `pubspec.yaml` | ||
| Differences from the V6 page's recipe text: | ||
| - V5 host code uses `StatefulWidget` with direct listener management (V6 uses BLoC pattern); both work with the embedded chat_builder package since it owns its own state. | ||
| - V5 calls work via the standard `cometchat-flutter-v5-calls` flow — no [[project_v6_flutter_calls_partial]] navigatorKey workaround needed (that's a V6-beta-specific issue). |
| # Adding calls to an existing chat integration (Flutter V6 / Bloc) | ||
| V6 calls are **bundled** into `cometchat_chat_uikit: ^6.0.0-beta2` — no separate calls package. Toggle on by enabling calling in the UIKit settings. | ||
| V6 calls are **bundled** into `cometchat_chat_uikit: ^6.0` — no separate calls package. Toggle on by enabling calling in the UIKit settings. | ||
@@ -14,3 +14,3 @@ **Read first:** `cometchat-flutter-v6-calls/SKILL.md` — V6 architecture (Bloc). | ||
| dependencies: | ||
| cometchat_chat_uikit: ^6.0.0-beta2 # V6 — calls bundled | ||
| cometchat_chat_uikit: ^6.0 # V6 — calls bundled | ||
| ``` | ||
@@ -122,3 +122,3 @@ | ||
| - [ ] `cometchat_chat_uikit: ^6.0.0-beta2` (or higher V6) in pubspec | ||
| - [ ] `cometchat_chat_uikit: ^6.0` (or higher V6) in pubspec | ||
| - [ ] `..enableCalling()` chained in UIKitSettingsBuilder | ||
@@ -125,0 +125,0 @@ - [ ] Native config (iOS + Android) same as V5 |
| --- | ||
| name: cometchat-flutter-v6-calls | ||
| description: CometChat Calls integration for Flutter UIKit v6 (Bloc-based, beta). ⚠️ Partially working at v6.0.0-beta2 (2026-05-13) — chat + calls init OK, outgoing call API succeeds, but TWO vendor bugs block end-to-end calling: (1) MaterialApp MUST set `navigatorKey: CallNavigationContext.navigatorKey` or the outgoing-call screen never renders ("Context is not mounted for navigation" — undocumented hard requirement); (2) even with the navigatorKey wired, the outgoing-call screen does NOT transition to the in-call view when peer accepts — call goes to `ongoing` at server level but UI stays on "Calling…" indefinitely, no client-side workaround. Customers shipping calls on Flutter today must stay on V5 cohort (cometchat_calls_uikit ^5.0.+). Covers UIKitSettings calling block, CometChatUIKitCalls.init() after CometChatUIKit.init() (CALLS_INIT_AFTER_CHAT_INIT), the kit's Bloc-driven CometChatCallButtons / CometChatIncomingCall / CometChatOutgoingCall / CometChatOngoingCall / CometChatCallLogs / CometChatCallBubble widgets, CallingConfiguration, native_call_kit module (iOS CallKit + Android ConnectionService), CallOperationsServiceLocator lifecycle, FCM + PushKit VoIP push, and additive-vs-standalone modes. | ||
| description: "CometChat Calls integration for Flutter UIKit v6 (Bloc-based, stable). Production-ready at v6.0.1 GA (validated 2026-05-27 on Pixel 3) with ONE explicit MaterialApp wiring requirement still active from v6.0.0-beta2 — MaterialApp MUST wire navigatorKey to CallNavigationContext.navigatorKey or the outgoing-call screen never renders. (Vendor's own 6.0.1 sample is missing this line and broken out-of-the-box — file a vendor ticket if you find that sample first.) The earlier outgoing-to-in-call BLoC transition bug from beta2 is FIXED in 6.0.1 GA per the v6.0.0 changelog's 'Refreshed BLoC implementations across ongoing call flows' entry. Customers can now use V6 for calls. Covers UIKitSettings calling block, CometChatUIKitCalls.init() after CometChatUIKit.init() (CALLS_INIT_AFTER_CHAT_INIT), the kit's Bloc-driven CometChatCallButtons / CometChatIncomingCall / CometChatOutgoingCall / CometChatOngoingCall / CometChatCallLogs / CometChatCallBubble widgets, CallingConfiguration, native_call_kit module (iOS CallKit + Android ConnectionService), CallOperationsServiceLocator lifecycle, FCM + PushKit VoIP push, and additive-vs-standalone modes." | ||
| license: "MIT" | ||
| compatibility: "Flutter >= 2.5, Dart >= 3.0; cometchat_chat_uikit ^6.0.0-beta2 (calls bundled in); minSdk 26+ on Android; iOS 13+" | ||
| compatibility: "Flutter >= 2.5, Dart >= 3.0; cometchat_chat_uikit ^6.0.1 (calls bundled in); minSdk 26+ on Android; iOS 13+" | ||
| allowed-tools: "shell, file-read, file-search, file-list, ask-user" | ||
@@ -15,3 +15,3 @@ metadata: | ||
| Production-grade voice + video calling for Flutter UIKit v6 (beta, Bloc-based). Loaded by `cometchat-calls` when `framework === "flutter"` and `flutter_version === "v6"`. Operates in two modes: | ||
| Production-grade voice + video calling for Flutter UIKit v6 (stable, Bloc-based). Loaded by `cometchat-calls` when `framework === "flutter"` and `flutter_version === "v6"`. Operates in two modes: | ||
@@ -33,3 +33,3 @@ - **Standalone** — calls is the product. Chat SDK + Calls SDK without the v6 UI Kit (rare today, since the UI Kit ships calling bundled). Custom call screens on the SDKs. | ||
| **Ground truth:** | ||
| - SDK source — installed `cometchat_chat_uikit@6.0.0-beta2` artifacts under `~/.pub-cache/` | ||
| - SDK source — installed `cometchat_chat_uikit@6.0.1` artifacts under `~/.pub-cache/` | ||
| - Sample app — `~/Downloads/calls-sdk/calls-sdk-flutter-5/sample-apps/` (V5 sample; V6 sample app may not exist yet — verify before citing) | ||
@@ -172,7 +172,7 @@ - Public docs — https://www.cometchat.com/docs/calls/flutter/overview (note: V6 docs may still reference V5 module split) | ||
| **⚠️ `navigatorKey: CallNavigationContext.navigatorKey` is REQUIRED on MaterialApp at v6.0.0-beta2.** The kit's `CometChatCallButtons` and outgoing-call flow navigate via `CallNavigationContext.navigatorKey.currentContext`. Without this line, `CometChat.initiateCall` succeeds but the outgoing-call screen never renders — kit logs `"Context is not mounted for navigation"` and silently drops the navigation. Symptom: user taps call button, peer rings, but the Flutter app shows nothing. | ||
| **⚠️ `navigatorKey: CallNavigationContext.navigatorKey` is REQUIRED on MaterialApp — still active in v6.0.1 GA.** The kit's `CometChatCallButtons` and outgoing-call flow navigate via `CallNavigationContext.navigatorKey.currentContext`. Without this line, `CometChat.initiateCall` succeeds (CALL-TRAP confirms `onSuccess` fires with a valid sessionId) but `currentContext` is null so `CometChatOutgoingCall` never mounts. Symptom: user taps call button, peer rings, but the Flutter app shows nothing. **Note: the vendor's own 6.0.1 sample app (`examples/sample_app`) is missing this line and is broken out-of-the-box** — its `user_info_screen.dart` reads `CallNavigationContext.navigatorKey.currentContext` but `main.dart`'s MaterialApp never sets the key. Don't copy the vendor sample's `main.dart` verbatim; add the navigatorKey. | ||
| Import: `import 'package:cometchat_chat_uikit/cometchat_calls_uikit.dart' show CallNavigationContext;` (use `show` to avoid a name collision with kit-exported `IncomingCallOverlay`). | ||
| **⚠️ Outgoing → in-call screen transition is broken at v6.0.0-beta2.** When the peer accepts, the call's status moves to `ongoing` at the server level — but the kit's outgoing-call screen does NOT transition to the in-call view. Users see the "Calling…" ring spinner indefinitely while the call is actually live. Pressing the reject button fires `CometChat.rejectCall` which the server rejects with `"The call status cannot be updated from ongoing to cancelled"`. **No client-side workaround known** — vendor needs to fix the outgoing-call BLoC's `onOutgoingCallAccepted` handler to push the in-call route. Validated on 2026-05-13 against a Next.js web peer. | ||
| **✅ Outgoing → in-call screen transition is FIXED in v6.0.1 GA.** (It was broken in v6.0.0-beta2 — the outgoing-call screen stayed on "Calling…" indefinitely after the peer accepted.) Validated end-to-end 2026-05-27 on Pixel 3 with full CALL-TRAP instrumentation: with the navigatorKey wired (above), when the peer accepts, the kit's `OutgoingCallBloc` fires `pushReplacement` and the screen transitions automatically from `CometChatOutgoingCall` ("Calling…") to the in-call surface — observed call-duration timer ticking + WebRTC rendering frames @ ~27 fps. The v6.0.0 changelog's "Refreshed BLoC implementations across … call buttons, and ongoing call flows" was the fix. No client-side workaround needed beyond the navigatorKey wiring. | ||
@@ -190,3 +190,3 @@ In standalone mode, `native_call_kit` owns the OS-level ring UI; the in-app overlay only fires when the app is foregrounded. | ||
| sdk: flutter | ||
| cometchat_chat_uikit: ^6.0.0-beta2 # calls bundled in | ||
| cometchat_chat_uikit: ^6.0 # calls bundled in | ||
| permission_handler: ^11.0.0 | ||
@@ -202,3 +202,3 @@ flutter_callkit_incoming: ^2.0.0 # standalone — VoIP UI bridge | ||
| hosted: https://dart.cloudsmith.io/cometchat/cometchat/ | ||
| version: ^6.0.0-beta2 | ||
| version: ^6.0.1 | ||
| ``` | ||
@@ -345,3 +345,3 @@ | ||
| 1. Confirms `cometchat_chat_uikit` is on `^6.0.0-beta2` — calls are already bundled. | ||
| 1. Confirms `cometchat_chat_uikit` is on `^6.0.1` — calls are already bundled. | ||
| 2. Patches the `CometChatUIKit.init` call to add `CometChatUIKitCalls.init` in the success callback (rule 1.1). | ||
@@ -368,3 +368,3 @@ 3. Adds `CometChatDisplayIncomingCallOverlay` to `MaterialApp.builder` (rule 1.7). | ||
| - [ ] `cometchat_chat_uikit ^6.0.0-beta2` in pubspec.yaml (V6 bundles calls — no separate calls package) | ||
| - [ ] `cometchat_chat_uikit ^6.0.1` in pubspec.yaml (V6 bundles calls — no separate calls package) | ||
| - [ ] `CometChatUIKitCalls.init` called inside `CometChatUIKit.init`'s `onSuccess` (rule 1.1) | ||
@@ -371,0 +371,0 @@ - [ ] `CometChatUIKitCalls.init` called exactly once per app lifecycle |
@@ -305,2 +305,179 @@ --- | ||
| - [ ] Colors from `CometChatThemeHelper`, never hardcoded | ||
| - [ ] Strings from `Translations.of(context)`, never hardcoded | ||
| ## Visual Builder integration | ||
| > **⚠ The Visual Builder emits V5-shaped code.** The canonical builder repo at the `chat_builder/` directory inside the Flutter Visual Builder ZIP (download from https://preview.cometchat.com/downloads/cometchat-builder-flutter.zip) uses `cometchat_chat_uikit: ^5.2.12` + `cometchat_calls_uikit: ^5.0.13` — the **V5** Flutter UI Kit packages. There is no V6-native Visual Builder canonical from vendor side yet ([F22 finding, 2026-05-22](https://github.com/cometchat/cometchat-skills/issues)). When a V6 project picks the Visually path, the copied `chat_builder/` path-dep brings V5 deps in transitively — your V6 host app code is untouched, but the Visual-Builder-emitted screens are V5-flavored. Treat as transitional until vendor publishes a V6 canonical. V6 customers preferring a single-version dep tree should use the In-code path. | ||
| > | ||
| > The recipe below is identical to what `cometchat-flutter-v5-core` §"Visual Builder integration" prescribes (both reference the same canonical). This page kept for V6 customers who still hit the Visually flow. | ||
| When the dispatcher's Step 3.1 sets `customize=visual` and the platform resolves to `flutter`, skills runs **`cometchat builder export --platform flutter`** — a single CLI command that downloads the canonical static template ZIP from `preview.cometchat.com/downloads/cometchat-builder-flutter.zip`, fetches the per-builder settings JSON, applies F3 + F10 missing-field defaults, and writes the entire `chat_builder/` package to `--output` (default: `chat_builder/`). | ||
| The `chat_builder/` directory is wired **as a path dependency** in the customer's project — it owns the entire chat surface (conversations, messages, users, groups, calls). The customer's app initializes settings and launches a screen via `ChatBuilder.launchBuilder(context)` or `ChatBuilder.launchMessages(...)`. | ||
| This is intentionally a heavier copy than iOS (Pod) / Android (Gradle plugin) — the Flutter builder package is not yet published to pub.dev. | ||
| ### 1. Run `cometchat builder export` | ||
| ```bash | ||
| cometchat builder export --platform flutter --json | ||
| ``` | ||
| Defaults to `--output chat_builder/`. The command writes the entire canonical `chat_builder/` Dart package (~50 files: `lib/`, `assets/`, `android/`, `ios/`, `pubspec.yaml`) — verbatim from the canonical ZIP — and patches `chat_builder/assets/sample_app/cometchat-builder-settings.json` with the **envelope-shape JSON** `{ builderId, name, settings: {...} }` (F3 + F10 defaults injected: `mentionAll: true`, `inAppSounds: { incomingMessageSound: true, outgoingMessageSound: true }`). No SKILLS-AUTO-GENERATED sentinel (JSON forbids `//` comments). | ||
| `BuilderSettingsHelper.loadFromAsset()` reads this exported settings file at runtime. | ||
| Resync = re-run the same command with `--force` (full re-download + replace). See `cometchat-core` §11.6 for the resync contract. | ||
| #### F56 + F57 + F58 — known canonical-ZIP rough edges (re-apply after every export) | ||
| Three issues live in the upstream canonical `chat_builder/` Dart package (verified 2026-05-25 against `preview.cometchat.com/downloads/cometchat-builder-flutter.zip`). Vendor-side fixes pending; agent must work around them on **every** `builder export` (first emit AND every resync, since `--force` re-overwrites the patches): | ||
| **F56 — Wrong asset path in `api_services.dart`** | ||
| `chat_builder/lib/services/api_services.dart` line ~41: | ||
| ```dart | ||
| // Canonical (broken): | ||
| await loadJsonFromAssets('assets/chat_builder/sample_data.json'); | ||
| // Agent must patch to: | ||
| await loadJsonFromAssets('assets/sample_app/sample_data.json'); | ||
| ``` | ||
| The canonical references `assets/chat_builder/sample_data.json` — a path that doesn't exist in the customer's host app after the standard `cp -r chat_builder/assets/ assets/` copy. The actual file lands at `assets/sample_app/sample_data.json`. Without this patch, the login screen's "default users" list silently falls back to empty and customers see an empty user picker. | ||
| **F57 — F56 patch is overwritten on every resync** | ||
| `builder export --platform flutter --force` re-copies the canonical's `api_services.dart` verbatim — re-introducing the wrong path. Agent must re-apply the F56 patch after every resync. This is a known re-apply task; document it in the customer-facing summary so they know "the agent will need to patch this each time the dashboard settings change". | ||
| **F58 — Asset copy is additive, not idempotent** | ||
| The standard prescription `cp -r chat_builder/assets/ assets/` is additive — files removed from the canonical (e.g., a deprecated icon) stay in the customer's `assets/` directory because cp doesn't delete. On resync, use `rsync -a --delete chat_builder/assets/ assets/` instead, OR `rm -rf assets/ && cp -r chat_builder/assets/ assets/` first. | ||
| **Tracking**: a single vendor ticket bundles F22 (V5-shaped canonical), F56 (wrong asset path), F57 (resync overwrite of F56 fix), F48 lineage (font paths in earlier Android-ZIP iterations) — see CometChat Linear for the consolidated upstream-fix request. v4.3.0 ships with the agent-side workarounds documented above; a future release picks up the upstream fixes when they land. | ||
| ### Files skills writes (after `builder export`) | ||
| | Path | Content | | ||
| |---|---| | ||
| | `lib/cometchat/cometchat_app.dart` | Thin `StatefulWidget` that initializes the kit then exposes a launch trigger calling `ChatBuilder.launchBuilder(context)` | | ||
| | `lib/cometchat/secrets.dart` | Credentials class (`Secrets.appId / region / authKey`) populated by Step 2c, added to `.gitignore` | | ||
| ### Files patched | ||
| | Path | Patch | | ||
| |---|---| | ||
| | `pubspec.yaml` | Add `chat_builder: { path: ./chat_builder }` to dependencies. Add `assets/` + `assets/sample_app/` to `flutter.assets`. Add font families `arial / inter / roboto / times New Roman` exactly as defined in `chat_builder/pubspec.yaml` | | ||
| | `lib/main.dart` | Insert `WidgetsFlutterBinding.ensureInitialized()` + `await BuilderSettingsHelper.loadFromAsset()` before `runApp()` | | ||
| | `ios/Podfile` | `platform :ios, '13.0'` (raise if lower) | | ||
| | `android/app/build.gradle.kts` | `ndkVersion = "27.0.12077973"`, `minSdk = 24` (raise if lower) | | ||
| | `android/gradle.properties` | **Non-negotiable:** append `android.enableJetifier=true`. CometChat Chat SDK transitively pulls `com.android.support:support-compat:26.1.0` which collides with `androidx.core:core` — without Jetifier the build fails with `Duplicate class android.support.v4.os.ResultReceiver` etc. Same root cause as `cometchat-android-v6-core` §1.3a — Flutter's Android side has identical exposure. Validated 2026-05-19 on smoke test | | ||
| | `android/app/src/main/AndroidManifest.xml` + `ios/Runner/Info.plist` | `RECORD_AUDIO` / `CAMERA` permissions + `NSMicrophoneUsageDescription` / `NSCameraUsageDescription` if any call feature is enabled | | ||
| ### Init flow (lib/main.dart) | ||
| ```dart | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:chat_builder/builder/builder_settings_helper.dart'; | ||
| Future<void> main() async { | ||
| WidgetsFlutterBinding.ensureInitialized(); | ||
| await BuilderSettingsHelper.loadFromAsset(); | ||
| runApp(const MyApp()); | ||
| } | ||
| ``` | ||
| `BuilderSettingsHelper.loadFromAsset()` reads `chat_builder/assets/cometchat-builder-settings.json` and populates the in-memory settings the `ChatBuilder.*` screens consume. Standard `CometChatUIKit.init(uiKitSettings: ...)` (see this file's `INIT_FIRST` rule) is still required and runs before any chat surface mounts — the wrapper below handles that. | ||
| ### The wrapper template | ||
| ```dart | ||
| // lib/cometchat/cometchat_app.dart | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:chat_builder/builder/chat_builder.dart'; | ||
| import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart'; | ||
| import 'secrets.dart'; | ||
| /// Top-level chat surface emitted by the Visual Builder Visually path. | ||
| /// The `chat_builder` package owns the UI; this widget bootstraps UI Kit init | ||
| /// then renders a launch trigger. Step 3c placement decides where this widget | ||
| /// mounts in the host app (a route, a tab, a dialog). | ||
| class CometChatApp extends StatefulWidget { | ||
| const CometChatApp({super.key}); | ||
| @override | ||
| State<CometChatApp> createState() => _CometChatAppState(); | ||
| } | ||
| class _CometChatAppState extends State<CometChatApp> { | ||
| bool _isReady = false; | ||
| String? _initError; | ||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| _bootstrap(); | ||
| } | ||
| void _bootstrap() { | ||
| final settings = (UIKitSettingsBuilder() | ||
| ..appId = Secrets.appId | ||
| ..region = Secrets.region | ||
| ..authKey = Secrets.authKey | ||
| ..subscriptionType = CometChatSubscriptionType.allUsers) | ||
| .build(); | ||
| CometChatUIKit.init( | ||
| uiKitSettings: settings, | ||
| onSuccess: (_) => setState(() => _isReady = true), | ||
| onError: (e) => setState(() => _initError = e.message), | ||
| ); | ||
| } | ||
| @override | ||
| Widget build(BuildContext context) { | ||
| if (_initError != null) { | ||
| return Scaffold(body: Center(child: Text('CometChat init failed: $_initError'))); | ||
| } | ||
| if (!_isReady) { | ||
| return const Scaffold(body: Center(child: CircularProgressIndicator())); | ||
| } | ||
| return Scaffold( | ||
| resizeToAvoidBottomInset: false, | ||
| body: Center( | ||
| child: ElevatedButton( | ||
| onPressed: () => ChatBuilder.launchBuilder(context), | ||
| child: const Text('Open chat'), | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
| ``` | ||
| `ChatBuilder.launchBuilder(context)` opens the builder's login / dashboard flow; once a user is logged in, the package's internal navigation handles conversations → messages. | ||
| For direct deep-links into a specific user or group thread (skipping the dashboard), use: | ||
| ```dart | ||
| ChatBuilder.launchMessages(context: context, user: user); | ||
| ChatBuilder.launchMessages(context: context, group: group); | ||
| ``` | ||
| These are the two public entry points the `chat_builder` package exposes — see `chat_builder/lib/builder/chat_builder.dart` (inside the Flutter Visual Builder ZIP at https://preview.cometchat.com/downloads/cometchat-builder-flutter.zip). | ||
| `resizeToAvoidBottomInset: false` is non-negotiable on any `Scaffold` whose body chain reaches `CometChatMessageComposer` (see this file's `SCAFFOLD_NO_RESIZE` rule). The composer handles keyboard spacing internally. | ||
| ### Calls + builder | ||
| If `CometChatBuilderSettings` reports any call feature enabled, the `chat_builder` package handles incoming/outgoing call surfaces internally (it ships its own call routes). External wiring still required: | ||
| 1. Add `cometchat_calls_uikit` to `pubspec.yaml` | ||
| 2. iOS `Info.plist` + Android `AndroidManifest.xml` permissions per `cometchat-flutter-v6-calls` | ||
| 3. Apply the `navigatorKey: CallNavigationContext.navigatorKey` workaround documented in `cometchat-flutter-v6-calls` §1.7 (kit 6.0.0-beta2 requires this for incoming-call routing) | ||
| 4. Push wiring — defer to `cometchat-flutter-v6-push` (audit-verified namespace `PNRegistry`, NOT `CometChatNotifications` — [[project_sdk_symbol_audit_2026_05_14]]) | ||
| 5. **Vendor blocker:** outgoing→in-call transition is broken on `cometchat_chat_uikit 6.0.0-beta2` ([[project_v6_flutter_calls_partial]]); Flutter V5 cohort is production-recommended until vendor fix lands | ||
| ### What is NOT honored in v1 | ||
| Skills emits a launch-button trigger, not the full multi-tab layout the `chat_builder` package's internal `Dashboard` provides. Customers get the dashboard once they tap "Open chat" — but the host-app surface is intentionally minimal so Step 3c placement (route, tab, dialog) can decide the mount shape. Theme color + typography + chat-feature toggles ARE honored via the embedded package. For deep customization beyond what the builder JSON allows, fall back to the code-driven path (see this file's standard placement pattern) and skip `chat_builder` entirely. |
| --- | ||
| name: cometchat-flutter-v6-push | ||
| description: Push notifications for CometChat Flutter UIKit v6 (beta, Bloc-based). Covers firebase_messaging setup for FCM (Android) + APNs (iOS via Firebase), CometChat dashboard PushPlatform configuration, token registration via the Notifications SDK, background isolate handler (Dart entry-point rule), foreground vs background message routing, notification tap deep-link to chat threads, and the Bloc patterns for surfacing push state. Sister skill of cometchat-flutter-v5-push — same FCM stack, Bloc-flavored client integration. | ||
| description: Push notifications for CometChat Flutter UIKit v6 (stable, Bloc-based). Covers firebase_messaging setup for FCM (Android) + APNs (iOS via Firebase), CometChat dashboard PushPlatform configuration, token registration via the Notifications SDK, background isolate handler (Dart entry-point rule), foreground vs background message routing, notification tap deep-link to chat threads, and the Bloc patterns for surfacing push state. Sister skill of cometchat-flutter-v5-push — same FCM stack, Bloc-flavored client integration. | ||
| license: "MIT" | ||
@@ -59,3 +59,3 @@ compatibility: "Flutter >= 2.5, Dart >= 3.0; cometchat_chat_uikit ^6.0.0-beta2; firebase_messaging ^14.0.0; firebase_core ^2.0.0; Android minSdk 26+; iOS 13+ deployment target" | ||
| sdk: flutter | ||
| cometchat_chat_uikit: ^6.0.0-beta2 | ||
| cometchat_chat_uikit: ^6.0 | ||
| firebase_messaging: ^14.0.0 | ||
@@ -62,0 +62,0 @@ firebase_core: ^2.0.0 |
| --- | ||
| name: cometchat-flutter-v6-testing | ||
| description: Testing patterns for CometChat Flutter UIKit v6 (beta, Bloc-based). Covers flutter_test + bloc_test for Bloc unit tests, mocktail for SDK mocking, widget tests around the Bloc-driven CometChat widgets, integration_test for real-device flows, golden tests for theming, and CI on GitHub Actions / Codemagic. Sister skill of cometchat-flutter-v5-testing — the cohorts have different state-management primitives (GetX vs Bloc) so the patterns differ. | ||
| description: Testing patterns for CometChat Flutter UIKit v6 (stable, Bloc-based). Covers flutter_test + bloc_test for Bloc unit tests, mocktail for SDK mocking, widget tests around the Bloc-driven CometChat widgets, integration_test for real-device flows, golden tests for theming, and CI on GitHub Actions / Codemagic. Sister skill of cometchat-flutter-v5-testing — the cohorts have different state-management primitives (GetX vs Bloc) so the patterns differ. | ||
| license: "MIT" | ||
@@ -15,3 +15,3 @@ compatibility: "Flutter >= 2.5, Dart >= 3.0; flutter_test (built-in); bloc_test >= 9.0; mocktail >= 1.0; integration_test (built-in); cometchat_chat_uikit ^6.0.0-beta2" | ||
| Test recipes for Flutter UIKit v6 (beta, Bloc-based). Most of the v5 patterns carry over; the deltas are around state-management primitives (Bloc, not GetX) and the unified `cometchat_chat_uikit` package (calls bundled — see `cometchat-flutter-v6-calls`). | ||
| Test recipes for Flutter UIKit v6 (stable, Bloc-based). Most of the v5 patterns carry over; the deltas are around state-management primitives (Bloc, not GetX) and the unified `cometchat_chat_uikit` package (calls bundled — see `cometchat-flutter-v6-calls`). | ||
@@ -18,0 +18,0 @@ **Read these other skills first:** |
@@ -823,1 +823,143 @@ --- | ||
| The UI Kit automatically detects and enables calling features when the Calls SDK is present. | ||
| ## Visual Builder integration | ||
| When the dispatcher's Step 3.1 sets `customize=visual` and the platform resolves to `ios`, skills runs **`cometchat builder export --platform ios`** — a single CLI command that downloads the canonical static template ZIP from `preview.cometchat.com/downloads/cometchat-builder-ios.zip`, fetches the per-builder settings JSON, applies F3 + F10 missing-field defaults, and writes 3 files to `--output` (default: `CometChat/`): | ||
| - `MessagesVC.swift` — verbatim view controller composing header + list + composer | ||
| - `ThreadedMessagesVC.swift` — verbatim helper VC (imported by MessagesVC) | ||
| - `cometchat-builder-settings.json` — **envelope-shape JSON** `{ builderId, name, settings: {...} }` (no sentinel — JSON forbids `//` comments) | ||
| ### 1. Run `cometchat builder export` | ||
| ```bash | ||
| cometchat builder export --platform ios --json | ||
| ``` | ||
| Defaults to `--output CometChat/`. Adjust the output if the customer's project uses a different chat-surface group name (e.g. `--output Chat/` for projects that use a `Chat` group). | ||
| `CometChatBuilderSettings.loadFromJSON()` looks for the envelope shape — handing it the raw settings blob causes `CometChatBuilderSettings.shared` to fall back to defaults silently (no error logged). The CLI always writes the envelope, so this trap is closed. | ||
| Resync = re-run the same command with `--force`. See `cometchat-core` §11.6 for the resync contract. | ||
| ### 2. Files skills writes (after `builder export`) | ||
| | Path | Content | | ||
| |---|---| | ||
| | `CometChat/CometChatApp.swift` | SwiftUI wrapper that mounts `CometChatConversations` + pushes `MessagesVC` on tap. Feature flags read from `CometChatBuilderSettings.shared`. Skills emits this — not from the ZIP. | | ||
| ### Files patched | ||
| | Path | Patch | | ||
| |---|---| | ||
| | `Podfile` | Add `pod 'CometChatBuilder'` (the canonical pod surfacing `CometChatBuilderSettings.shared` + `loadFromJSON()`). Add `pod 'CometChatUIKitSwift', '~> 5.1'` if not already declared. SPM equivalent: add `https://github.com/cometchat/cometchat-builder-ios` | | ||
| | `AppDelegate.swift` (UIKit) / `App.swift` (SwiftUI) | Add `CometChatBuilderSettings.loadFromJSON()` + theme application + `CometChatUIKit.init(uiKitSettings:)` in `application(_:didFinishLaunchingWithOptions:)` — see init code below | | ||
| | App target | Toggle `cometchat-builder-settings.json` for **target membership** in Xcode (else `loadFromJSON()` silently returns defaults) | | ||
| | Entry view — `ContentView.swift` (SwiftUI) or root `UIViewController` (UIKit) | Mount `CometChatApp()` per Step 3c placement (modal sheet, push-navigation destination, tab item, or embedded view) | | ||
| | `Info.plist` | `NSMicrophoneUsageDescription` + `NSCameraUsageDescription` if `CometChatBuilderSettings.shared.callFeatures` has any enabled feature | | ||
| ### Init code — AppDelegate | ||
| ```swift | ||
| // AppDelegate.swift — inside application(_:didFinishLaunchingWithOptions:) | ||
| import CometChatBuilder | ||
| import CometChatUIKitSwift | ||
| // 1. Load builder settings from bundled JSON | ||
| CometChatBuilderSettings.loadFromJSON() | ||
| // 2. Apply builder theme tokens to the kit's global theme | ||
| CometChatTheme.primaryColor = UIColor.dynamicColor( | ||
| lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.brandColor), | ||
| darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.brandColor) | ||
| ) | ||
| CometChatTheme.textColorPrimary = UIColor.dynamicColor( | ||
| lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.primaryTextLight), | ||
| darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.primaryTextDark) | ||
| ) | ||
| CometChatTheme.textColorSecondary = UIColor.dynamicColor( | ||
| lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.secondaryTextLight), | ||
| darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.secondaryTextDark) | ||
| ) | ||
| CometChatTypography.customFontFamilyName = CometChatBuilderSettings.shared.style.typography.font | ||
| // 3. Standard UI Kit init (this file's §2 — credentials from Secrets.swift) | ||
| CometChatManager.shared.initialize( | ||
| appID: Secrets.appID, authKey: Secrets.authKey, region: Secrets.region | ||
| ) { _, _ in } | ||
| ``` | ||
| `loadFromJSON()` reads `cometchat-builder-settings.json` from the main bundle. If the file isn't a target member (Xcode → file inspector → Target Membership), `CometChatBuilderSettings.shared` silently falls back to defaults — this is the #1 integration bug. Sanity-check by printing `CometChatBuilderSettings.shared.style.color.brandColor` after `loadFromJSON()`. | ||
| ### The wrapper template | ||
| ```swift | ||
| // CometChat/CometChatApp.swift | ||
| import SwiftUI | ||
| import UIKit | ||
| import CometChatBuilder | ||
| import CometChatUIKitSwift | ||
| import CometChatSDK | ||
| /// Top-level chat surface emitted by the Visual Builder Visually path. | ||
| /// Master/detail SwiftUI host: `CometChatConversations` at root, `MessagesVC` pushed on tap. | ||
| /// Feature visibility flags are read from `CometChatBuilderSettings.shared`. | ||
| public struct CometChatApp: View { | ||
| public init() {} | ||
| public var body: some View { | ||
| ConversationsContainer().ignoresSafeArea() | ||
| } | ||
| } | ||
| private struct ConversationsContainer: UIViewControllerRepresentable { | ||
| func makeUIViewController(context: Context) -> UINavigationController { | ||
| let conversationsVC = CometChatConversations() | ||
| let core = CometChatBuilderSettings.shared.chatFeatures.coreMessagingExperience | ||
| conversationsVC.hideUserStatus = !core.userAndFriendsPresence | ||
| conversationsVC.hideReceipts = !core.messageDeliveryAndReadReceipts | ||
| let nav = UINavigationController(rootViewController: conversationsVC) | ||
| conversationsVC.set(onItemClick: { [weak nav] conversation, _ in | ||
| let vc = MessagesVC() | ||
| if let user = conversation.conversationWith as? User { vc.user = user } | ||
| else if let group = conversation.conversationWith as? Group { vc.group = group } | ||
| nav?.pushViewController(vc, animated: true) | ||
| }) | ||
| return nav | ||
| } | ||
| func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {} | ||
| } | ||
| private extension UIColor { | ||
| convenience init?(hex: String) { | ||
| let s = hex.replacingOccurrences(of: "#", with: "") | ||
| guard s.count == 6, let rgb = UInt32(s, radix: 16) else { return nil } | ||
| self.init(red: CGFloat((rgb >> 16) & 0xFF) / 255, | ||
| green: CGFloat((rgb >> 8) & 0xFF) / 255, | ||
| blue: CGFloat(rgb & 0xFF) / 255, | ||
| alpha: 1) | ||
| } | ||
| static func dynamicColor(lightModeColor: UIColor?, darkModeColor: UIColor?) -> UIColor { | ||
| UIColor { traits in | ||
| (traits.userInterfaceStyle == .dark ? darkModeColor : lightModeColor) ?? UIColor.label | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| `MessagesVC.swift` is **copied verbatim** from `CometChatBuilderSwift/BuilderApp/View Controllers/CometChat Components/MessagesVC.swift` (inside the Ios Visual Builder ZIP at https://preview.cometchat.com/downloads/cometchat-builder-ios.zip). It composes header + list + composer in a `UIViewController` and wires reaction / thread / typing per `CometChatBuilderSettings.shared.chatFeatures.*`. Do not hand-roll this — the canonical file is the reference. | ||
| `Secrets.swift` is the credentials enum (`enum Secrets { static let appID = "..."; static let region = "..."; static let authKey = "..." }`) populated by Step 2c provision. Added to `.gitignore`. | ||
| **SwiftUI vs UIKit hosts.** The template above is the SwiftUI host. For pure UIKit apps, skip `UIViewControllerRepresentable` and present `UINavigationController(rootViewController: CometChatConversations())` directly in `SceneDelegate` — see `CometChatBuilderSwift/BuilderApp/SceneDelegate.swift` (inside the Ios Visual Builder ZIP at https://preview.cometchat.com/downloads/cometchat-builder-ios.zip) + `View Controllers/HomeScreenViewController.swift` for the canonical UIKit pattern. | ||
| ### Calls + builder | ||
| If `CometChatBuilderSettings.shared.callFeatures` has any enabled feature: | ||
| 1. Init Calls SDK at the same site as UI Kit (see `cometchat-ios-calls`) | ||
| 2. Mount `CometChatIncomingCall` at app root (SwiftUI App's `WindowGroup` root, or UIKit's `keyWindow.rootViewController` overlay — the builder repo's `BuilderApplication`-equivalent pattern in `SceneDelegate.swift` is the reference) | ||
| 3. PushKit + CallKit wiring — defer to `cometchat-ios-push` | ||
| ### What is NOT honored in v1 | ||
| The builder repo's `HomeScreenViewController` is a `UITabBarController` with up to 4 tabs (Chats / Calls / Users / Groups) driven by `CometChatBuilderSettings.shared.layout.tabs`. Skills' thin wrapper emits a single conversations surface, not the tabbed shape. Theme color + typography + chat-feature toggles ARE honored. To get the tabbed shape, copy `HomeScreenViewController.swift` from the builder repo instead of the wrapper template above. |
@@ -88,5 +88,10 @@ --- | ||
| react-native-callstats \ | ||
| react-native-webrtc | ||
| react-native-webrtc \ | ||
| react-native-url-polyfill \ | ||
| react-native-performance \ | ||
| valibot | ||
| ``` | ||
| > **`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`.) | ||
| WebRTC bloats the binary. Skip until the user actually wants calls. | ||
@@ -260,2 +265,4 @@ | ||
| > **Known issue (F78) — chat-sdk 4.0.22 breaks bare RN at runtime.** Separate from the build-time Maven error above: `@cometchat/chat-sdk-react-native@4.0.22` declares `react-native@0.64.2` + async-storage as **hard deps** (4.0.21 had none), so npm installs a nested duplicate react-native inside the SDK. The app builds fine but crashes at JS startup with `Cannot read property 'CometChatThemeProvider' of undefined` (AsyncStorage native module mismatch). **Expo is unaffected.** Fix: add npm `overrides` to dedupe, or pin `@cometchat/chat-sdk-react-native@4.0.21`. Full root cause + exact `overrides` block in `cometchat-native-troubleshooting` §3bc. Tracked in ENG-35653. | ||
| ### 3c. Android: Metro config for custom fonts or assets (if applicable) | ||
@@ -515,1 +522,15 @@ | ||
| | `cometchat-native-troubleshooting` | pod install fails, build errors, missing modules, privacy manifest rejection | | ||
| ## Visual Builder integration (v4.3) | ||
| If the customer picks **Visually** in dispatcher Step 3.1, the bare RN 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. | ||
| **Full recipe lives in `cometchat-native-core` §"Visual Builder integration".** Bare RN-specific notes: | ||
| - No `npx expo install` — install deps via `npm install` and pin manually. The 11 explicit peer-dep list applies in full (see §"Mandatory peer deps"). | ||
| - Env via `react-native-dotenv` Babel plugin — the standard bare-RN convention. Add `[['module:react-native-dotenv']]` to `babel.config.js` plugins. Import via `import { COMETCHAT_APP_ID, COMETCHAT_REGION, COMETCHAT_AUTH_KEY } from "@env"`. Add a `src/env.d.ts` type declaration so TS recognizes the `@env` module. | ||
| - Required additional deps: `zustand`, `@react-native-async-storage/async-storage`, plus the standard 11 peers from §"Mandatory peer deps". | ||
| - `useConfig(s => s.settings.style)` — note the selector takes `AppConfig` directly, NOT `s.config.settings.style` (Finding F6, 2026-05-21). | ||
| - iOS host: run `cd ios && pod install` after the deps land. B6 smoke validated: Metro bundles 4.6 MB iOS bundle clean on RN 0.81.5 + React 19.1.0. | ||
| If the customer picks **In code**, ignore this section — the standard four-wrapper chain + provider pattern applies. |
@@ -136,6 +136,64 @@ --- | ||
| ### 1.7 IncomingCall mounted at app root | ||
| ### 1.7 IncomingCall + OutgoingCall + OngoingCall — full event-listener wiring | ||
| `<CometChatIncomingCall />` (additive mode) goes inside the root navigator OR in the App.tsx wrapper, ABOVE all stacks/tabs. Same rule as web — calls only ring on screens where the listener exists. | ||
| `<CometChatIncomingCall />`, `<CometChatOutgoingCall />`, and `<CometChatOngoingCall />` are **NOT auto-mounted by each other**. The parent component must register both `CometChat.addCallListener` (SDK socket) and `CometChatUIEventHandler.addCallListener` (UI events fired by `<CometChatCallButtons>` / `<CometChatMessageHeader>`), then conditionally render whichever overlay matches current state. Validated 2026-05-26 on Pixel 3 + `@cometchat/chat-uikit-react-native@5.3.5`. | ||
| Mount this wiring inside the root navigator OR in the App.tsx wrapper, ABOVE all stacks/tabs — calls only ring on screens where the listener exists. | ||
| ```tsx | ||
| import { CometChat } from "@cometchat/chat-sdk-react-native"; | ||
| import { | ||
| CometChatIncomingCall, | ||
| CometChatOutgoingCall, | ||
| CometChatOngoingCall, | ||
| CometChatUIEventHandler, | ||
| } from "@cometchat/chat-uikit-react-native"; | ||
| import { StyleSheet, View } from "react-native"; | ||
| const CALL_LISTENER_ID = "app-call-listener"; | ||
| function CallSurfaces() { | ||
| const [outgoingCall, setOutgoingCall] = useState<CometChat.Call | null>(null); | ||
| const [incomingCall, setIncomingCall] = useState<CometChat.Call | null>(null); | ||
| const [ongoingCall, setOngoingCall] = useState<CometChat.Call | null>(null); | ||
| useEffect(() => { | ||
| // SDK socket — incoming + outgoing-rejected | ||
| CometChat.addCallListener( | ||
| CALL_LISTENER_ID, | ||
| new CometChat.CallListener({ | ||
| onIncomingCallReceived: (call: CometChat.Call) => setIncomingCall(call), | ||
| onIncomingCallCancelled: () => setIncomingCall(null), | ||
| onOutgoingCallAccepted: () => {}, | ||
| onOutgoingCallRejected: () => setOutgoingCall(null), | ||
| }) | ||
| ); | ||
| // UI events fired by the kit's CallButtons / MessageHeader on tap | ||
| CometChatUIEventHandler.addCallListener(CALL_LISTENER_ID, { | ||
| ccOutgoingCall: ({ call }) => setOutgoingCall(call), | ||
| ccCallEnded: () => { | ||
| setOutgoingCall(null); | ||
| setIncomingCall(null); | ||
| setOngoingCall(null); | ||
| }, | ||
| ccShowOngoingCall: ({ call }) => setOngoingCall(call), | ||
| }); | ||
| return () => { | ||
| CometChat.removeCallListener(CALL_LISTENER_ID); | ||
| CometChatUIEventHandler.removeCallListener(CALL_LISTENER_ID); | ||
| }; | ||
| }, []); | ||
| return ( | ||
| <> | ||
| {incomingCall && <View style={StyleSheet.absoluteFill}><CometChatIncomingCall call={incomingCall} /></View>} | ||
| {outgoingCall && <View style={StyleSheet.absoluteFill}><CometChatOutgoingCall call={outgoingCall} /></View>} | ||
| {ongoingCall && <View style={StyleSheet.absoluteFill}><CometChatOngoingCall call={ongoingCall} /></View>} | ||
| </> | ||
| ); | ||
| } | ||
| ``` | ||
| > **Don't skip the `ccOutgoingCall` listener.** Without it, tapping the video/voice button in `<CometChatMessageHeader>` triggers the call at the SDK level (WebRTC, camera, audio init) 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. | ||
| In standalone mode, CallKit/ConnectionService own the foreground UI; `<CometChatIncomingCall />` is not used. Instead, a `react-native-callkeep` event listener at app root reports new calls to the OS. | ||
@@ -380,5 +438,5 @@ | ||
| | `<CometChatCallButtons user={u} group={g} />` | Voice + video icon row (typically inside `CometChatMessageHeader`). **Group + user semantics differ** — see callout below | | ||
| | `<CometChatIncomingCall />` | Root-mounted listener | | ||
| | `<CometChatOutgoingCall />` | Auto-mounted by IncomingCall on initiate | | ||
| | `<CometChatOngoingCall />` | Active call view | | ||
| | `<CometChatIncomingCall />` | Root-mounted; renders ringing UI for incoming call (controlled by parent state — see §1.7) | | ||
| | `<CometChatOutgoingCall />` | Root-mounted; renders "Calling…" UI for outgoing call (controlled by parent state — see §1.7) | | ||
| | `<CometChatOngoingCall />` | Root-mounted; renders active in-call view (controlled by parent state — see §1.7) | | ||
| | `<CometChatCallLogs onItemClick={fn} />` | History | | ||
@@ -385,0 +443,0 @@ |
@@ -271,9 +271,11 @@ --- | ||
| Incoming call notification. Render at the app root so it's visible on any screen. | ||
| Incoming call notification. **Parent-controlled — must be conditionally rendered when an incoming-call event fires on `CometChat.addCallListener`'s `onIncomingCallReceived`.** Render at the app root so it's visible on any screen. | ||
| > **DO NOT pass `onAccept`** — it short-circuits the kit's internal `acceptCall` + transition to `<CometChatOngoingCall>`. The kit fires `ccShowOngoingCall` after acceptance; let the parent's `CometChatUIEventHandler.addCallListener` mount the ongoing surface. Only handle `onDecline` + `onError`. See `cometchat-native-calls` §1.8.c and full wiring in `cometchat-native-features` §3d. | ||
| ```tsx | ||
| <CometChatIncomingCall | ||
| call={incomingCall} | ||
| onAccept={(call) => {}} | ||
| onDecline={(call) => {}} | ||
| onDecline={() => setIncomingCall(null)} | ||
| onError={() => setIncomingCall(null)} | ||
| disableSoundForCalls={false} | ||
@@ -285,3 +287,3 @@ /> | ||
| Ringing-while-calling screen after `CometChat.initiateCall(...)`. | ||
| Ringing-while-calling screen. **Parent-controlled — NOT auto-mounted by `CometChatIncomingCall` or by tapping the call button.** To make the call button in `<CometChatMessageHeader>` actually mount this overlay, register `CometChatUIEventHandler.addCallListener` and listen for `ccOutgoingCall`. Full wiring template in `cometchat-native-features` §3d. | ||
@@ -297,3 +299,3 @@ ```tsx | ||
| In-call UI — tiles, controls, mute, end-call. | ||
| In-call UI — tiles, controls, mute, end-call. **Parent-controlled — NOT auto-mounted.** Mount when `CometChatUIEventHandler.addCallListener`'s `ccShowOngoingCall` event fires (kit emits this after `acceptCall` succeeds OR when the caller's outgoing call is accepted by the peer). Full wiring in `cometchat-native-features` §3d. | ||
@@ -300,0 +302,0 @@ ```tsx |
@@ -83,3 +83,8 @@ --- | ||
| ```tsx | ||
| const user = await CometChatUIKit.getLoggedInUser(); | ||
| let user; | ||
| try { | ||
| user = await CometChatUIKit.getLoggedInUser(); | ||
| } catch (e: any) { | ||
| if (e?.code !== "NOT_FOUND") throw e; // no-session is the expected "first run" path | ||
| } | ||
| if (!user) { | ||
@@ -90,2 +95,4 @@ await CometChatUIKit.login({ uid: "cometchat-uid-1" }); // note: OBJECT form | ||
| **⚠️ `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`.) | ||
| **⚠️ `login()` takes an object `{ uid: "..." }` on React Native**, not a bare string like on the web. Passing `"cometchat-uid-1"` directly silently fails. | ||
@@ -109,3 +116,8 @@ | ||
| async function ensureLoggedIn(uid: string, authToken?: string): Promise<void> { | ||
| const existing = await CometChatUIKit.getLoggedInUser(); | ||
| let existing; | ||
| try { | ||
| existing = await CometChatUIKit.getLoggedInUser(); | ||
| } catch (e: any) { | ||
| if (e?.code !== "NOT_FOUND") throw e; // first-run path | ||
| } | ||
| if (existing) return; | ||
@@ -259,3 +271,8 @@ if (loginInFlight) { | ||
| async function ensureLoggedIn(uid: string, authToken?: string): Promise<void> { | ||
| const existing = await CometChatUIKit.getLoggedInUser(); | ||
| let existing; | ||
| try { | ||
| existing = await CometChatUIKit.getLoggedInUser(); | ||
| } catch (e: any) { | ||
| if (e?.code !== "NOT_FOUND") throw e; // first-run path | ||
| } | ||
| if (existing) return; | ||
@@ -383,13 +400,22 @@ if (loginInFlight) { | ||
| react-native-gesture-handler \ | ||
| react-native-safe-area-context | ||
| react-native-safe-area-context \ | ||
| punycode | ||
| ``` | ||
| > **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.) | ||
| > 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. | ||
| Expo adds `expo-av` / `expo-image-picker` depending on which features you enable. Calls require the separate package: | ||
| 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: | ||
| ```bash | ||
| npm install @cometchat/calls-sdk-react-native | ||
| npm install @cometchat/calls-sdk-react-native \ | ||
| react-native-background-timer \ | ||
| react-native-url-polyfill \ | ||
| react-native-performance \ | ||
| valibot | ||
| ``` | ||
| > 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. | ||
| See `cometchat-native-features` for when to add the calls SDK. | ||
@@ -413,1 +439,178 @@ | ||
| | `cometchat-native-troubleshooting` | When diagnosing build errors, runtime failures, permission issues | | ||
| ## Visual Builder integration | ||
| 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/`). | ||
| 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). | ||
| 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. | ||
| ### 1. Run `cometchat builder export` | ||
| ```bash | ||
| cometchat builder export --platform react-native --json | ||
| ``` | ||
| Defaults to `--output src/config/`. The command writes: | ||
| | File | Content | | ||
| |---|---| | ||
| | `src/config/store.ts` | Zustand store with full `AppConfig` typings, AsyncStorage persistence, `useConfig<T>(selector)` hook, `useConfigStore`. Verbatim from canonical ZIP. | | ||
| | `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). | | ||
| Resync = re-run the same command with `--force` (full re-download + replace). See `cometchat-core` §11.6 for the resync contract. | ||
| ### Files patched | ||
| | Path | Patch | | ||
| |---|---| | ||
| | `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`. | | ||
| | 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. | | ||
| | `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). | | ||
| | `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`). | | ||
| | `ios/Podfile` + `ios/<App>/Info.plist` (bare) or `app.json` plugins (Expo) | Camera + microphone usage descriptions if any `callFeatures.voiceAndVideoCalling.*` is true. | | ||
| ### Entry-file init pattern (bare RN / Expo) | ||
| ```tsx | ||
| // App.tsx | ||
| import './gesture-handler'; // bare RN: line 1, before any other import | ||
| import React, { useEffect, useState } from 'react'; | ||
| import { Platform } from 'react-native'; | ||
| import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; | ||
| import { GestureHandlerRootView } from 'react-native-gesture-handler'; | ||
| import { | ||
| CometChatUIKit, | ||
| UIKitSettings, | ||
| CometChatThemeProvider, | ||
| CometChatI18nProvider, | ||
| CometChatTheme, | ||
| } from '@cometchat/chat-uikit-react-native'; | ||
| import { CometChat } from '@cometchat/chat-sdk-react-native'; | ||
| import type { DeepPartial } from '@cometchat/chat-uikit-react-native/src/shared/helper/types'; | ||
| import { useConfig } from './src/config/store'; | ||
| import RootStackNavigator from './src/navigation/RootStackNavigator'; // your existing navigator | ||
| // Map builder font name → platform-specific PostScript / asset name. | ||
| // 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). | ||
| const FONT_MAP: Record<string, { regular: string; medium: string; bold: string }> = { | ||
| 'times new roman': { | ||
| regular: Platform.OS === 'ios' ? 'TimesNewRomanPSMT' : 'times_new_roman_regular', | ||
| medium: Platform.OS === 'ios' ? 'TimesNewRomanPSMT' : 'times_new_roman_medium', | ||
| bold: Platform.OS === 'ios' ? 'TimesNewRomanPS-BoldMT' : 'times_new_roman_bold', | ||
| }, | ||
| inter: { | ||
| regular: Platform.OS === 'ios' ? 'Inter-Regular' : 'inter_regular', | ||
| medium: Platform.OS === 'ios' ? 'Inter-Medium' : 'inter_medium', | ||
| bold: Platform.OS === 'ios' ? 'Inter-Bold' : 'inter_bold', | ||
| }, | ||
| roboto: { | ||
| regular: Platform.OS === 'ios' ? 'Roboto-Regular' : 'roboto_regular', | ||
| medium: Platform.OS === 'ios' ? 'Roboto-Medium' : 'roboto_medium', | ||
| bold: Platform.OS === 'ios' ? 'Roboto-Bold' : 'roboto_bold', | ||
| }, | ||
| }; | ||
| export default function App() { | ||
| const styleConfig = useConfig(state => state.settings.style); | ||
| const [isReady, setIsReady] = useState(false); | ||
| useEffect(() => { | ||
| const settings = new UIKitSettings.UIKitSettingsBuilder() | ||
| .setAppId(process.env.EXPO_PUBLIC_COMETCHAT_APP_ID!) | ||
| .setRegion(process.env.EXPO_PUBLIC_COMETCHAT_REGION!) | ||
| .setAuthKey(process.env.EXPO_PUBLIC_COMETCHAT_AUTH_KEY!) | ||
| .subscribePresenceForAllUsers() | ||
| .build(); | ||
| CometChatUIKit.init(settings).then(() => setIsReady(true)).catch(console.error); | ||
| }, []); | ||
| const fontKey = styleConfig.typography.font.toLowerCase().trim(); | ||
| const fontVariants = FONT_MAP[fontKey] ?? FONT_MAP.inter; | ||
| const theme: { light: DeepPartial<CometChatTheme>; dark: DeepPartial<CometChatTheme> } = { | ||
| light: { | ||
| color: { | ||
| primary: styleConfig.color.brandColor, | ||
| textPrimary: styleConfig.color.primaryTextLight, | ||
| textSecondary: styleConfig.color.secondaryTextLight, | ||
| }, | ||
| typography: { fontFamily: fontVariants.regular }, | ||
| }, | ||
| dark: { | ||
| color: { | ||
| primary: styleConfig.color.brandColor, | ||
| textPrimary: styleConfig.color.primaryTextDark, | ||
| textSecondary: styleConfig.color.secondaryTextDark, | ||
| }, | ||
| typography: { fontFamily: fontVariants.regular }, | ||
| }, | ||
| }; | ||
| if (!isReady) return null; | ||
| return ( | ||
| <GestureHandlerRootView style={{ flex: 1 }}> | ||
| <SafeAreaProvider> | ||
| <SafeAreaView edges={['top', 'bottom']} style={{ flex: 1 }}> | ||
| <CometChatThemeProvider theme={theme}> | ||
| <CometChatI18nProvider> | ||
| <RootStackNavigator /> | ||
| </CometChatI18nProvider> | ||
| </CometChatThemeProvider> | ||
| </SafeAreaView> | ||
| </SafeAreaProvider> | ||
| </GestureHandlerRootView> | ||
| ); | ||
| } | ||
| ``` | ||
| **Critical:** | ||
| - `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. | ||
| - `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+`. | ||
| - 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. | ||
| - `CometChatUIKit.init(settings)` returns a Promise — `isReady` gate before render prevents `RootStackNavigator` from mounting chat components before init resolves. | ||
| - 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). | ||
| ### Feature flag access | ||
| Components throughout the customer's app can read flags reactively: | ||
| ```tsx | ||
| const reactionsEnabled = useConfig(s => s.settings.chatFeatures.deeperEngagement.reactions); | ||
| const audioCallsEnabled = useConfig( | ||
| s => s.settings.callFeatures.voiceAndVideoCalling.oneOnOneVoiceCalling, | ||
| ); | ||
| ``` | ||
| Hide buttons / disable composer actions / skip mounting components based on these. The full `AppConfig` typings are in the copied `src/config/store.ts`. | ||
| ### Resync flow | ||
| The "Re-sync visual builder" iteration menu option (see `cometchat/SKILL.md § Step 7`) is a one-command re-run: | ||
| ```bash | ||
| cometchat builder export --platform react-native --force --json | ||
| ``` | ||
| `--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. | ||
| 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". | ||
| The customer reloads the dev build (`r` in Metro) — `useConfig` rehydrates from AsyncStorage on next mount. | ||
| ### Calls + builder | ||
| If `callFeatures.voiceAndVideoCalling.*` is true: | ||
| 1. Add `@cometchat/calls-sdk-react-native@5.0.0` + the Cloudsmith `@cometchat/calls-lib-webrtc` tarball (per `cometchat-native-calls`). | ||
| 2. Wire `CometChat.addCallListener` + `CometChatUIEventHandler.addCallListener` in `App.tsx` — copy the listener block verbatim from the canonical app's `App.tsx`. | ||
| 3. Mount `<CometChatIncomingCall>` between `<CometChatI18nProvider>` and `<RootStackNavigator>` when an `incomingCall` ref is set. The full pattern is in the canonical `App.tsx`. | ||
| 4. Configure iOS PushKit + Android FCM data-message wiring — defer to `cometchat-native-push` and invoke it after the Visual Builder section completes. | ||
| ### What is NOT honored in v1 | ||
| `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`. |
@@ -93,3 +93,6 @@ --- | ||
| ```bash | ||
| npm install @cometchat/calls-sdk-react-native | ||
| npm install @cometchat/calls-sdk-react-native \ | ||
| react-native-url-polyfill \ | ||
| react-native-performance \ | ||
| valibot | ||
| npx expo install \ | ||
@@ -102,2 +105,4 @@ @react-native-community/netinfo \ | ||
| > **`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.) | ||
| 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. | ||
@@ -505,1 +510,15 @@ | ||
| | `cometchat-native-troubleshooting` | Prebuild failures, Expo Go errors, keyboard issues, blank chat | | ||
| ## Visual Builder integration (v4.3) | ||
| 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. | ||
| **Full recipe lives in `cometchat-native-core` §"Visual Builder integration".** Expo-specific notes: | ||
| - Use `npx expo install` for the deps (not raw `npm install`) — Expo SDK pins compatible versions. | ||
| - Env via `process.env.EXPO_PUBLIC_COMETCHAT_*` (no extra Babel plugin needed; Expo handles this). | ||
| - Required additional deps beyond the standard provider set: `zustand`, `@react-native-async-storage/async-storage`. | ||
| - `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). | ||
| - A2 smoke validated: Metro bundles 10.8 MB iOS export clean on Expo SDK 54. | ||
| If the customer picks **In code**, ignore this section — the standard four-wrapper chain + provider pattern from §"Provider chain" applies. |
@@ -210,42 +210,49 @@ --- | ||
| ### 3d — Register the call listener at app root | ||
| ### 3d — Register the call listeners at app root (incoming + outgoing + ongoing) | ||
| 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`): | ||
| 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]]). | ||
| ```tsx | ||
| import React, { useEffect, useRef, useState } from "react"; | ||
| import React, { useEffect, useState } from "react"; | ||
| import { StyleSheet, View } from "react-native"; | ||
| import { CometChat } from "@cometchat/chat-sdk-react-native"; | ||
| import { CometChatIncomingCall } from "@cometchat/chat-uikit-react-native"; | ||
| import { | ||
| CometChatIncomingCall, | ||
| CometChatOutgoingCall, | ||
| CometChatOngoingCall, | ||
| CometChatUIEventHandler, | ||
| } from "@cometchat/chat-uikit-react-native"; | ||
| const CALL_LISTENER_ID = "APP_CALL_LISTENER"; | ||
| function CallEventsProvider({ children }: { children: React.ReactNode }) { | ||
| const [callReceived, setCallReceived] = useState(false); | ||
| const incomingCall = useRef<CometChat.Call | null>(null); | ||
| const LISTENER_ID = "APP_CALL_LISTENER"; | ||
| const [incomingCall, setIncomingCall] = useState<CometChat.Call | null>(null); | ||
| const [outgoingCall, setOutgoingCall] = useState<CometChat.Call | null>(null); | ||
| const [ongoingCall, setOngoingCall] = useState<CometChat.Call | null>(null); | ||
| useEffect(() => { | ||
| // SDK socket — incoming side | ||
| CometChat.addCallListener( | ||
| LISTENER_ID, | ||
| CALL_LISTENER_ID, | ||
| new CometChat.CallListener({ | ||
| onIncomingCallReceived: (call) => { | ||
| incomingCall.current = call; | ||
| setCallReceived(true); | ||
| }, | ||
| onOutgoingCallAccepted: (call) => { | ||
| // navigate to the ongoing-call screen | ||
| }, | ||
| onOutgoingCallRejected: () => { | ||
| incomingCall.current = null; | ||
| setCallReceived(false); | ||
| }, | ||
| onIncomingCallCancelled: () => { | ||
| incomingCall.current = null; | ||
| setCallReceived(false); | ||
| }, | ||
| onCallEndedMessageReceived: () => { | ||
| incomingCall.current = null; | ||
| setCallReceived(false); | ||
| }, | ||
| onIncomingCallReceived: (call: CometChat.Call) => setIncomingCall(call), | ||
| onIncomingCallCancelled: () => setIncomingCall(null), | ||
| onOutgoingCallAccepted: () => {}, // kit owns the transition | ||
| onOutgoingCallRejected: () => setOutgoingCall(null), | ||
| }) | ||
| ); | ||
| return () => CometChat.removeCallListener(LISTENER_ID); | ||
| // UI event bus — outgoing side (fired by CallButtons / MessageHeader) | ||
| CometChatUIEventHandler.addCallListener(CALL_LISTENER_ID, { | ||
| ccOutgoingCall: ({ call }) => setOutgoingCall(call), | ||
| ccCallEnded: () => { | ||
| setOutgoingCall(null); | ||
| setIncomingCall(null); | ||
| setOngoingCall(null); | ||
| }, | ||
| ccShowOngoingCall: ({ call }) => setOngoingCall(call), | ||
| }); | ||
| return () => { | ||
| CometChat.removeCallListener(CALL_LISTENER_ID); | ||
| CometChatUIEventHandler.removeCallListener(CALL_LISTENER_ID); | ||
| }; | ||
| }, []); | ||
@@ -256,9 +263,21 @@ | ||
| {children} | ||
| {callReceived && incomingCall.current && ( | ||
| <CometChatIncomingCall | ||
| call={incomingCall.current} | ||
| onAccept={() => { /* navigate to ongoing-call screen */ }} | ||
| onDecline={() => setCallReceived(false)} | ||
| /> | ||
| {incomingCall && ( | ||
| <View style={StyleSheet.absoluteFill}> | ||
| <CometChatIncomingCall | ||
| call={incomingCall} | ||
| onDecline={() => setIncomingCall(null)} | ||
| onError={() => setIncomingCall(null)} | ||
| /> | ||
| </View> | ||
| )} | ||
| {outgoingCall && ( | ||
| <View style={StyleSheet.absoluteFill}> | ||
| <CometChatOutgoingCall call={outgoingCall} /> | ||
| </View> | ||
| )} | ||
| {ongoingCall && ( | ||
| <View style={StyleSheet.absoluteFill}> | ||
| <CometChatOngoingCall call={ongoingCall} /> | ||
| </View> | ||
| )} | ||
| </> | ||
@@ -269,2 +288,6 @@ ); | ||
| > **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. | ||
| > **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). | ||
| Wrap the app (inside the existing provider chain, below `CometChatProvider`): | ||
@@ -271,0 +294,0 @@ |
@@ -289,2 +289,64 @@ --- | ||
| ### 3bb. `react-native-document-picker` build failure on RN 0.85+ (F70) | ||
| **Symptom:** Android debug build fails with a hard Java compile error inside `react-native-document-picker`'s source — typically `cannot find symbol class GuardedResultAsyncTask`. | ||
| **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. | ||
| **Fix:** uninstall it and switch to a maintained alternative if document picking is needed. | ||
| ```bash | ||
| # Uninstall the broken package | ||
| npm uninstall react-native-document-picker | ||
| # For Expo apps — official maintained pick | ||
| npx expo install expo-document-picker | ||
| # For bare RN — maintained community fork | ||
| npm install @react-native-documents/picker | ||
| ``` | ||
| `cometchat verify` flags this combination automatically (`rn_doc_picker_compat` check) — runs as part of every verify since v4.3.0. | ||
| ### 3bc. `Cannot read property 'CometChatThemeProvider' of undefined` on bare RN (F78 — chat-sdk 4.0.22 packaging regression) | ||
| **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`…"). | ||
| **This is NOT the §3b Maven-repo build failure** — that one fails the Gradle build. F78 builds fine, then crashes at runtime. | ||
| **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**: | ||
| ``` | ||
| node_modules/@cometchat/chat-sdk-react-native/node_modules/ | ||
| ├── react-native/ → 0.64.2 (duplicate of your app's 0.85.x) | ||
| └── @react-native-async-storage/async-storage/ → 1.24.0 | ||
| ``` | ||
| 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. | ||
| Confirm you're hit by it: | ||
| ```bash | ||
| ls node_modules/@cometchat/chat-sdk-react-native/node_modules/react-native/package.json && echo "F78: nested RN present" | ||
| ``` | ||
| **Fix — pick one (both verified to remove the nested install):** | ||
| 1. **npm `overrides`** (keep chat-sdk 4.0.22) — add to the app's `package.json`, then reinstall: | ||
| ```jsonc | ||
| "overrides": { | ||
| "@cometchat/chat-sdk-react-native": { | ||
| "react": "$react", | ||
| "react-native": "$react-native", | ||
| "@react-native-async-storage/async-storage": "$@react-native-async-storage/async-storage" | ||
| } | ||
| } | ||
| ``` | ||
| yarn uses the equivalent top-level `resolutions`. | ||
| 2. **Pin the known-good SDK:** `npm i @cometchat/chat-sdk-react-native@4.0.21` (zero deps → no nesting). | ||
| 3. **Prefer Expo** for greenfield — the Expo cohort is fresh-validated and never hits this. | ||
| 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. | ||
| The permanent fix is SDK-side (move react/react-native to `peerDependencies`) — tracked in **ENG-35653**. | ||
| ### 3c. Metro cache issues (post-dep-install "not found" errors) | ||
@@ -291,0 +353,0 @@ |
@@ -858,1 +858,29 @@ --- | ||
| 8. Verify: `npm run build` should succeed without SSR errors | ||
| ## 14. Visual Builder integration (v4.3) | ||
| 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. | ||
| **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + Next.js-specific gotchas: | ||
| ### App Router (recommended for Visual Builder) | ||
| - Run `cometchat builder export --platform react --output src/app/CometChat --json`. | ||
| - Create `src/app/CometChatNoSSR/CometChatNoSSR.tsx` (client component, init + login + render). | ||
| - Create `src/app/CometChatAppWrapper.tsx` with `"use client"` + `dynamic(() => import("../app/CometChatNoSSR/CometChatNoSSR"), { ssr: false })`. | ||
| - Import the wrapper in `src/app/page.tsx`. | ||
| - **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/`. | ||
| ### Pages Router (NOT recommended) | ||
| **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. | ||
| 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. | ||
| ### Both routers | ||
| - Use `process.env.NEXT_PUBLIC_COMETCHAT_*` (NOT `import.meta.env.*` which is Vite-only). | ||
| - Pin `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5`. | ||
| - `package.json` needs `cometChatCustomConfig` block (Finding F2). | ||
| If the customer picks **In code**, ignore this section. |
@@ -576,1 +576,14 @@ --- | ||
| **Do not skip step 6.** The provider must wrap the app root so init happens once, regardless of which route or modal opens chat. | ||
| ## 9. Visual Builder integration (v4.3) | ||
| 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 />`. | ||
| **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + Vite/CRA-specific gotchas: | ||
| - **`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. | ||
| - **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. | ||
| - **`package.json` needs `cometChatCustomConfig`** block (Finding F2). The canonical context reads `packageJson.cometChatCustomConfig.{name, version, production}`. | ||
| - **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). | ||
| 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.) |
@@ -260,2 +260,4 @@ --- | ||
| > **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. | ||
| ### SSR prevention | ||
@@ -721,1 +723,32 @@ | ||
| 10. Verify: loaders/actions contain NO CometChat imports | ||
| ## 10. Visual Builder integration (v4.3) | ||
| 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. | ||
| **Full recipe lives in `cometchat-core` §11 "Visual Builder integration".** This section is a pointer + React Router-specific gotchas: | ||
| ### Framework mode (v7 default) | ||
| - Run `cometchat builder export --platform react --output app/CometChat --json`. | ||
| - Create the chat route as `app/routes/chat.client.tsx` — the `.client.tsx` suffix skips SSR for this file. | ||
| - Register the route in `app/routes.ts` via `route("chat", "routes/chat.client.tsx")`. | ||
| ### Data mode (library, v6/v7) | ||
| - Run `cometchat builder export --platform react --output src/CometChat --json` (default). | ||
| - Same Vite-based init pattern as plain Vite + React (see `cometchat-react-patterns` §9). | ||
| ### ✓ Rolldown handled (Finding F19, fixed in v4.3.0) | ||
| 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. | ||
| **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. | ||
| ### Both modes | ||
| - Pin `@cometchat/chat-uikit-react@6.4.3` + `@cometchat/calls-sdk-javascript@4.2.5`. | ||
| - `package.json` needs `cometChatCustomConfig` block (Finding F2). | ||
| - Vite 7+ `tsconfig.app.json` requires the relaxation set from `cometchat-core` §11.2. | ||
| If the customer picks **In code**, ignore this section. |
Sorry, the diff of this file is too big to display
3109611
3.81%969
2.65%201
11.67%