@cometchat/skills 4.2.1 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -12
- package/bin/install.js +32 -4
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +236 -17
- package/skills/cometchat-android-v5-core/SKILL.md +16 -0
- package/skills/cometchat-android-v6-calls/SKILL.md +1 -1
- package/skills/cometchat-android-v6-core/SKILL.md +202 -2
- package/skills/cometchat-android-v6-migration/SKILL.md +1 -1
- package/skills/cometchat-angular-core/SKILL.md +22 -0
- package/skills/cometchat-astro-patterns/SKILL.md +20 -0
- package/skills/cometchat-core/SKILL.md +163 -0
- package/skills/cometchat-flutter-v5-core/SKILL.md +17 -0
- package/skills/cometchat-flutter-v6-calls/SKILL.md +10 -10
- package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +3 -3
- package/skills/cometchat-flutter-v6-core/SKILL.md +178 -1
- package/skills/cometchat-flutter-v6-push/SKILL.md +2 -2
- package/skills/cometchat-flutter-v6-testing/SKILL.md +2 -2
- package/skills/cometchat-ios-core/SKILL.md +142 -0
- package/skills/cometchat-native-bare-patterns/SKILL.md +22 -1
- package/skills/cometchat-native-calls/SKILL.md +63 -5
- package/skills/cometchat-native-components/SKILL.md +7 -5
- package/skills/cometchat-native-core/SKILL.md +209 -6
- package/skills/cometchat-native-expo-patterns/SKILL.md +20 -1
- package/skills/cometchat-native-features/SKILL.md +57 -34
- package/skills/cometchat-native-troubleshooting/SKILL.md +62 -0
- package/skills/cometchat-nextjs-patterns/SKILL.md +28 -0
- package/skills/cometchat-react-patterns/SKILL.md +13 -0
- package/skills/cometchat-react-router-patterns/SKILL.md +33 -0
|
@@ -302,4 +302,181 @@ Every component follows this structure:
|
|
|
302
302
|
- [ ] Theme cached in `didChangeDependencies()`, not `build()`
|
|
303
303
|
- [ ] SDK listeners registered with unique ID, removed in `dispose()`
|
|
304
304
|
- [ ] Colors from `CometChatThemeHelper`, never hardcoded
|
|
305
|
-
|
|
305
|
+
|
|
306
|
+
## Visual Builder integration
|
|
307
|
+
|
|
308
|
+
> **⚠ 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.
|
|
309
|
+
>
|
|
310
|
+
> 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.
|
|
311
|
+
|
|
312
|
+
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/`).
|
|
313
|
+
|
|
314
|
+
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(...)`.
|
|
315
|
+
|
|
316
|
+
This is intentionally a heavier copy than iOS (Pod) / Android (Gradle plugin) — the Flutter builder package is not yet published to pub.dev.
|
|
317
|
+
|
|
318
|
+
### 1. Run `cometchat builder export`
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
cometchat builder export --platform flutter --json
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
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).
|
|
325
|
+
|
|
326
|
+
`BuilderSettingsHelper.loadFromAsset()` reads this exported settings file at runtime.
|
|
327
|
+
|
|
328
|
+
Resync = re-run the same command with `--force` (full re-download + replace). See `cometchat-core` §11.6 for the resync contract.
|
|
329
|
+
|
|
330
|
+
#### F56 + F57 + F58 — known canonical-ZIP rough edges (re-apply after every export)
|
|
331
|
+
|
|
332
|
+
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):
|
|
333
|
+
|
|
334
|
+
**F56 — Wrong asset path in `api_services.dart`**
|
|
335
|
+
|
|
336
|
+
`chat_builder/lib/services/api_services.dart` line ~41:
|
|
337
|
+
|
|
338
|
+
```dart
|
|
339
|
+
// Canonical (broken):
|
|
340
|
+
await loadJsonFromAssets('assets/chat_builder/sample_data.json');
|
|
341
|
+
|
|
342
|
+
// Agent must patch to:
|
|
343
|
+
await loadJsonFromAssets('assets/sample_app/sample_data.json');
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
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.
|
|
347
|
+
|
|
348
|
+
**F57 — F56 patch is overwritten on every resync**
|
|
349
|
+
|
|
350
|
+
`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".
|
|
351
|
+
|
|
352
|
+
**F58 — Asset copy is additive, not idempotent**
|
|
353
|
+
|
|
354
|
+
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.
|
|
355
|
+
|
|
356
|
+
**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.
|
|
357
|
+
|
|
358
|
+
### Files skills writes (after `builder export`)
|
|
359
|
+
|
|
360
|
+
| Path | Content |
|
|
361
|
+
|---|---|
|
|
362
|
+
| `lib/cometchat/cometchat_app.dart` | Thin `StatefulWidget` that initializes the kit then exposes a launch trigger calling `ChatBuilder.launchBuilder(context)` |
|
|
363
|
+
| `lib/cometchat/secrets.dart` | Credentials class (`Secrets.appId / region / authKey`) populated by Step 2c, added to `.gitignore` |
|
|
364
|
+
|
|
365
|
+
### Files patched
|
|
366
|
+
|
|
367
|
+
| Path | Patch |
|
|
368
|
+
|---|---|
|
|
369
|
+
| `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` |
|
|
370
|
+
| `lib/main.dart` | Insert `WidgetsFlutterBinding.ensureInitialized()` + `await BuilderSettingsHelper.loadFromAsset()` before `runApp()` |
|
|
371
|
+
| `ios/Podfile` | `platform :ios, '13.0'` (raise if lower) |
|
|
372
|
+
| `android/app/build.gradle.kts` | `ndkVersion = "27.0.12077973"`, `minSdk = 24` (raise if lower) |
|
|
373
|
+
| `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 |
|
|
374
|
+
| `android/app/src/main/AndroidManifest.xml` + `ios/Runner/Info.plist` | `RECORD_AUDIO` / `CAMERA` permissions + `NSMicrophoneUsageDescription` / `NSCameraUsageDescription` if any call feature is enabled |
|
|
375
|
+
|
|
376
|
+
### Init flow (lib/main.dart)
|
|
377
|
+
|
|
378
|
+
```dart
|
|
379
|
+
import 'package:flutter/material.dart';
|
|
380
|
+
import 'package:chat_builder/builder/builder_settings_helper.dart';
|
|
381
|
+
|
|
382
|
+
Future<void> main() async {
|
|
383
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
384
|
+
await BuilderSettingsHelper.loadFromAsset();
|
|
385
|
+
runApp(const MyApp());
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`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.
|
|
390
|
+
|
|
391
|
+
### The wrapper template
|
|
392
|
+
|
|
393
|
+
```dart
|
|
394
|
+
// lib/cometchat/cometchat_app.dart
|
|
395
|
+
import 'package:flutter/material.dart';
|
|
396
|
+
import 'package:chat_builder/builder/chat_builder.dart';
|
|
397
|
+
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
|
|
398
|
+
|
|
399
|
+
import 'secrets.dart';
|
|
400
|
+
|
|
401
|
+
/// Top-level chat surface emitted by the Visual Builder Visually path.
|
|
402
|
+
/// The `chat_builder` package owns the UI; this widget bootstraps UI Kit init
|
|
403
|
+
/// then renders a launch trigger. Step 3c placement decides where this widget
|
|
404
|
+
/// mounts in the host app (a route, a tab, a dialog).
|
|
405
|
+
class CometChatApp extends StatefulWidget {
|
|
406
|
+
const CometChatApp({super.key});
|
|
407
|
+
|
|
408
|
+
@override
|
|
409
|
+
State<CometChatApp> createState() => _CometChatAppState();
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
class _CometChatAppState extends State<CometChatApp> {
|
|
413
|
+
bool _isReady = false;
|
|
414
|
+
String? _initError;
|
|
415
|
+
|
|
416
|
+
@override
|
|
417
|
+
void initState() {
|
|
418
|
+
super.initState();
|
|
419
|
+
_bootstrap();
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
void _bootstrap() {
|
|
423
|
+
final settings = (UIKitSettingsBuilder()
|
|
424
|
+
..appId = Secrets.appId
|
|
425
|
+
..region = Secrets.region
|
|
426
|
+
..authKey = Secrets.authKey
|
|
427
|
+
..subscriptionType = CometChatSubscriptionType.allUsers)
|
|
428
|
+
.build();
|
|
429
|
+
|
|
430
|
+
CometChatUIKit.init(
|
|
431
|
+
uiKitSettings: settings,
|
|
432
|
+
onSuccess: (_) => setState(() => _isReady = true),
|
|
433
|
+
onError: (e) => setState(() => _initError = e.message),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
@override
|
|
438
|
+
Widget build(BuildContext context) {
|
|
439
|
+
if (_initError != null) {
|
|
440
|
+
return Scaffold(body: Center(child: Text('CometChat init failed: $_initError')));
|
|
441
|
+
}
|
|
442
|
+
if (!_isReady) {
|
|
443
|
+
return const Scaffold(body: Center(child: CircularProgressIndicator()));
|
|
444
|
+
}
|
|
445
|
+
return Scaffold(
|
|
446
|
+
resizeToAvoidBottomInset: false,
|
|
447
|
+
body: Center(
|
|
448
|
+
child: ElevatedButton(
|
|
449
|
+
onPressed: () => ChatBuilder.launchBuilder(context),
|
|
450
|
+
child: const Text('Open chat'),
|
|
451
|
+
),
|
|
452
|
+
),
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
`ChatBuilder.launchBuilder(context)` opens the builder's login / dashboard flow; once a user is logged in, the package's internal navigation handles conversations → messages.
|
|
459
|
+
|
|
460
|
+
For direct deep-links into a specific user or group thread (skipping the dashboard), use:
|
|
461
|
+
|
|
462
|
+
```dart
|
|
463
|
+
ChatBuilder.launchMessages(context: context, user: user);
|
|
464
|
+
ChatBuilder.launchMessages(context: context, group: group);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
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).
|
|
468
|
+
|
|
469
|
+
`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.
|
|
470
|
+
|
|
471
|
+
### Calls + builder
|
|
472
|
+
|
|
473
|
+
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:
|
|
474
|
+
1. Add `cometchat_calls_uikit` to `pubspec.yaml`
|
|
475
|
+
2. iOS `Info.plist` + Android `AndroidManifest.xml` permissions per `cometchat-flutter-v6-calls`
|
|
476
|
+
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)
|
|
477
|
+
4. Push wiring — defer to `cometchat-flutter-v6-push` (audit-verified namespace `PNRegistry`, NOT `CometChatNotifications` — [[project_sdk_symbol_audit_2026_05_14]])
|
|
478
|
+
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
|
|
479
|
+
|
|
480
|
+
### What is NOT honored in v1
|
|
481
|
+
|
|
482
|
+
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cometchat-flutter-v6-push
|
|
3
|
-
description: Push notifications for CometChat Flutter UIKit v6 (
|
|
3
|
+
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.
|
|
4
4
|
license: "MIT"
|
|
5
5
|
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"
|
|
6
6
|
allowed-tools: "shell, file-read, file-search, file-list, ask-user"
|
|
@@ -56,7 +56,7 @@ Unlike Web Push (which requires you to run a push server), CometChat hosts the c
|
|
|
56
56
|
dependencies:
|
|
57
57
|
flutter:
|
|
58
58
|
sdk: flutter
|
|
59
|
-
cometchat_chat_uikit: ^6.0
|
|
59
|
+
cometchat_chat_uikit: ^6.0
|
|
60
60
|
firebase_messaging: ^14.0.0
|
|
61
61
|
firebase_core: ^2.0.0
|
|
62
62
|
flutter_local_notifications: ^16.0.0 # for foreground notifications + iOS in-app banner
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cometchat-flutter-v6-testing
|
|
3
|
-
description: Testing patterns for CometChat Flutter UIKit v6 (
|
|
3
|
+
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.
|
|
4
4
|
license: "MIT"
|
|
5
5
|
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"
|
|
6
6
|
allowed-tools: "shell, file-read, file-search, file-list, ask-user"
|
|
@@ -12,7 +12,7 @@ metadata:
|
|
|
12
12
|
|
|
13
13
|
## Purpose
|
|
14
14
|
|
|
15
|
-
Test recipes for Flutter UIKit v6 (
|
|
15
|
+
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`).
|
|
16
16
|
|
|
17
17
|
**Read these other skills first:**
|
|
18
18
|
- `cometchat-flutter-v6-core` — UIKitSettings, init/login order, hard rules
|
|
@@ -820,3 +820,145 @@ pod 'CometChatCallsSDK', '~> 4.0'
|
|
|
820
820
|
```
|
|
821
821
|
|
|
822
822
|
The UI Kit automatically detects and enables calling features when the Calls SDK is present.
|
|
823
|
+
|
|
824
|
+
## Visual Builder integration
|
|
825
|
+
|
|
826
|
+
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/`):
|
|
827
|
+
|
|
828
|
+
- `MessagesVC.swift` — verbatim view controller composing header + list + composer
|
|
829
|
+
- `ThreadedMessagesVC.swift` — verbatim helper VC (imported by MessagesVC)
|
|
830
|
+
- `cometchat-builder-settings.json` — **envelope-shape JSON** `{ builderId, name, settings: {...} }` (no sentinel — JSON forbids `//` comments)
|
|
831
|
+
|
|
832
|
+
### 1. Run `cometchat builder export`
|
|
833
|
+
|
|
834
|
+
```bash
|
|
835
|
+
cometchat builder export --platform ios --json
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
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).
|
|
839
|
+
|
|
840
|
+
`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.
|
|
841
|
+
|
|
842
|
+
Resync = re-run the same command with `--force`. See `cometchat-core` §11.6 for the resync contract.
|
|
843
|
+
|
|
844
|
+
### 2. Files skills writes (after `builder export`)
|
|
845
|
+
|
|
846
|
+
| Path | Content |
|
|
847
|
+
|---|---|
|
|
848
|
+
| `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. |
|
|
849
|
+
|
|
850
|
+
### Files patched
|
|
851
|
+
|
|
852
|
+
| Path | Patch |
|
|
853
|
+
|---|---|
|
|
854
|
+
| `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` |
|
|
855
|
+
| `AppDelegate.swift` (UIKit) / `App.swift` (SwiftUI) | Add `CometChatBuilderSettings.loadFromJSON()` + theme application + `CometChatUIKit.init(uiKitSettings:)` in `application(_:didFinishLaunchingWithOptions:)` — see init code below |
|
|
856
|
+
| App target | Toggle `cometchat-builder-settings.json` for **target membership** in Xcode (else `loadFromJSON()` silently returns defaults) |
|
|
857
|
+
| 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) |
|
|
858
|
+
| `Info.plist` | `NSMicrophoneUsageDescription` + `NSCameraUsageDescription` if `CometChatBuilderSettings.shared.callFeatures` has any enabled feature |
|
|
859
|
+
|
|
860
|
+
### Init code — AppDelegate
|
|
861
|
+
|
|
862
|
+
```swift
|
|
863
|
+
// AppDelegate.swift — inside application(_:didFinishLaunchingWithOptions:)
|
|
864
|
+
import CometChatBuilder
|
|
865
|
+
import CometChatUIKitSwift
|
|
866
|
+
|
|
867
|
+
// 1. Load builder settings from bundled JSON
|
|
868
|
+
CometChatBuilderSettings.loadFromJSON()
|
|
869
|
+
|
|
870
|
+
// 2. Apply builder theme tokens to the kit's global theme
|
|
871
|
+
CometChatTheme.primaryColor = UIColor.dynamicColor(
|
|
872
|
+
lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.brandColor),
|
|
873
|
+
darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.brandColor)
|
|
874
|
+
)
|
|
875
|
+
CometChatTheme.textColorPrimary = UIColor.dynamicColor(
|
|
876
|
+
lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.primaryTextLight),
|
|
877
|
+
darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.primaryTextDark)
|
|
878
|
+
)
|
|
879
|
+
CometChatTheme.textColorSecondary = UIColor.dynamicColor(
|
|
880
|
+
lightModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.secondaryTextLight),
|
|
881
|
+
darkModeColor: UIColor(hex: CometChatBuilderSettings.shared.style.color.secondaryTextDark)
|
|
882
|
+
)
|
|
883
|
+
CometChatTypography.customFontFamilyName = CometChatBuilderSettings.shared.style.typography.font
|
|
884
|
+
|
|
885
|
+
// 3. Standard UI Kit init (this file's §2 — credentials from Secrets.swift)
|
|
886
|
+
CometChatManager.shared.initialize(
|
|
887
|
+
appID: Secrets.appID, authKey: Secrets.authKey, region: Secrets.region
|
|
888
|
+
) { _, _ in }
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
`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()`.
|
|
892
|
+
|
|
893
|
+
### The wrapper template
|
|
894
|
+
|
|
895
|
+
```swift
|
|
896
|
+
// CometChat/CometChatApp.swift
|
|
897
|
+
import SwiftUI
|
|
898
|
+
import UIKit
|
|
899
|
+
import CometChatBuilder
|
|
900
|
+
import CometChatUIKitSwift
|
|
901
|
+
import CometChatSDK
|
|
902
|
+
|
|
903
|
+
/// Top-level chat surface emitted by the Visual Builder Visually path.
|
|
904
|
+
/// Master/detail SwiftUI host: `CometChatConversations` at root, `MessagesVC` pushed on tap.
|
|
905
|
+
/// Feature visibility flags are read from `CometChatBuilderSettings.shared`.
|
|
906
|
+
public struct CometChatApp: View {
|
|
907
|
+
public init() {}
|
|
908
|
+
public var body: some View {
|
|
909
|
+
ConversationsContainer().ignoresSafeArea()
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
private struct ConversationsContainer: UIViewControllerRepresentable {
|
|
914
|
+
func makeUIViewController(context: Context) -> UINavigationController {
|
|
915
|
+
let conversationsVC = CometChatConversations()
|
|
916
|
+
let core = CometChatBuilderSettings.shared.chatFeatures.coreMessagingExperience
|
|
917
|
+
conversationsVC.hideUserStatus = !core.userAndFriendsPresence
|
|
918
|
+
conversationsVC.hideReceipts = !core.messageDeliveryAndReadReceipts
|
|
919
|
+
|
|
920
|
+
let nav = UINavigationController(rootViewController: conversationsVC)
|
|
921
|
+
conversationsVC.set(onItemClick: { [weak nav] conversation, _ in
|
|
922
|
+
let vc = MessagesVC()
|
|
923
|
+
if let user = conversation.conversationWith as? User { vc.user = user }
|
|
924
|
+
else if let group = conversation.conversationWith as? Group { vc.group = group }
|
|
925
|
+
nav?.pushViewController(vc, animated: true)
|
|
926
|
+
})
|
|
927
|
+
return nav
|
|
928
|
+
}
|
|
929
|
+
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
private extension UIColor {
|
|
933
|
+
convenience init?(hex: String) {
|
|
934
|
+
let s = hex.replacingOccurrences(of: "#", with: "")
|
|
935
|
+
guard s.count == 6, let rgb = UInt32(s, radix: 16) else { return nil }
|
|
936
|
+
self.init(red: CGFloat((rgb >> 16) & 0xFF) / 255,
|
|
937
|
+
green: CGFloat((rgb >> 8) & 0xFF) / 255,
|
|
938
|
+
blue: CGFloat(rgb & 0xFF) / 255,
|
|
939
|
+
alpha: 1)
|
|
940
|
+
}
|
|
941
|
+
static func dynamicColor(lightModeColor: UIColor?, darkModeColor: UIColor?) -> UIColor {
|
|
942
|
+
UIColor { traits in
|
|
943
|
+
(traits.userInterfaceStyle == .dark ? darkModeColor : lightModeColor) ?? UIColor.label
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
`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.
|
|
950
|
+
|
|
951
|
+
`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`.
|
|
952
|
+
|
|
953
|
+
**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.
|
|
954
|
+
|
|
955
|
+
### Calls + builder
|
|
956
|
+
|
|
957
|
+
If `CometChatBuilderSettings.shared.callFeatures` has any enabled feature:
|
|
958
|
+
1. Init Calls SDK at the same site as UI Kit (see `cometchat-ios-calls`)
|
|
959
|
+
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)
|
|
960
|
+
3. PushKit + CallKit wiring — defer to `cometchat-ios-push`
|
|
961
|
+
|
|
962
|
+
### What is NOT honored in v1
|
|
963
|
+
|
|
964
|
+
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.
|
|
@@ -85,9 +85,14 @@ npm install \
|
|
|
85
85
|
@react-native-community/netinfo \
|
|
86
86
|
react-native-background-timer \
|
|
87
87
|
react-native-callstats \
|
|
88
|
-
react-native-webrtc
|
|
88
|
+
react-native-webrtc \
|
|
89
|
+
react-native-url-polyfill \
|
|
90
|
+
react-native-performance \
|
|
91
|
+
valibot
|
|
89
92
|
```
|
|
90
93
|
|
|
94
|
+
> **`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`.)
|
|
95
|
+
|
|
91
96
|
WebRTC bloats the binary. Skip until the user actually wants calls.
|
|
92
97
|
|
|
93
98
|
---
|
|
@@ -256,6 +261,8 @@ allprojects {
|
|
|
256
261
|
|
|
257
262
|
Without this fix, the whole Android build fails early. This is a UI Kit-specific gotcha because the kit pins async-storage v3+.
|
|
258
263
|
|
|
264
|
+
> **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.
|
|
265
|
+
|
|
259
266
|
### 3c. Android: Metro config for custom fonts or assets (if applicable)
|
|
260
267
|
|
|
261
268
|
If the project uses custom icon fonts or bundled assets, confirm `react-native.config.js` includes:
|
|
@@ -510,3 +517,17 @@ Use Expo + `npx expo prebuild` to generate native projects on demand. Stay on `c
|
|
|
510
517
|
| `cometchat-native-customization` | Text formatters, events, custom views |
|
|
511
518
|
| `cometchat-native-production` | Server-side auth tokens + user management |
|
|
512
519
|
| `cometchat-native-troubleshooting` | pod install fails, build errors, missing modules, privacy manifest rejection |
|
|
520
|
+
|
|
521
|
+
## Visual Builder integration (v4.3)
|
|
522
|
+
|
|
523
|
+
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.
|
|
524
|
+
|
|
525
|
+
**Full recipe lives in `cometchat-native-core` §"Visual Builder integration".** Bare RN-specific notes:
|
|
526
|
+
|
|
527
|
+
- 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").
|
|
528
|
+
- 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.
|
|
529
|
+
- Required additional deps: `zustand`, `@react-native-async-storage/async-storage`, plus the standard 11 peers from §"Mandatory peer deps".
|
|
530
|
+
- `useConfig(s => s.settings.style)` — note the selector takes `AppConfig` directly, NOT `s.config.settings.style` (Finding F6, 2026-05-21).
|
|
531
|
+
- 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.
|
|
532
|
+
|
|
533
|
+
If the customer picks **In code**, ignore this section — the standard four-wrapper chain + provider pattern applies.
|
|
@@ -133,9 +133,67 @@ Required:
|
|
|
133
133
|
- Android — runtime requests via `PermissionsAndroid.requestMultiple` for `RECORD_AUDIO`, `CAMERA`, `POST_NOTIFICATIONS` (Android 13+)
|
|
134
134
|
- Expo managed — declare in `app.json` `expo.ios.infoPlist` and `expo.android.permissions`; the prebuild merges into native manifests
|
|
135
135
|
|
|
136
|
-
### 1.7 IncomingCall
|
|
136
|
+
### 1.7 IncomingCall + OutgoingCall + OngoingCall — full event-listener wiring
|
|
137
137
|
|
|
138
|
-
`<CometChatIncomingCall />`
|
|
138
|
+
`<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`.
|
|
139
|
+
|
|
140
|
+
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.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { CometChat } from "@cometchat/chat-sdk-react-native";
|
|
144
|
+
import {
|
|
145
|
+
CometChatIncomingCall,
|
|
146
|
+
CometChatOutgoingCall,
|
|
147
|
+
CometChatOngoingCall,
|
|
148
|
+
CometChatUIEventHandler,
|
|
149
|
+
} from "@cometchat/chat-uikit-react-native";
|
|
150
|
+
import { StyleSheet, View } from "react-native";
|
|
151
|
+
|
|
152
|
+
const CALL_LISTENER_ID = "app-call-listener";
|
|
153
|
+
|
|
154
|
+
function CallSurfaces() {
|
|
155
|
+
const [outgoingCall, setOutgoingCall] = useState<CometChat.Call | null>(null);
|
|
156
|
+
const [incomingCall, setIncomingCall] = useState<CometChat.Call | null>(null);
|
|
157
|
+
const [ongoingCall, setOngoingCall] = useState<CometChat.Call | null>(null);
|
|
158
|
+
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
// SDK socket — incoming + outgoing-rejected
|
|
161
|
+
CometChat.addCallListener(
|
|
162
|
+
CALL_LISTENER_ID,
|
|
163
|
+
new CometChat.CallListener({
|
|
164
|
+
onIncomingCallReceived: (call: CometChat.Call) => setIncomingCall(call),
|
|
165
|
+
onIncomingCallCancelled: () => setIncomingCall(null),
|
|
166
|
+
onOutgoingCallAccepted: () => {},
|
|
167
|
+
onOutgoingCallRejected: () => setOutgoingCall(null),
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
// UI events fired by the kit's CallButtons / MessageHeader on tap
|
|
171
|
+
CometChatUIEventHandler.addCallListener(CALL_LISTENER_ID, {
|
|
172
|
+
ccOutgoingCall: ({ call }) => setOutgoingCall(call),
|
|
173
|
+
ccCallEnded: () => {
|
|
174
|
+
setOutgoingCall(null);
|
|
175
|
+
setIncomingCall(null);
|
|
176
|
+
setOngoingCall(null);
|
|
177
|
+
},
|
|
178
|
+
ccShowOngoingCall: ({ call }) => setOngoingCall(call),
|
|
179
|
+
});
|
|
180
|
+
return () => {
|
|
181
|
+
CometChat.removeCallListener(CALL_LISTENER_ID);
|
|
182
|
+
CometChatUIEventHandler.removeCallListener(CALL_LISTENER_ID);
|
|
183
|
+
};
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<>
|
|
188
|
+
{incomingCall && <View style={StyleSheet.absoluteFill}><CometChatIncomingCall call={incomingCall} /></View>}
|
|
189
|
+
{outgoingCall && <View style={StyleSheet.absoluteFill}><CometChatOutgoingCall call={outgoingCall} /></View>}
|
|
190
|
+
{ongoingCall && <View style={StyleSheet.absoluteFill}><CometChatOngoingCall call={ongoingCall} /></View>}
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
> **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.
|
|
139
197
|
|
|
140
198
|
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.
|
|
141
199
|
|
|
@@ -376,9 +434,9 @@ Same names + shapes as the JavaScript SDK (Section 3 of `cometchat-react-calls`)
|
|
|
376
434
|
| Component | Purpose |
|
|
377
435
|
|---|---|
|
|
378
436
|
| `<CometChatCallButtons user={u} group={g} />` | Voice + video icon row (typically inside `CometChatMessageHeader`). **Group + user semantics differ** — see callout below |
|
|
379
|
-
| `<CometChatIncomingCall />` | Root-mounted
|
|
380
|
-
| `<CometChatOutgoingCall />` |
|
|
381
|
-
| `<CometChatOngoingCall />` |
|
|
437
|
+
| `<CometChatIncomingCall />` | Root-mounted; renders ringing UI for incoming call (controlled by parent state — see §1.7) |
|
|
438
|
+
| `<CometChatOutgoingCall />` | Root-mounted; renders "Calling…" UI for outgoing call (controlled by parent state — see §1.7) |
|
|
439
|
+
| `<CometChatOngoingCall />` | Root-mounted; renders active in-call view (controlled by parent state — see §1.7) |
|
|
382
440
|
| `<CometChatCallLogs onItemClick={fn} />` | History |
|
|
383
441
|
|
|
384
442
|
#### ⚠️ Group calls use message-based join, not the ringing channel (validated 2026-05-15)
|
|
@@ -268,20 +268,22 @@ Voice + video call initiators. Drop into `AuxiliaryButtonView` on `CometChatMess
|
|
|
268
268
|
|
|
269
269
|
### CometChatIncomingCall
|
|
270
270
|
|
|
271
|
-
Incoming call notification. Render at the app root so it's visible on any screen.
|
|
271
|
+
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.
|
|
272
|
+
|
|
273
|
+
> **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.
|
|
272
274
|
|
|
273
275
|
```tsx
|
|
274
276
|
<CometChatIncomingCall
|
|
275
277
|
call={incomingCall}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
onDecline={() => setIncomingCall(null)}
|
|
279
|
+
onError={() => setIncomingCall(null)}
|
|
278
280
|
disableSoundForCalls={false}
|
|
279
281
|
/>
|
|
280
282
|
```
|
|
281
283
|
|
|
282
284
|
### CometChatOutgoingCall
|
|
283
285
|
|
|
284
|
-
Ringing-while-calling screen
|
|
286
|
+
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.
|
|
285
287
|
|
|
286
288
|
```tsx
|
|
287
289
|
<CometChatOutgoingCall
|
|
@@ -292,7 +294,7 @@ Ringing-while-calling screen after `CometChat.initiateCall(...)`.
|
|
|
292
294
|
|
|
293
295
|
### CometChatOngoingCall
|
|
294
296
|
|
|
295
|
-
In-call UI — tiles, controls, mute, end-call.
|
|
297
|
+
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.
|
|
296
298
|
|
|
297
299
|
```tsx
|
|
298
300
|
<CometChatOngoingCall
|