@snapie/chat-client 0.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.
package/dist/react.js ADDED
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/react/index.tsx
7
+ var ChatContext = react.createContext(null);
8
+ function ChatProvider({ client, children }) {
9
+ return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: client, children });
10
+ }
11
+ function useChatClient() {
12
+ const client = react.useContext(ChatContext);
13
+ if (!client) throw new Error("useChatClient must be used inside <ChatProvider>");
14
+ return client;
15
+ }
16
+ function useConversations(clientOverride) {
17
+ const client = clientOverride ?? useChatClient();
18
+ const [conversations, setConversations] = react.useState([]);
19
+ const [loading, setLoading] = react.useState(true);
20
+ react.useEffect(() => {
21
+ let first = true;
22
+ const unsub = client.subscribeToConversations((convs) => {
23
+ setConversations(convs);
24
+ if (first) {
25
+ setLoading(false);
26
+ first = false;
27
+ }
28
+ });
29
+ return unsub;
30
+ }, [client]);
31
+ return { conversations, loading };
32
+ }
33
+ function useChatMessages(conversationId, type, clientOverride) {
34
+ const client = clientOverride ?? useChatClient();
35
+ const [messages, setMessages] = react.useState([]);
36
+ const [loading, setLoading] = react.useState(false);
37
+ const [error, setError] = react.useState(null);
38
+ react.useEffect(() => {
39
+ if (!conversationId || !client.isAuthenticated()) return;
40
+ setLoading(true);
41
+ setMessages([]);
42
+ setError(null);
43
+ let first = true;
44
+ const unsub = client.subscribeToMessages(conversationId, type, (msgs) => {
45
+ setMessages(msgs);
46
+ if (first) {
47
+ setLoading(false);
48
+ first = false;
49
+ }
50
+ });
51
+ return unsub;
52
+ }, [client, conversationId, type]);
53
+ const sendMessage = react.useCallback(
54
+ async (content, replyTo) => {
55
+ if (!conversationId) return;
56
+ try {
57
+ const { message } = await client.sendMessage(conversationId, type, content, replyTo);
58
+ setMessages((prev) => [...prev, message]);
59
+ } catch (e) {
60
+ setError(e instanceof Error ? e.message : "Send failed");
61
+ }
62
+ },
63
+ [client, conversationId, type]
64
+ );
65
+ const editMessage = react.useCallback(
66
+ async (messageId, content) => {
67
+ if (!conversationId) return;
68
+ const updated = await client.editMessage(conversationId, type, messageId, content);
69
+ setMessages((prev) => prev.map((m) => m._id === updated._id ? updated : m));
70
+ },
71
+ [client, conversationId, type]
72
+ );
73
+ return { messages, loading, error, sendMessage, editMessage };
74
+ }
75
+ function useUnreadCount(clientOverride) {
76
+ const client = clientOverride ?? useChatClient();
77
+ const [unreadCount, setUnreadCount] = react.useState(0);
78
+ react.useEffect(() => {
79
+ if (!client.isAuthenticated()) return;
80
+ const unsub = client.subscribeToUnreadCount(setUnreadCount);
81
+ return unsub;
82
+ }, [client]);
83
+ return { unreadCount };
84
+ }
85
+ function useTyping(conversationId, clientOverride) {
86
+ const client = clientOverride ?? useChatClient();
87
+ const [typingUsers, setTypingUsers] = react.useState([]);
88
+ const timerRef = react.useRef(null);
89
+ react.useEffect(() => {
90
+ if (!conversationId) return;
91
+ const interval = setInterval(async () => {
92
+ try {
93
+ const info = await client.getTyping(conversationId);
94
+ setTypingUsers(info.users);
95
+ } catch {
96
+ }
97
+ }, 3e3);
98
+ return () => clearInterval(interval);
99
+ }, [client, conversationId]);
100
+ const setTyping = react.useCallback(
101
+ (isTyping) => {
102
+ if (!conversationId) return;
103
+ client.setTyping(conversationId, isTyping).catch(() => {
104
+ });
105
+ if (isTyping) {
106
+ if (timerRef.current) clearTimeout(timerRef.current);
107
+ timerRef.current = setTimeout(() => {
108
+ client.setTyping(conversationId, false).catch(() => {
109
+ });
110
+ }, 5e3);
111
+ }
112
+ },
113
+ [client, conversationId]
114
+ );
115
+ return { typingUsers, setTyping };
116
+ }
117
+
118
+ exports.ChatProvider = ChatProvider;
119
+ exports.useChatMessages = useChatMessages;
120
+ exports.useConversations = useConversations;
121
+ exports.useTyping = useTyping;
122
+ exports.useUnreadCount = useUnreadCount;
123
+ //# sourceMappingURL=react.js.map
124
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/index.tsx"],"names":["createContext","useContext","useState","useEffect","useCallback","useRef"],"mappings":"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","file":"react.js","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { ChatClient } from '../client';\nimport type { Conversation, Message, TypingStatusInfo } from '../types';\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\nconst ChatContext = createContext<ChatClient | null>(null);\n\ninterface ChatProviderProps {\n client: ChatClient;\n children: ReactNode;\n}\n\n/** Wrap your app (or the chat section) with this to access hooks without prop-drilling. */\nexport function ChatProvider({ client, children }: ChatProviderProps) {\n return <ChatContext.Provider value={client}>{children}</ChatContext.Provider>;\n}\n\nfunction useChatClient(): ChatClient {\n const client = useContext(ChatContext);\n if (!client) throw new Error('useChatClient must be used inside <ChatProvider>');\n return client;\n}\n\n// ── Hooks ─────────────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to the live conversations list.\n *\n * @example\n * const { conversations, loading } = useConversations();\n */\nexport function useConversations(clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [conversations, setConversations] = useState<Conversation[]>([]);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let first = true;\n const unsub = client.subscribeToConversations((convs) => {\n setConversations(convs);\n if (first) { setLoading(false); first = false; }\n });\n return unsub;\n }, [client]);\n\n return { conversations, loading };\n}\n\n/**\n * Subscribe to live messages for a conversation.\n * Handles initial load + polling automatically.\n *\n * @example\n * const { messages, loading, sendMessage } = useChatMessages(conv._id, conv.type);\n */\nexport function useChatMessages(\n conversationId: string | null,\n type: 'channel' | 'dm' | 'group',\n clientOverride?: ChatClient\n) {\n const client = clientOverride ?? useChatClient();\n const [messages, setMessages] = useState<Message[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!conversationId || !client.isAuthenticated()) return;\n setLoading(true);\n setMessages([]);\n setError(null);\n\n let first = true;\n const unsub = client.subscribeToMessages(conversationId, type, (msgs) => {\n setMessages(msgs);\n if (first) { setLoading(false); first = false; }\n });\n\n return unsub;\n }, [client, conversationId, type]);\n\n const sendMessage = useCallback(\n async (content: string, replyTo?: string) => {\n if (!conversationId) return;\n try {\n const { message } = await client.sendMessage(conversationId, type, content, replyTo);\n setMessages(prev => [...prev, message]);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Send failed');\n }\n },\n [client, conversationId, type]\n );\n\n const editMessage = useCallback(\n async (messageId: string, content: string) => {\n if (!conversationId) return;\n const updated = await client.editMessage(conversationId, type, messageId, content);\n setMessages(prev => prev.map(m => m._id === updated._id ? updated : m));\n },\n [client, conversationId, type]\n );\n\n return { messages, loading, error, sendMessage, editMessage };\n}\n\n/**\n * Subscribe to the unread message count badge.\n *\n * @example\n * const { unreadCount } = useUnreadCount();\n */\nexport function useUnreadCount(clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [unreadCount, setUnreadCount] = useState(0);\n\n useEffect(() => {\n if (!client.isAuthenticated()) return;\n const unsub = client.subscribeToUnreadCount(setUnreadCount);\n return unsub;\n }, [client]);\n\n return { unreadCount };\n}\n\n/**\n * Typing indicator for a conversation.\n * Returns who is typing and exposes `setTyping` to broadcast your own state.\n *\n * @example\n * const { typingUsers, setTyping } = useTyping(conv._id);\n */\nexport function useTyping(conversationId: string | null, clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [typingUsers, setTypingUsers] = useState<string[]>([]);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!conversationId) return;\n const interval = setInterval(async () => {\n try {\n const info: TypingStatusInfo = await client.getTyping(conversationId);\n setTypingUsers(info.users);\n } catch { /* */ }\n }, 3000);\n return () => clearInterval(interval);\n }, [client, conversationId]);\n\n const setTyping = useCallback(\n (isTyping: boolean) => {\n if (!conversationId) return;\n client.setTyping(conversationId, isTyping).catch(() => { /* */ });\n\n // Auto-clear after 5s so we don't leave stale typing state\n if (isTyping) {\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n client.setTyping(conversationId, false).catch(() => { /* */ });\n }, 5000);\n }\n },\n [client, conversationId]\n );\n\n return { typingUsers, setTyping };\n}\n\n// Re-export client type so consumers don't need a second import\nexport type { ChatClient };\n"]}
package/dist/react.mjs ADDED
@@ -0,0 +1,118 @@
1
+ import { createContext, useState, useEffect, useCallback, useRef, useContext } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/react/index.tsx
5
+ var ChatContext = createContext(null);
6
+ function ChatProvider({ client, children }) {
7
+ return /* @__PURE__ */ jsx(ChatContext.Provider, { value: client, children });
8
+ }
9
+ function useChatClient() {
10
+ const client = useContext(ChatContext);
11
+ if (!client) throw new Error("useChatClient must be used inside <ChatProvider>");
12
+ return client;
13
+ }
14
+ function useConversations(clientOverride) {
15
+ const client = clientOverride ?? useChatClient();
16
+ const [conversations, setConversations] = useState([]);
17
+ const [loading, setLoading] = useState(true);
18
+ useEffect(() => {
19
+ let first = true;
20
+ const unsub = client.subscribeToConversations((convs) => {
21
+ setConversations(convs);
22
+ if (first) {
23
+ setLoading(false);
24
+ first = false;
25
+ }
26
+ });
27
+ return unsub;
28
+ }, [client]);
29
+ return { conversations, loading };
30
+ }
31
+ function useChatMessages(conversationId, type, clientOverride) {
32
+ const client = clientOverride ?? useChatClient();
33
+ const [messages, setMessages] = useState([]);
34
+ const [loading, setLoading] = useState(false);
35
+ const [error, setError] = useState(null);
36
+ useEffect(() => {
37
+ if (!conversationId || !client.isAuthenticated()) return;
38
+ setLoading(true);
39
+ setMessages([]);
40
+ setError(null);
41
+ let first = true;
42
+ const unsub = client.subscribeToMessages(conversationId, type, (msgs) => {
43
+ setMessages(msgs);
44
+ if (first) {
45
+ setLoading(false);
46
+ first = false;
47
+ }
48
+ });
49
+ return unsub;
50
+ }, [client, conversationId, type]);
51
+ const sendMessage = useCallback(
52
+ async (content, replyTo) => {
53
+ if (!conversationId) return;
54
+ try {
55
+ const { message } = await client.sendMessage(conversationId, type, content, replyTo);
56
+ setMessages((prev) => [...prev, message]);
57
+ } catch (e) {
58
+ setError(e instanceof Error ? e.message : "Send failed");
59
+ }
60
+ },
61
+ [client, conversationId, type]
62
+ );
63
+ const editMessage = useCallback(
64
+ async (messageId, content) => {
65
+ if (!conversationId) return;
66
+ const updated = await client.editMessage(conversationId, type, messageId, content);
67
+ setMessages((prev) => prev.map((m) => m._id === updated._id ? updated : m));
68
+ },
69
+ [client, conversationId, type]
70
+ );
71
+ return { messages, loading, error, sendMessage, editMessage };
72
+ }
73
+ function useUnreadCount(clientOverride) {
74
+ const client = clientOverride ?? useChatClient();
75
+ const [unreadCount, setUnreadCount] = useState(0);
76
+ useEffect(() => {
77
+ if (!client.isAuthenticated()) return;
78
+ const unsub = client.subscribeToUnreadCount(setUnreadCount);
79
+ return unsub;
80
+ }, [client]);
81
+ return { unreadCount };
82
+ }
83
+ function useTyping(conversationId, clientOverride) {
84
+ const client = clientOverride ?? useChatClient();
85
+ const [typingUsers, setTypingUsers] = useState([]);
86
+ const timerRef = useRef(null);
87
+ useEffect(() => {
88
+ if (!conversationId) return;
89
+ const interval = setInterval(async () => {
90
+ try {
91
+ const info = await client.getTyping(conversationId);
92
+ setTypingUsers(info.users);
93
+ } catch {
94
+ }
95
+ }, 3e3);
96
+ return () => clearInterval(interval);
97
+ }, [client, conversationId]);
98
+ const setTyping = useCallback(
99
+ (isTyping) => {
100
+ if (!conversationId) return;
101
+ client.setTyping(conversationId, isTyping).catch(() => {
102
+ });
103
+ if (isTyping) {
104
+ if (timerRef.current) clearTimeout(timerRef.current);
105
+ timerRef.current = setTimeout(() => {
106
+ client.setTyping(conversationId, false).catch(() => {
107
+ });
108
+ }, 5e3);
109
+ }
110
+ },
111
+ [client, conversationId]
112
+ );
113
+ return { typingUsers, setTyping };
114
+ }
115
+
116
+ export { ChatProvider, useChatMessages, useConversations, useTyping, useUnreadCount };
117
+ //# sourceMappingURL=react.mjs.map
118
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/index.tsx"],"names":[],"mappings":"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","file":"react.mjs","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { ChatClient } from '../client';\nimport type { Conversation, Message, TypingStatusInfo } from '../types';\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\nconst ChatContext = createContext<ChatClient | null>(null);\n\ninterface ChatProviderProps {\n client: ChatClient;\n children: ReactNode;\n}\n\n/** Wrap your app (or the chat section) with this to access hooks without prop-drilling. */\nexport function ChatProvider({ client, children }: ChatProviderProps) {\n return <ChatContext.Provider value={client}>{children}</ChatContext.Provider>;\n}\n\nfunction useChatClient(): ChatClient {\n const client = useContext(ChatContext);\n if (!client) throw new Error('useChatClient must be used inside <ChatProvider>');\n return client;\n}\n\n// ── Hooks ─────────────────────────────────────────────────────────────────────\n\n/**\n * Subscribe to the live conversations list.\n *\n * @example\n * const { conversations, loading } = useConversations();\n */\nexport function useConversations(clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [conversations, setConversations] = useState<Conversation[]>([]);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let first = true;\n const unsub = client.subscribeToConversations((convs) => {\n setConversations(convs);\n if (first) { setLoading(false); first = false; }\n });\n return unsub;\n }, [client]);\n\n return { conversations, loading };\n}\n\n/**\n * Subscribe to live messages for a conversation.\n * Handles initial load + polling automatically.\n *\n * @example\n * const { messages, loading, sendMessage } = useChatMessages(conv._id, conv.type);\n */\nexport function useChatMessages(\n conversationId: string | null,\n type: 'channel' | 'dm' | 'group',\n clientOverride?: ChatClient\n) {\n const client = clientOverride ?? useChatClient();\n const [messages, setMessages] = useState<Message[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!conversationId || !client.isAuthenticated()) return;\n setLoading(true);\n setMessages([]);\n setError(null);\n\n let first = true;\n const unsub = client.subscribeToMessages(conversationId, type, (msgs) => {\n setMessages(msgs);\n if (first) { setLoading(false); first = false; }\n });\n\n return unsub;\n }, [client, conversationId, type]);\n\n const sendMessage = useCallback(\n async (content: string, replyTo?: string) => {\n if (!conversationId) return;\n try {\n const { message } = await client.sendMessage(conversationId, type, content, replyTo);\n setMessages(prev => [...prev, message]);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Send failed');\n }\n },\n [client, conversationId, type]\n );\n\n const editMessage = useCallback(\n async (messageId: string, content: string) => {\n if (!conversationId) return;\n const updated = await client.editMessage(conversationId, type, messageId, content);\n setMessages(prev => prev.map(m => m._id === updated._id ? updated : m));\n },\n [client, conversationId, type]\n );\n\n return { messages, loading, error, sendMessage, editMessage };\n}\n\n/**\n * Subscribe to the unread message count badge.\n *\n * @example\n * const { unreadCount } = useUnreadCount();\n */\nexport function useUnreadCount(clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [unreadCount, setUnreadCount] = useState(0);\n\n useEffect(() => {\n if (!client.isAuthenticated()) return;\n const unsub = client.subscribeToUnreadCount(setUnreadCount);\n return unsub;\n }, [client]);\n\n return { unreadCount };\n}\n\n/**\n * Typing indicator for a conversation.\n * Returns who is typing and exposes `setTyping` to broadcast your own state.\n *\n * @example\n * const { typingUsers, setTyping } = useTyping(conv._id);\n */\nexport function useTyping(conversationId: string | null, clientOverride?: ChatClient) {\n const client = clientOverride ?? useChatClient();\n const [typingUsers, setTypingUsers] = useState<string[]>([]);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!conversationId) return;\n const interval = setInterval(async () => {\n try {\n const info: TypingStatusInfo = await client.getTyping(conversationId);\n setTypingUsers(info.users);\n } catch { /* */ }\n }, 3000);\n return () => clearInterval(interval);\n }, [client, conversationId]);\n\n const setTyping = useCallback(\n (isTyping: boolean) => {\n if (!conversationId) return;\n client.setTyping(conversationId, isTyping).catch(() => { /* */ });\n\n // Auto-clear after 5s so we don't leave stale typing state\n if (isTyping) {\n if (timerRef.current) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n client.setTyping(conversationId, false).catch(() => { /* */ });\n }, 5000);\n }\n },\n [client, conversationId]\n );\n\n return { typingUsers, setTyping };\n}\n\n// Re-export client type so consumers don't need a second import\nexport type { ChatClient };\n"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@snapie/chat-client",
3
+ "version": "0.1.0",
4
+ "description": "Hive-authenticated chat client SDK for Snapie — DMs, channels, groups, and real-time subscriptions",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./react": {
15
+ "types": "./dist/react.d.ts",
16
+ "import": "./dist/react.mjs",
17
+ "require": "./dist/react.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "keywords": [
29
+ "hive",
30
+ "blockchain",
31
+ "chat",
32
+ "sdk",
33
+ "snapie",
34
+ "messaging",
35
+ "dm",
36
+ "channels"
37
+ ],
38
+ "author": "Mantequilla-Soft",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/Mantequilla-Soft/snapie-io"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^18.0.0",
49
+ "react-dom": "^18.0.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "react": {
53
+ "optional": true
54
+ },
55
+ "react-dom": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "@types/react": "^18",
61
+ "tsup": "^8.5.1",
62
+ "typescript": "^5"
63
+ }
64
+ }