@cometchat/skills-cli 2.0.4 → 2.2.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/LICENSE +21 -0
- package/bin/cometchat.mjs +12 -6
- package/dist/index.js +5512 -515
- package/dist/registry/schemas/config.schema.json +119 -0
- package/dist/registry/v6/experiences/1-conversation-list/reactjs-dedicated-route.template.json +81 -0
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +87 -0
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +20 -0
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +54 -0
- package/dist/registry/v6/experiences/1-conversation-list/templates/reactjs-dedicated-route/CometChatSelector.tsx.tpl +40 -0
- package/dist/registry/v6/experiences/2-one-to-one/reactjs-dedicated-route.template.json +75 -0
- package/dist/registry/v6/experiences/2-one-to-one/reactjs.template.json +6 -3
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +84 -0
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +9 -0
- package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +43 -0
- package/dist/registry/v6/experiences/3-tab-based/reactjs-dedicated-route.template.json +89 -0
- package/dist/registry/v6/experiences/3-tab-based/reactjs.template.json +6 -3
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/ChatPage.tsx.tpl +84 -0
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.css.tpl +11 -0
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatMessenger.tsx.tpl +61 -0
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.css.tpl +43 -0
- package/dist/registry/v6/experiences/3-tab-based/templates/reactjs-dedicated-route/CometChatTabs.tsx.tpl +72 -0
- package/dist/registry/v6/features/catalog.json +9 -1
- package/dist/utils/error-dictionary.js +170 -0
- package/dist/utils/onboarding-options.js +30 -0
- 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
|
+
}
|
package/dist/registry/v6/experiences/1-conversation-list/reactjs-dedicated-route.template.json
ADDED
|
@@ -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",
|
package/dist/registry/v6/experiences/2-one-to-one/templates/reactjs-dedicated-route/ChatPage.tsx.tpl
ADDED
|
@@ -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
|
+
}
|