@cometchat/skills-cli 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/bin/cometchat.mjs +12 -6
  2. package/dist/index.js +5467 -1539
  3. package/dist/registry/schemas/config.schema.json +119 -0
  4. package/dist/registry/v6/experiences/1-conversation-list/reactjs-dedicated-route.template.json +81 -0
  5. package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +87 -0
  6. package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +20 -0
  7. package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +54 -0
  8. package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatSelector.tsx.tpl +40 -0
  9. package/dist/registry/v6/experiences/2-one-to-one/reactjs-dedicated-route.template.json +75 -0
  10. package/dist/registry/v6/experiences/2-one-to-one/reactjs.template.json +6 -3
  11. package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +84 -0
  12. package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +9 -0
  13. package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +43 -0
  14. package/dist/registry/v6/experiences/3-tab-based/reactjs-dedicated-route.template.json +89 -0
  15. package/dist/registry/v6/experiences/3-tab-based/reactjs.template.json +6 -3
  16. package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +84 -0
  17. package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +11 -0
  18. package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +61 -0
  19. package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.css.tpl +43 -0
  20. package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.tsx.tpl +72 -0
  21. package/dist/utils/error-dictionary.js +170 -0
  22. package/dist/utils/onboarding-options.js +30 -0
  23. package/package.json +11 -4
@@ -0,0 +1,119 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://cometchat.com/schemas/config.json",
4
+ "title": "CometChat Project Config",
5
+ "description": "Persistent project-level configuration for CometChat integrations. Written by the wizard/CLI, read by skills and commands.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["version"],
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string",
12
+ "description": "JSON Schema URI for editor autocomplete."
13
+ },
14
+ "version": {
15
+ "type": "number",
16
+ "const": 1,
17
+ "description": "Schema version. Always 1."
18
+ },
19
+ "appId": {
20
+ "type": "string",
21
+ "description": "CometChat App ID. Not a secret — safe to commit."
22
+ },
23
+ "region": {
24
+ "type": "string",
25
+ "enum": ["us", "eu", "in"],
26
+ "description": "CometChat data region."
27
+ },
28
+ "framework": {
29
+ "type": "string",
30
+ "enum": ["reactjs", "nextjs", "react-router", "astro"],
31
+ "description": "Detected or user-overridden framework."
32
+ },
33
+ "router": {
34
+ "type": "string",
35
+ "enum": ["app", "pages"],
36
+ "description": "Next.js router variant. Only applicable when framework is nextjs."
37
+ },
38
+ "envPrefix": {
39
+ "type": "string",
40
+ "enum": ["VITE_", "NEXT_PUBLIC_", "PUBLIC_"],
41
+ "description": "Environment variable prefix for client-side env vars."
42
+ },
43
+ "typescript": {
44
+ "type": "boolean",
45
+ "description": "Whether the project uses TypeScript."
46
+ },
47
+ "intent": {
48
+ "type": "string",
49
+ "enum": ["marketplace", "saas", "social", "messaging", "support", "exploring"],
50
+ "description": "What the user is building — drives the recommended integration shape."
51
+ },
52
+ "experience": {
53
+ "type": "number",
54
+ "enum": [1, 2, 3],
55
+ "description": "CometChat experience level (1 = multi-conversation, 2 = single-thread, 3 = full-messenger)."
56
+ },
57
+ "placement": {
58
+ "type": "string",
59
+ "enum": ["route", "modal", "drawer", "widget", "embed", "demo"],
60
+ "description": "Where the chat UI is placed in the app."
61
+ },
62
+ "placementPath": {
63
+ "type": "string",
64
+ "description": "Route path for the chat page (e.g. \"/messages\")."
65
+ },
66
+ "authMode": {
67
+ "type": "string",
68
+ "enum": ["authKey", "tokenEndpoint"],
69
+ "description": "Authentication mode — dev (authKey) or production (tokenEndpoint)."
70
+ },
71
+ "tokenEndpointPath": {
72
+ "type": "string",
73
+ "description": "Path to the server-side token endpoint (e.g. \"app/api/cometchat-token/route.ts\")."
74
+ },
75
+ "theme": {
76
+ "type": "string",
77
+ "description": "Theme preset name (slack, whatsapp, discord, etc.) or \"custom\"."
78
+ },
79
+ "darkMode": {
80
+ "type": "boolean",
81
+ "description": "Whether dark mode is enabled."
82
+ },
83
+ "paths": {
84
+ "type": "object",
85
+ "description": "Paths to CometChat files in this project.",
86
+ "additionalProperties": false,
87
+ "properties": {
88
+ "provider": {
89
+ "type": "string",
90
+ "description": "Path to the CometChat provider component (e.g. \"src/providers/CometChatProvider.tsx\")."
91
+ },
92
+ "chatPage": {
93
+ "type": "string",
94
+ "description": "Path to the main chat page (e.g. \"app/messages/page.tsx\")."
95
+ },
96
+ "chatDrawer": {
97
+ "type": "string",
98
+ "description": "Path to the chat drawer component."
99
+ },
100
+ "chatModal": {
101
+ "type": "string",
102
+ "description": "Path to the chat modal component."
103
+ },
104
+ "cssImport": {
105
+ "type": "string",
106
+ "description": "Path where css-variables.css was imported."
107
+ }
108
+ }
109
+ },
110
+ "features": {
111
+ "type": "array",
112
+ "items": {
113
+ "type": "string"
114
+ },
115
+ "uniqueItems": true,
116
+ "description": "Features applied on top of the base integration (e.g. [\"instant-messaging\", \"media-sharing\"])."
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "$schema": "../../../schemas/template.schema.json",
3
+ "version": "1.0.0",
4
+ "experience_id": 1,
5
+ "framework": "reactjs-dedicated-route",
6
+ "sdk_compatibility": ">=6.0.0 <7.0.0",
7
+ "default_placement": "dedicated-route",
8
+ "deps": [
9
+ { "name": "@cometchat/chat-uikit-react", "version": "^6" },
10
+ { "name": "@cometchat/chat-sdk-javascript", "version": "^4" }
11
+ ],
12
+ "env_vars": [
13
+ { "name": "{{ENV_PREFIX}}COMETCHAT_APP_ID", "required": true, "description": "CometChat App ID from your dashboard" },
14
+ { "name": "{{ENV_PREFIX}}COMETCHAT_REGION", "required": true, "description": "CometChat region (us, eu, in, etc.)" },
15
+ { "name": "{{ENV_PREFIX}}COMETCHAT_AUTH_KEY", "required": true, "secret": true, "description": "CometChat Auth Key (development only — use auth tokens in production)" }
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "src/cometchat/CometChatSelector.tsx",
20
+ "action": "create",
21
+ "template_ref": "CometChatSelector.tsx.tpl",
22
+ "owned": true
23
+ },
24
+ {
25
+ "path": "src/cometchat/CometChatMessenger.tsx",
26
+ "action": "create",
27
+ "template_ref": "CometChatMessenger.tsx.tpl",
28
+ "owned": true
29
+ },
30
+ {
31
+ "path": "src/cometchat/CometChatMessenger.css",
32
+ "action": "create",
33
+ "template_ref": "CometChatMessenger.css.tpl",
34
+ "owned": true
35
+ },
36
+ {
37
+ "path": "src/pages/ChatPage.tsx",
38
+ "action": "create",
39
+ "template_ref": "ChatPage.tsx.tpl",
40
+ "owned": true,
41
+ "is_entry": true
42
+ },
43
+ {
44
+ "path": "src/App.tsx",
45
+ "action": "patch",
46
+ "patch_mode": "prepend",
47
+ "patch_content": "import ChatPage from \"./pages/ChatPage\";\n",
48
+ "skip_if_exists": "./pages/ChatPage",
49
+ "embed_skip": true
50
+ },
51
+ {
52
+ "path": "src/App.tsx",
53
+ "action": "patch",
54
+ "patch_mode": "insert-before",
55
+ "anchor": "</Routes>",
56
+ "patch_content": " <Route path=\"{{PLACEMENT_PATH}}\" element={<ChatPage />} />\n ",
57
+ "skip_if_exists": "element={<ChatPage />}",
58
+ "embed_skip": true
59
+ },
60
+ {
61
+ "path": "src/index.css",
62
+ "action": "patch",
63
+ "patch_mode": "prepend",
64
+ "patch_content": "@import url(\"@cometchat/chat-uikit-react/css-variables.css\");\n",
65
+ "skip_if_exists": "@cometchat/chat-uikit-react/css-variables.css"
66
+ }
67
+ ],
68
+ "verify_rules": [
69
+ "css_variables_imported_once",
70
+ "init_before_login",
71
+ "no_auth_key_in_source",
72
+ "render_gated_on_login_resolve",
73
+ "error_ui_visible_on_failure"
74
+ ],
75
+ "next_steps": [
76
+ "Run `npm run dev` to start the dev server",
77
+ "Visit http://localhost:5173{{PLACEMENT_PATH}}",
78
+ "Click a conversation in the left panel to open it",
79
+ "If the route didn't wire up automatically (your App.tsx layout is non-standard), add: <Route path=\"{{PLACEMENT_PATH}}\" element={<ChatPage />} /> inside your existing <Routes> block and import ChatPage from ./pages/ChatPage"
80
+ ]
81
+ }
@@ -0,0 +1,87 @@
1
+ import { lazy, Suspense, useEffect, useState } from "react";
2
+ import {
3
+ CometChatUIKit,
4
+ UIKitSettingsBuilder,
5
+ } from "@cometchat/chat-uikit-react";
6
+
7
+ const CometChatMessenger = lazy(
8
+ () => import("../cometchat/CometChatMessenger")
9
+ );
10
+
11
+ const COMETCHAT_CONSTANTS = {
12
+ APP_ID: import.meta.env.{{ENV_PREFIX}}COMETCHAT_APP_ID as string,
13
+ REGION: import.meta.env.{{ENV_PREFIX}}COMETCHAT_REGION as string,
14
+ AUTH_KEY: import.meta.env.{{ENV_PREFIX}}COMETCHAT_AUTH_KEY as string,
15
+ };
16
+
17
+ const settings = new UIKitSettingsBuilder()
18
+ .setAppId(COMETCHAT_CONSTANTS.APP_ID)
19
+ .setRegion(COMETCHAT_CONSTANTS.REGION)
20
+ .setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY)
21
+ .subscribePresenceForAllUsers()
22
+ .build();
23
+
24
+ // Module-level promise so init + login only runs once, even if the page
25
+ // remounts (route changes, StrictMode double-render). Subsequent mounts
26
+ // await the same resolved promise.
27
+ let initPromise: Promise<void> | null = null;
28
+ function ensureInitialized(): Promise<void> {
29
+ if (initPromise) return initPromise;
30
+ initPromise = CometChatUIKit.init(settings)
31
+ .then(() => CometChatUIKit.getLoggedinUser())
32
+ .then((user) => {
33
+ if (user) return undefined;
34
+ return CometChatUIKit.login("{{LOGIN_UID}}").then(() => undefined);
35
+ })
36
+ .then(() => undefined);
37
+ return initPromise;
38
+ }
39
+
40
+ export default function ChatPage() {
41
+ const [ready, setReady] = useState(false);
42
+ const [error, setError] = useState<string | null>(null);
43
+
44
+ useEffect(() => {
45
+ let cancelled = false;
46
+ ensureInitialized()
47
+ .then(() => {
48
+ if (!cancelled) setReady(true);
49
+ })
50
+ .catch((e: unknown) => {
51
+ if (!cancelled) setError(String(e));
52
+ });
53
+ return () => {
54
+ cancelled = true;
55
+ };
56
+ }, []);
57
+
58
+ if (error) {
59
+ return (
60
+ <div
61
+ className="cometchat-page"
62
+ style={{
63
+ color: "red",
64
+ padding: 16,
65
+ fontFamily: "monospace",
66
+ }}
67
+ >
68
+ CometChat failed to load: {error}
69
+ </div>
70
+ );
71
+ }
72
+ if (!ready) {
73
+ return (
74
+ <div className="cometchat-page" style={{ padding: 16 }}>
75
+ Loading chat…
76
+ </div>
77
+ );
78
+ }
79
+
80
+ return (
81
+ <div className="cometchat-page">
82
+ <Suspense fallback={<div style={{ padding: 16 }}>Loading chat…</div>}>
83
+ <CometChatMessenger />
84
+ </Suspense>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,20 @@
1
+ /* CometChat layout — IMPORTANT: read this before customizing.
2
+ *
3
+ * The CometChat React UI Kit v6 wraps every component in a `<div class="cometchat">`
4
+ * which does NOT set `height: 100%`. CSS flex with `flex: 1` alone WILL NOT propagate
5
+ * height through this wrapper, so the message list (which uses `overflow: hidden auto`
6
+ * + `height: 100%` internally) collapses to content size — disabling scroll and
7
+ * showing messages from the top instead of bottom-anchored.
8
+ *
9
+ * This stylesheet scopes the layout to `.cometchat-page` (the root div in
10
+ * ChatPage.tsx) so it doesn't leak into the rest of your app's pages.
11
+ */
12
+ .cometchat-page { width: 100vw; height: 100vh; }
13
+ .conversations-with-messages { display: flex; height: 100%; width: 100%; }
14
+ .conversations-wrapper { height: 100vh; width: 480px; overflow: hidden; display: flex; flex-direction: column; flex-shrink: 0; }
15
+ .conversations-wrapper > .cometchat { overflow: hidden; }
16
+ .messages-wrapper { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; }
17
+ .empty-conversation { height: 100vh; width: 100%; display: flex; justify-content: center; align-items: center; background: var(--cometchat-background-color-03, #F5F5F5); color: var(--cometchat-text-color-secondary, #727272); font: var(--cometchat-font-body-regular, 400 14px Roboto); }
18
+ .cometchat .cometchat-message-composer { border-radius: 0; }
19
+ /* Side panel — used by details, search, threaded messages. Ready to use in Phase B. */
20
+ .side-component-wrapper { display: flex; width: 30%; max-width: 420px; flex-direction: column; flex-shrink: 0; background: var(--cometchat-background-color-01, #FFF); border-left: 1px solid var(--cometchat-border-color-light, #F5F5F5); height: 100%; overflow: hidden; }
@@ -0,0 +1,54 @@
1
+ import { useState } from "react";
2
+ import {
3
+ CometChatMessageComposer,
4
+ CometChatMessageHeader,
5
+ CometChatMessageList,
6
+ } from "@cometchat/chat-uikit-react";
7
+ import { CometChat } from "@cometchat/chat-sdk-javascript";
8
+ import { CometChatSelector } from "./CometChatSelector";
9
+ import "./CometChatMessenger.css";
10
+
11
+ export default function CometChatMessenger() {
12
+ const [selectedUser, setSelectedUser] = useState<
13
+ CometChat.User | undefined
14
+ >();
15
+ const [selectedGroup, setSelectedGroup] = useState<
16
+ CometChat.Group | undefined
17
+ >();
18
+
19
+ return (
20
+ <div className="conversations-with-messages">
21
+ <div className="conversations-wrapper">
22
+ <CometChatSelector
23
+ onSelectorItemClicked={(activeItem) => {
24
+ const item =
25
+ activeItem instanceof CometChat.Conversation
26
+ ? activeItem.getConversationWith()
27
+ : activeItem;
28
+ if (item instanceof CometChat.User) {
29
+ setSelectedUser(item);
30
+ setSelectedGroup(undefined);
31
+ } else if (item instanceof CometChat.Group) {
32
+ setSelectedUser(undefined);
33
+ setSelectedGroup(item);
34
+ }
35
+ }}
36
+ />
37
+ </div>
38
+ {selectedUser || selectedGroup ? (
39
+ <div className="messages-wrapper">
40
+ {selectedUser && <CometChatMessageHeader user={selectedUser} />}
41
+ {selectedGroup && <CometChatMessageHeader group={selectedGroup} />}
42
+ {selectedUser && <CometChatMessageList user={selectedUser} />}
43
+ {selectedGroup && <CometChatMessageList group={selectedGroup} />}
44
+ {selectedUser && <CometChatMessageComposer user={selectedUser} />}
45
+ {selectedGroup && <CometChatMessageComposer group={selectedGroup} />}
46
+ </div>
47
+ ) : (
48
+ <div className="empty-conversation">
49
+ Select a conversation to start chatting
50
+ </div>
51
+ )}
52
+ </div>
53
+ );
54
+ }
@@ -0,0 +1,40 @@
1
+ import { useEffect, useState } from "react";
2
+ import { CometChat } from "@cometchat/chat-sdk-javascript";
3
+ import {
4
+ CometChatConversations,
5
+ CometChatUIKitLoginListener,
6
+ } from "@cometchat/chat-uikit-react";
7
+
8
+ interface SelectorProps {
9
+ onSelectorItemClicked?: (
10
+ input: CometChat.Conversation | CometChat.User | CometChat.Group,
11
+ type: string
12
+ ) => void;
13
+ }
14
+
15
+ export const CometChatSelector = ({
16
+ onSelectorItemClicked = () => {},
17
+ }: SelectorProps) => {
18
+ const [loggedInUser, setLoggedInUser] = useState<CometChat.User | null>(null);
19
+ const [activeConversation, setActiveConversation] = useState<
20
+ CometChat.Conversation | undefined
21
+ >();
22
+
23
+ useEffect(() => {
24
+ setLoggedInUser(CometChatUIKitLoginListener.getLoggedInUser());
25
+ }, []);
26
+
27
+ return (
28
+ <>
29
+ {loggedInUser && (
30
+ <CometChatConversations
31
+ activeConversation={activeConversation}
32
+ onItemClick={(e) => {
33
+ setActiveConversation(e);
34
+ onSelectorItemClicked(e, "updateSelectedItem");
35
+ }}
36
+ />
37
+ )}
38
+ </>
39
+ );
40
+ };
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "../../../schemas/template.schema.json",
3
+ "version": "1.0.0",
4
+ "experience_id": 2,
5
+ "framework": "reactjs-dedicated-route",
6
+ "sdk_compatibility": ">=6.0.0 <7.0.0",
7
+ "default_placement": "dedicated-route",
8
+ "deps": [
9
+ { "name": "@cometchat/chat-uikit-react", "version": "^6" },
10
+ { "name": "@cometchat/chat-sdk-javascript", "version": "^4" }
11
+ ],
12
+ "env_vars": [
13
+ { "name": "{{ENV_PREFIX}}COMETCHAT_APP_ID", "required": true, "description": "CometChat App ID from your dashboard" },
14
+ { "name": "{{ENV_PREFIX}}COMETCHAT_REGION", "required": true, "description": "CometChat region (us, eu, in, etc.)" },
15
+ { "name": "{{ENV_PREFIX}}COMETCHAT_AUTH_KEY", "required": true, "secret": true, "description": "CometChat Auth Key (development only — use auth tokens in production)" }
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "src/cometchat/CometChatMessenger.tsx",
20
+ "action": "create",
21
+ "template_ref": "CometChatMessenger.tsx.tpl",
22
+ "owned": true
23
+ },
24
+ {
25
+ "path": "src/cometchat/CometChatMessenger.css",
26
+ "action": "create",
27
+ "template_ref": "CometChatMessenger.css.tpl",
28
+ "owned": true
29
+ },
30
+ {
31
+ "path": "src/pages/ChatPage.tsx",
32
+ "action": "create",
33
+ "template_ref": "ChatPage.tsx.tpl",
34
+ "owned": true,
35
+ "is_entry": true
36
+ },
37
+ {
38
+ "path": "src/App.tsx",
39
+ "action": "patch",
40
+ "patch_mode": "prepend",
41
+ "patch_content": "import ChatPage from \"./pages/ChatPage\";\n",
42
+ "skip_if_exists": "./pages/ChatPage",
43
+ "embed_skip": true
44
+ },
45
+ {
46
+ "path": "src/App.tsx",
47
+ "action": "patch",
48
+ "patch_mode": "insert-before",
49
+ "anchor": "</Routes>",
50
+ "patch_content": " <Route path=\"{{PLACEMENT_PATH}}\" element={<ChatPage />} />\n ",
51
+ "skip_if_exists": "element={<ChatPage />}",
52
+ "embed_skip": true
53
+ },
54
+ {
55
+ "path": "src/index.css",
56
+ "action": "patch",
57
+ "patch_mode": "prepend",
58
+ "patch_content": "@import url(\"@cometchat/chat-uikit-react/css-variables.css\");\n",
59
+ "skip_if_exists": "@cometchat/chat-uikit-react/css-variables.css"
60
+ }
61
+ ],
62
+ "verify_rules": [
63
+ "css_variables_imported_once",
64
+ "init_before_login",
65
+ "no_auth_key_in_source",
66
+ "render_gated_on_login_resolve",
67
+ "error_ui_visible_on_failure"
68
+ ],
69
+ "next_steps": [
70
+ "Run `npm run dev` to start the dev server",
71
+ "Visit http://localhost:5173{{PLACEMENT_PATH}} — you'll see a one-to-one chat with `{{CHAT_UID}}`",
72
+ "To change the chat partner, edit `CHAT_UID` in `src/cometchat/CometChatMessenger.tsx`",
73
+ "If the route didn't wire up automatically (your App.tsx layout is non-standard), add: <Route path=\"{{PLACEMENT_PATH}}\" element={<ChatPage />} /> inside your existing <Routes> block and import ChatPage from ./pages/ChatPage"
74
+ ]
75
+ }
@@ -38,19 +38,22 @@
38
38
  "path": "src/main.tsx",
39
39
  "action": "create",
40
40
  "template_ref": "main.tsx.tpl",
41
- "owned": true
41
+ "owned": true,
42
+ "embed_skip": true
42
43
  },
43
44
  {
44
45
  "path": "src/App.tsx",
45
46
  "action": "create",
46
47
  "template_ref": "App.tsx.tpl",
47
- "owned": true
48
+ "owned": true,
49
+ "is_entry": true
48
50
  },
49
51
  {
50
52
  "path": "src/App.css",
51
53
  "action": "create",
52
54
  "template_ref": "App.css.tpl",
53
- "owned": true
55
+ "owned": true,
56
+ "move_with_entry": true
54
57
  },
55
58
  {
56
59
  "path": "src/index.css",
@@ -0,0 +1,84 @@
1
+ import { lazy, Suspense, useEffect, useState } from "react";
2
+ import {
3
+ CometChatUIKit,
4
+ UIKitSettingsBuilder,
5
+ } from "@cometchat/chat-uikit-react";
6
+
7
+ const CometChatMessenger = lazy(
8
+ () => import("../cometchat/CometChatMessenger")
9
+ );
10
+
11
+ const COMETCHAT_CONSTANTS = {
12
+ APP_ID: import.meta.env.{{ENV_PREFIX}}COMETCHAT_APP_ID as string,
13
+ REGION: import.meta.env.{{ENV_PREFIX}}COMETCHAT_REGION as string,
14
+ AUTH_KEY: import.meta.env.{{ENV_PREFIX}}COMETCHAT_AUTH_KEY as string,
15
+ };
16
+
17
+ const settings = new UIKitSettingsBuilder()
18
+ .setAppId(COMETCHAT_CONSTANTS.APP_ID)
19
+ .setRegion(COMETCHAT_CONSTANTS.REGION)
20
+ .setAuthKey(COMETCHAT_CONSTANTS.AUTH_KEY)
21
+ .subscribePresenceForAllUsers()
22
+ .build();
23
+
24
+ let initPromise: Promise<void> | null = null;
25
+ function ensureInitialized(): Promise<void> {
26
+ if (initPromise) return initPromise;
27
+ initPromise = CometChatUIKit.init(settings)
28
+ .then(() => CometChatUIKit.getLoggedinUser())
29
+ .then((user) => {
30
+ if (user) return undefined;
31
+ return CometChatUIKit.login("{{LOGIN_UID}}").then(() => undefined);
32
+ })
33
+ .then(() => undefined);
34
+ return initPromise;
35
+ }
36
+
37
+ export default function ChatPage() {
38
+ const [ready, setReady] = useState(false);
39
+ const [error, setError] = useState<string | null>(null);
40
+
41
+ useEffect(() => {
42
+ let cancelled = false;
43
+ ensureInitialized()
44
+ .then(() => {
45
+ if (!cancelled) setReady(true);
46
+ })
47
+ .catch((e: unknown) => {
48
+ if (!cancelled) setError(String(e));
49
+ });
50
+ return () => {
51
+ cancelled = true;
52
+ };
53
+ }, []);
54
+
55
+ if (error) {
56
+ return (
57
+ <div
58
+ className="cometchat-page"
59
+ style={{
60
+ color: "red",
61
+ padding: 16,
62
+ fontFamily: "monospace",
63
+ }}
64
+ >
65
+ CometChat failed to load: {error}
66
+ </div>
67
+ );
68
+ }
69
+ if (!ready) {
70
+ return (
71
+ <div className="cometchat-page" style={{ padding: 16 }}>
72
+ Loading chat…
73
+ </div>
74
+ );
75
+ }
76
+
77
+ return (
78
+ <div className="cometchat-page">
79
+ <Suspense fallback={<div style={{ padding: 16 }}>Loading chat…</div>}>
80
+ <CometChatMessenger />
81
+ </Suspense>
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,9 @@
1
+ /* Scoped to .cometchat-page (root div in ChatPage.tsx) so the fixed-height
2
+ * chat layout doesn't leak into other routes. See the exp 1 dedicated-route
3
+ * CSS file for the full explanation of why the message list needs resolved
4
+ * heights on every ancestor.
5
+ */
6
+ .cometchat-page { width: 100%; height: 100vh; }
7
+ .messages-wrapper { height: 100%; width: 100%; display: flex; flex-direction: column; }
8
+ .empty-conversation { height: 100vh; width: 100%; display: flex; justify-content: center; align-items: center; background: var(--cometchat-background-color-03, #F5F5F5); color: var(--cometchat-text-color-secondary, #727272); font: var(--cometchat-font-body-regular, 400 14px Roboto); }
9
+ .cometchat .cometchat-message-composer { border-radius: 0; }
@@ -0,0 +1,43 @@
1
+ import { useEffect, useState } from "react";
2
+ import {
3
+ CometChatMessageComposer,
4
+ CometChatMessageHeader,
5
+ CometChatMessageList,
6
+ } from "@cometchat/chat-uikit-react";
7
+ import { CometChat } from "@cometchat/chat-sdk-javascript";
8
+ import "./CometChatMessenger.css";
9
+
10
+ // The hardcoded user this chat connects to. Change to point at any
11
+ // CometChat UID created in your dashboard.
12
+ const CHAT_UID = "{{CHAT_UID}}";
13
+
14
+ export default function CometChatMessenger() {
15
+ const [chatUser, setChatUser] = useState<CometChat.User | undefined>();
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ useEffect(() => {
19
+ CometChat.getUser(CHAT_UID)
20
+ .then((user) => setChatUser(user))
21
+ .catch((e: unknown) => setError(String(e)));
22
+ }, []);
23
+
24
+ if (error) {
25
+ return (
26
+ <div style={{ color: "red", padding: 16, fontFamily: "monospace" }}>
27
+ {error}
28
+ </div>
29
+ );
30
+ }
31
+
32
+ if (!chatUser) {
33
+ return <div className="empty-conversation">Loading chat…</div>;
34
+ }
35
+
36
+ return (
37
+ <div className="messages-wrapper">
38
+ <CometChatMessageHeader user={chatUser} />
39
+ <CometChatMessageList user={chatUser} />
40
+ <CometChatMessageComposer user={chatUser} />
41
+ </div>
42
+ );
43
+ }