@triadxyz/widgets 0.0.8 → 0.0.9
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/index.d.mts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/LiveChat.tsx +65 -187
- package/src/hooks/useChat.ts +45 -18
package/dist/index.d.mts
CHANGED
|
@@ -20,6 +20,9 @@ interface LiveChatTheme {
|
|
|
20
20
|
primaryColor?: string;
|
|
21
21
|
background?: string;
|
|
22
22
|
textColor?: string;
|
|
23
|
+
buttonSend: {
|
|
24
|
+
color: string;
|
|
25
|
+
};
|
|
23
26
|
}
|
|
24
27
|
interface LiveChatClassNames {
|
|
25
28
|
container?: string;
|
|
@@ -44,6 +47,6 @@ interface LiveChatProps {
|
|
|
44
47
|
description: string;
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
|
-
declare function LiveChat({ isOpen, onClose, title, theme, classNames, renderMessage, renderHeader, authority, customerAuthority,
|
|
50
|
+
declare function LiveChat({ isOpen, onClose, title, theme, classNames, renderMessage, renderHeader, authority, customerAuthority, onConnectWallet, }: LiveChatProps): react_jsx_runtime.JSX.Element | null;
|
|
48
51
|
|
|
49
52
|
export { ButtonWidget, type ButtonWidgetProps, LiveChat, type LiveChatClassNames, type LiveChatProps, type LiveChatTheme };
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ interface LiveChatTheme {
|
|
|
20
20
|
primaryColor?: string;
|
|
21
21
|
background?: string;
|
|
22
22
|
textColor?: string;
|
|
23
|
+
buttonSend: {
|
|
24
|
+
color: string;
|
|
25
|
+
};
|
|
23
26
|
}
|
|
24
27
|
interface LiveChatClassNames {
|
|
25
28
|
container?: string;
|
|
@@ -44,6 +47,6 @@ interface LiveChatProps {
|
|
|
44
47
|
description: string;
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
|
-
declare function LiveChat({ isOpen, onClose, title, theme, classNames, renderMessage, renderHeader, authority, customerAuthority,
|
|
50
|
+
declare function LiveChat({ isOpen, onClose, title, theme, classNames, renderMessage, renderHeader, authority, customerAuthority, onConnectWallet, }: LiveChatProps): react_jsx_runtime.JSX.Element | null;
|
|
48
51
|
|
|
49
52
|
export { ButtonWidget, type ButtonWidgetProps, LiveChat, type LiveChatClassNames, type LiveChatProps, type LiveChatTheme };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
'use strict';var jsxRuntime=require('react/jsx-runtime'),
|
|
1
|
+
'use strict';var jsxRuntime=require('react/jsx-runtime'),z=require('react'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),T=require('socket.io-client'),dateFns=require('date-fns');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var z__default=/*#__PURE__*/_interopDefault(z);var T__default=/*#__PURE__*/_interopDefault(T);var W=({label:r,variant:h="primary",className:x="",...c})=>jsxRuntime.jsx("button",{className:`px-4 py-2 rounded-lg font-medium transition-colors ${{primary:"bg-blue-600 text-white hover:bg-blue-700",secondary:"bg-gray-200 text-gray-800 hover:bg-gray-300"}[h]} ${x}`,...c,children:r});function p(...r){return tailwindMerge.twMerge(clsx.clsx(r))}var D=T__default.default("https://beta.triadfi.co",{path:"/socket.io",reconnection:true,reconnectionAttempts:5,reconnectionDelay:1e3,randomizationFactor:.5,timeout:5e3,transports:["websocket"]}),a=D;function w({authority:r,customerAuthority:h}){let[x,c]=z.useState([]),[l,b]=z.useState(0),n=h?.trim();return z.useEffect(()=>{if(!n)return;let u=s=>{c(s);},m=s=>{c(f=>[...f,s]);},o=s=>{b(s.count);},g=()=>{let s=r?{authority:r,customerAuthority:n}:{customerAuthority:n};a.emit("chat:join",s,f=>{f?.error&&console.error("join error",f.error);});};return a.on("chat:history",u),a.on("chat:message",m),a.on("chat:users",o),a.on("connect",g),g(),()=>{a.off("chat:history",u),a.off("chat:message",m),a.off("chat:users",o),a.off("connect",g);}},[r,n]),{messages:x,onlineCount:l,sendMessage:u=>new Promise(m=>{if(!r||!n){m({error:"INVALID_REQUEST"});return}a.emit("chat:message",{authority:r,customerAuthority:n,message:u},o=>{o?.error&&console.error("send error",o.error),m(o??{error:"INTERNAL_ERROR"});});})}}var K=200;function ie({isOpen:r,onClose:h,title:x="Live Chat",theme:c,classNames:l,renderMessage:b,renderHeader:n,authority:d,customerAuthority:u,onConnectWallet:m}){let[o,g]=z.useState(""),s=z.useRef(null),f=c?.primaryColor||"#FF3D00",N=c?.background||"var(--chat-bg)",{sendMessage:R,onlineCount:M,messages:y}=w({authority:d||void 0,customerAuthority:u}),L=()=>{s.current?.scrollIntoView({behavior:"smooth"});};z.useEffect(()=>{L();},[y]);let v=()=>{let e=o.trim();if(e){if(!d){m?.();return}g(""),R(e);}},A=e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),v());};return r?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("style",{children:`
|
|
2
2
|
:root {
|
|
3
3
|
--chat-bg: #ffffff;
|
|
4
4
|
}
|
|
5
5
|
.dark {
|
|
6
6
|
--chat-bg: #15181D;
|
|
7
7
|
}
|
|
8
|
-
`}),jsxRuntime.jsxs("div",{onClick:
|
|
8
|
+
`}),jsxRuntime.jsxs("div",{onClick:e=>e.stopPropagation(),className:p("fixed inset-0 z-[110] flex flex-col overflow-hidden bg-white dark:bg-app-dark-400 lg:inset-auto lg:right-6 lg:top-[75px] lg:max-w-[340px] lg:max-h-[calc(100vh-90px)] lg:border lg:border-black/10 lg:shadow-2xl lg:dark:border-white/10","lg:h-[800px]",l?.container),style:{background:N},children:[n?n():jsxRuntime.jsxs("div",{className:p("flex items-center justify-between border-b border-black/10 px-4 py-4 dark:border-white/10 lg:px-6",l?.header),children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-3",children:[jsxRuntime.jsx("h2",{className:"text-lg font-semibold text-triad-dark-100 dark:text-white",children:x}),jsxRuntime.jsxs("div",{className:"flex items-center gap-1.5",children:[jsxRuntime.jsx("div",{className:"size-2 rounded-full bg-green-500 animate-pulse"}),jsxRuntime.jsxs("span",{className:"text-xs text-app-gray-400 dark:text-white",children:[M," online"]})]})]}),jsxRuntime.jsx("button",{onClick:h,children:jsxRuntime.jsx(lucideReact.X,{className:"text-black dark:text-white",size:18})})]}),jsxRuntime.jsxs("div",{className:p("flex-1 overflow-y-auto space-y-4 px-4 py-4 lg:px-6",l?.messages),children:[y.map((e,C)=>b?jsxRuntime.jsx(z__default.default.Fragment,{children:b(e)},C):jsxRuntime.jsxs("div",{className:`flex flex-col gap-1 ${e.authority===d?"items-end":"items-start"}`,children:[e.authority!==d&&jsxRuntime.jsx("span",{className:"max-w-[160px] truncate text-xs font-medium text-triad-dark-100 dark:text-white",children:e.name}),jsxRuntime.jsx("div",{className:p("max-w-[75%] rounded-2xl px-4 py-2.5",e.authority===d?"rounded-br-sm text-white":"rounded-bl-sm bg-black/5 text-triad-dark-100 dark:bg-white/5 dark:text-white"),style:e.authority===d?{backgroundColor:f}:void 0,children:jsxRuntime.jsx("p",{className:"text-sm break-words",children:e.message})}),jsxRuntime.jsx("span",{className:"text-[10px] text-black opacity-50 dark:text-white",children:dateFns.format(e.timestamp,"HH:mm")})]},C)),jsxRuntime.jsx("div",{ref:s})]}),jsxRuntime.jsx("div",{className:"border-t border-black/10 px-4 py-4 dark:border-white/10 lg:px-6",children:jsxRuntime.jsx("div",{className:"flex flex-col gap-2",children:jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsx("input",{value:o,onChange:e=>g(e.target.value),onKeyDown:A,maxLength:K,placeholder:"Digite sua mensagem...",className:p("flex-1 rounded-xl bg-black/5 px-4 py-2.5 text-sm text-app-gray-400 outline-none dark:bg-white/5 dark:text-white",l?.input)}),jsxRuntime.jsx("button",{onClick:v,disabled:!o.trim()||!d&&!m,className:p("flex size-10 items-center justify-center rounded-xl transition-colors bg-app-dark-400",l?.button),style:{opacity:o.trim()?1:.5},children:jsxRuntime.jsx(lucideReact.Send,{color:c?.buttonSend.color,size:18})})]})})})]})]}):null}exports.ButtonWidget=W;exports.LiveChat=ie;//# sourceMappingURL=index.js.map
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ButtonWidget.tsx","../src/utils/cn.ts","../src/utils/socket.ts","../src/hooks/useChat.ts","../src/components/LiveChat.tsx"],"names":["ButtonWidget","label","variant","className","props","jsx","cn","classes","twMerge","clsx","socket","io","socket_default","useChat","authority","customerAuthority","messages","setMessages","useState","onlineCount","setOnlineCount","useEffect","handleHistory","msgs","handleMessage","msg","prev","handleUsers","data","message","res","LiveChat","isOpen","onClose","title","theme","classNames","renderMessage","renderHeader","fallbackMessages","onConnectWallet","inputValue","setInputValue","messagesEndRef","useRef","primary","background","sendMessage","scrollToBottom","handleSendMessage","handleKeyDown","e","jsxs","Fragment","X","idx","format","Send","m"],"mappings":"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","file":"index.js","sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport interface ButtonWidgetProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n label: string;\n variant?: 'primary' | 'secondary';\n}\n\nexport const ButtonWidget: React.FC<ButtonWidgetProps> = ({ \n label, \n variant = 'primary', \n className = '',\n ...props \n}) => {\n const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors';\n const variants = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\n secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300'\n };\n\n return (\n <button \n className={`${baseStyles} ${variants[variant]} ${className}`}\n {...props}\n >\n {label}\n </button>\n );\n};\n","import type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...classes: ClassValue[]) {\n return twMerge(clsx(classes));\n}\n","import io from \"socket.io-client\";\n\nconst socket = io(\"https://beta.triadfi.co\", {\n path: \"/socket.io\",\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n randomizationFactor: 0.5,\n timeout: 5000,\n transports: [\"websocket\"],\n});\n\nexport default socket;\n","import socket from \"@/utils/socket\";\nimport { useEffect, useState } from \"react\";\n\nexport interface ChatMessage {\n authority?: string;\n name: string;\n image: string;\n predictionsCount: number;\n message: string;\n timestamp: number;\n}\n\nexport function useChat({\n authority,\n customerAuthority,\n}: {\n authority?: string;\n customerAuthority: string;\n}) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [onlineCount, setOnlineCount] = useState(0);\n\n useEffect(() => {\n if (!authority || !customerAuthority) return;\n\n socket.emit(\"chat:join\", { authority, customerAuthority });\n\n const handleHistory = (msgs: ChatMessage[]) => {\n setMessages(msgs);\n };\n\n const handleMessage = (msg: ChatMessage) => {\n setMessages((prev) => [...prev, msg]);\n };\n\n const handleUsers = (data: any) => {\n setOnlineCount(data.count);\n };\n\n socket.on(\"chat:history\", handleHistory);\n socket.on(\"chat:message\", handleMessage);\n socket.on(\"chat:users\", handleUsers);\n\n return () => {\n socket.emit(\"chat:leave\", { authority, customerAuthority });\n\n socket.off(\"chat:history\", handleHistory);\n socket.off(\"chat:message\", handleMessage);\n socket.off(\"chat:users\", handleUsers);\n };\n }, [authority, customerAuthority]);\n\n const sendMessage = (message: string) => {\n socket.emit(\n \"chat:message\",\n { authority, customerAuthority, message },\n (res: any) => {\n if (res.error) {\n console.error(\"send error\", res.error);\n }\n },\n );\n };\n\n return {\n messages,\n onlineCount,\n sendMessage,\n };\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { X, Send } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useChat, ChatMessage } from \"@/hooks/useChat\";\nimport { format } from \"date-fns\";\n\nexport interface LiveChatTheme {\n primaryColor?: string;\n background?: string;\n textColor?: string;\n}\n\nexport interface LiveChatClassNames {\n container?: string;\n header?: string;\n messages?: string;\n input?: string;\n button?: string;\n}\n\nexport interface LiveChatProps {\n isOpen: boolean;\n onClose: () => void;\n\n // ui\n title?: string;\n\n // customization\n theme?: LiveChatTheme;\n classNames?: LiveChatClassNames;\n\n // slots\n renderMessage?: (message: ChatMessage) => React.ReactNode;\n renderHeader?: () => React.ReactNode;\n authority: string | null;\n customerAuthority: string;\n onConnectWallet?: () => void;\n fallbackMessages?: {\n connectWallet: string;\n description: string;\n };\n}\n\nexport function LiveChat({\n isOpen,\n onClose,\n title = \"Live Chat\",\n theme,\n classNames,\n renderMessage,\n renderHeader,\n authority,\n customerAuthority,\n fallbackMessages,\n onConnectWallet,\n}: LiveChatProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const primary = theme?.primaryColor || \"#FF3D00\";\n const background = theme?.background || \"var(--chat-bg)\";\n\n const { sendMessage, onlineCount, messages } = useChat({\n authority: authority || undefined,\n customerAuthority: customerAuthority,\n });\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n const handleSendMessage = () => {\n if (!inputValue.trim()) return;\n setInputValue(\"\");\n sendMessage(inputValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSendMessage();\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <>\n <style>{`\n :root {\n --chat-bg: #ffffff;\n }\n .dark {\n --chat-bg: #15181D;\n }\n `}</style>\n\n {/* Desktop */}\n <div\n onClick={(e) => e.stopPropagation()}\n className={cn(\n \"fixed right-6 z-[110] hidden lg:flex flex-col overflow-hidden shadow-2xl border border-black/10 dark:border-white/10\",\n classNames?.container,\n )}\n style={{\n top: \"75px\",\n width: \"340px\",\n height: !authority ? \"auto\" : \"800px\",\n maxHeight: \"calc(100vh - 90px)\",\n background,\n }}\n >\n {/* Header */}\n {renderHeader ? (\n renderHeader()\n ) : (\n <div\n className={cn(\n \"flex items-center justify-between px-6 py-4 border-b border-black/10 dark:border-white/10\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"dark:text-white text-black\" size={18} />\n </button>\n </div>\n )}\n\n {/* Messages */}\n {authority && (\n <div\n className={cn(\n \"flex-1 overflow-y-auto px-6 py-4 space-y-4\",\n classNames?.messages,\n )}\n >\n {messages.map((message, idx) =>\n renderMessage ? (\n renderMessage(message)\n ) : (\n <div\n key={idx}\n className={`flex flex-col gap-1 ${\n message.authority === authority\n ? \"items-end\"\n : \"items-start\"\n }`}\n >\n {message.authority !== authority && (\n <span className=\"text-xs font-medium text-triad-dark-100 dark:text-white truncate max-w-[120px] truncate max-w-[160px]\">\n {message.name}\n </span>\n )}\n\n <div\n className={cn(\n \"max-w-[75%] rounded-2xl px-4 py-2.5\",\n message.authority === authority\n ? \"text-white rounded-br-sm\"\n : \"bg-black/5 dark:bg-white/5 text-triad-dark-100 dark:text-white rounded-bl-sm\",\n )}\n style={\n message.authority === authority\n ? { backgroundColor: primary }\n : undefined\n }\n >\n <p className=\"text-sm break-words\">{message.message}</p>\n </div>\n\n <span className=\"text-[10px] opacity-50 text-black dark:text-white\">\n {format(message.timestamp, \"HH:mm\")}\n </span>\n </div>\n ),\n )}\n <div ref={messagesEndRef} />\n </div>\n )}\n {/* Input */}\n <div className=\"px-6 py-4 border-t border-black/10 dark:border-white/10\">\n {!authority ? (\n <div className=\"flex flex-col items-center justify-center gap-3 py-4 px-3 text-center\">\n {/* Icon */}\n <div\n className=\"flex items-center justify-center size-12 rounded-2xl\"\n style={{\n background: `${primary}15`,\n }}\n >\n <span className=\"text-xl\">💬</span>\n </div>\n\n {/* Text */}\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-sm font-semibold text-black dark:text-white\">\n {fallbackMessages?.connectWallet || \"Entre no chat\"}\n </span>\n <span className=\"text-xs text-black/60 dark:text-white max-w-[220px]\">\n {fallbackMessages?.description ||\n \"Conecte sua carteira para participar das conversas em tempo real\"}\n </span>\n </div>\n\n {/* Button */}\n {onConnectWallet && (\n <button\n onClick={onConnectWallet}\n style={{ backgroundColor: primary }}\n className=\"mt-2 w-full max-w-[220px] px-4 py-2.5 text-white text-sm font-semibold rounded-xl transition-all hover:opacity-90 active:scale-95 shadow-sm\"\n >\n {fallbackMessages?.connectWallet || \"Connect Wallet\"}\n </button>\n )}\n </div>\n ) : (\n <div className=\"flex items-center gap-2\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 px-4 py-2.5 rounded-xl bg-black/5 text-app-gray-400 dark:bg-white/5 dark:text-white text-sm outline-none\",\n classNames?.input,\n )}\n />\n\n <button\n onClick={handleSendMessage}\n disabled={!inputValue.trim()}\n className={cn(\n \"flex items-center justify-center size-10 rounded-xl transition-colors\",\n classNames?.button,\n )}\n style={{\n backgroundColor: inputValue.trim() ? primary : undefined,\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n >\n <Send size={18} className=\"dark:text-white text-app-gay-400\" />\n </button>\n </div>\n )}\n </div>\n </div>\n\n {/* Mobile */}\n <div className=\"fixed inset-0 z-[110] flex flex-col lg:hidden bg-white dark:bg-app-dark-400\">\n {/* Reaproveita mesma estrutura */}\n <div className=\"flex-1 flex flex-col\">\n {/* Header */}\n <div\n className={cn(\n \"flex items-center justify-between px-6 py-4 border-b border-black/10 dark:border-white/10\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"dark:text-white text-black\" size={18} />\n </button>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4\">\n {messages.map((m, idx) => (\n <div key={idx}>{m.message}</div>\n ))}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div className=\"p-4 border-t flex flex-col gap-2\">\n {!authority ? (\n <div className=\"flex-1 flex flex-col items-center justify-center gap-2 py-2\">\n <span className=\"text-sm font-medium text-black/60 dark:text-white/60 text-center\">\n {fallbackMessages?.description ||\n \"Faça login para participar do chat\"}\n </span>\n {onConnectWallet && (\n <button\n onClick={onConnectWallet}\n style={{ backgroundColor: primary }}\n className=\"px-4 py-2 text-white text-sm font-semibold rounded-xl transition-opacity hover:opacity-90 active:scale-95\"\n >\n {fallbackMessages?.connectWallet || \"Fazer Login\"}\n </button>\n )}\n </div>\n ) : (\n <div className=\"flex gap-2 w-full\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 px-4 py-2.5 rounded-xl bg-black/5 text-app-gay-400 dark:bg-white/5 dark:text-white text-sm outline-none\",\n classNames?.input,\n )}\n />\n <button\n className={cn(\n \"flex items-center justify-center size-10 rounded-xl transition-colors\",\n classNames?.button,\n )}\n style={{\n backgroundColor: inputValue.trim() ? primary : undefined,\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n onClick={handleSendMessage}\n >\n <Send\n className=\"dark:text-white text-app-gay-400\"\n size={18}\n />\n </button>\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ButtonWidget.tsx","../src/utils/cn.ts","../src/utils/socket.ts","../src/hooks/useChat.ts","../src/components/LiveChat.tsx"],"names":["ButtonWidget","label","variant","className","props","jsx","cn","classes","twMerge","clsx","socket","io","socket_default","useChat","authority","customerAuthority","messages","setMessages","useState","onlineCount","setOnlineCount","normalizedCustomerAuthority","useEffect","handleHistory","msgs","handleMessage","msg","prev","handleUsers","data","joinChat","joinPayload","res","message","resolve","CHAT_MAX_MESSAGE_LENGTH","LiveChat","isOpen","onClose","title","theme","classNames","renderMessage","renderHeader","onConnectWallet","inputValue","setInputValue","messagesEndRef","useRef","primary","background","sendMessage","scrollToBottom","handleSendMessage","handleKeyDown","jsxs","Fragment","X","idx","React","format","Send"],"mappings":"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","file":"index.js","sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport interface ButtonWidgetProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n label: string;\n variant?: 'primary' | 'secondary';\n}\n\nexport const ButtonWidget: React.FC<ButtonWidgetProps> = ({ \n label, \n variant = 'primary', \n className = '',\n ...props \n}) => {\n const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors';\n const variants = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\n secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300'\n };\n\n return (\n <button \n className={`${baseStyles} ${variants[variant]} ${className}`}\n {...props}\n >\n {label}\n </button>\n );\n};\n","import type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...classes: ClassValue[]) {\n return twMerge(clsx(classes));\n}\n","import io from \"socket.io-client\";\n\nconst socket = io(\"https://beta.triadfi.co\", {\n path: \"/socket.io\",\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n randomizationFactor: 0.5,\n timeout: 5000,\n transports: [\"websocket\"],\n});\n\nexport default socket;\n","import socket from \"@/utils/socket\";\nimport { useEffect, useState } from \"react\";\n\nexport interface ChatMessage {\n authority?: string;\n name: string;\n image: string;\n predictionsCount: number;\n message: string;\n timestamp: number;\n}\n\ninterface ChatUsersPayload {\n count: number;\n}\n\ninterface ChatResponse {\n error: string | null;\n}\n\nexport function useChat({\n authority,\n customerAuthority,\n}: {\n authority?: string;\n customerAuthority: string;\n}) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [onlineCount, setOnlineCount] = useState(0);\n const normalizedCustomerAuthority = customerAuthority?.trim();\n\n useEffect(() => {\n if (!normalizedCustomerAuthority) return;\n\n const handleHistory = (msgs: ChatMessage[]) => {\n setMessages(msgs);\n };\n\n const handleMessage = (msg: ChatMessage) => {\n setMessages((prev) => [...prev, msg]);\n };\n\n const handleUsers = (data: ChatUsersPayload) => {\n setOnlineCount(data.count);\n };\n\n const joinChat = () => {\n const joinPayload = authority\n ? { authority, customerAuthority: normalizedCustomerAuthority }\n : { customerAuthority: normalizedCustomerAuthority };\n\n socket.emit(\"chat:join\", joinPayload, (res: ChatResponse) => {\n if (res?.error) {\n console.error(\"join error\", res.error);\n }\n });\n };\n\n socket.on(\"chat:history\", handleHistory);\n socket.on(\"chat:message\", handleMessage);\n socket.on(\"chat:users\", handleUsers);\n socket.on(\"connect\", joinChat);\n joinChat();\n\n return () => {\n socket.off(\"chat:history\", handleHistory);\n socket.off(\"chat:message\", handleMessage);\n socket.off(\"chat:users\", handleUsers);\n socket.off(\"connect\", joinChat);\n };\n }, [authority, normalizedCustomerAuthority]);\n\n const sendMessage = (message: string): Promise<ChatResponse> =>\n new Promise((resolve) => {\n if (!authority || !normalizedCustomerAuthority) {\n resolve({ error: \"INVALID_REQUEST\" });\n return;\n }\n\n socket.emit(\n \"chat:message\",\n { authority, customerAuthority: normalizedCustomerAuthority, message },\n (res: ChatResponse) => {\n if (res?.error) {\n console.error(\"send error\", res.error);\n }\n resolve(res ?? { error: \"INTERNAL_ERROR\" });\n },\n );\n });\n\n return {\n messages,\n onlineCount,\n sendMessage,\n };\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { X, Send } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useChat, ChatMessage } from \"@/hooks/useChat\";\nimport { format } from \"date-fns\";\n\nexport interface LiveChatTheme {\n primaryColor?: string;\n background?: string;\n textColor?: string;\n buttonSend: {\n color: string;\n } \n}\n\nexport interface LiveChatClassNames {\n container?: string;\n header?: string;\n messages?: string;\n input?: string;\n button?: string;\n}\n\nexport interface LiveChatProps {\n isOpen: boolean;\n onClose: () => void;\n\n // ui\n title?: string;\n\n // customization\n theme?: LiveChatTheme;\n classNames?: LiveChatClassNames;\n\n // slots\n renderMessage?: (message: ChatMessage) => React.ReactNode;\n renderHeader?: () => React.ReactNode;\n authority: string | null;\n customerAuthority: string;\n onConnectWallet?: () => void;\n fallbackMessages?: {\n connectWallet: string;\n description: string;\n };\n}\n\nconst CHAT_MAX_MESSAGE_LENGTH = 200;\n\nexport function LiveChat({\n isOpen,\n onClose,\n title = \"Live Chat\",\n theme,\n classNames,\n renderMessage,\n renderHeader,\n authority,\n customerAuthority,\n onConnectWallet,\n}: LiveChatProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const primary = theme?.primaryColor || \"#FF3D00\";\n const background = theme?.background || \"var(--chat-bg)\";\n\n const { sendMessage, onlineCount, messages } = useChat({\n authority: authority || undefined,\n customerAuthority: customerAuthority,\n });\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n const handleSendMessage = () => {\n const message = inputValue.trim();\n if (!message) return;\n\n if (!authority) {\n onConnectWallet?.();\n return;\n }\n\n setInputValue(\"\");\n sendMessage(message);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSendMessage();\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <>\n <style>{`\n :root {\n --chat-bg: #ffffff;\n }\n .dark {\n --chat-bg: #15181D;\n }\n `}</style>\n\n <div\n onClick={(e) => e.stopPropagation()}\n className={cn(\n \"fixed inset-0 z-[110] flex flex-col overflow-hidden bg-white dark:bg-app-dark-400 lg:inset-auto lg:right-6 lg:top-[75px] lg:max-w-[340px] lg:max-h-[calc(100vh-90px)] lg:border lg:border-black/10 lg:shadow-2xl lg:dark:border-white/10\",\n \"lg:h-[800px]\",\n classNames?.container,\n )}\n style={{ background }}\n >\n {renderHeader ? (\n renderHeader()\n ) : (\n <div\n className={cn(\n \"flex items-center justify-between border-b border-black/10 px-4 py-4 dark:border-white/10 lg:px-6\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"text-black dark:text-white\" size={18} />\n </button>\n </div>\n )}\n\n <div\n className={cn(\n \"flex-1 overflow-y-auto space-y-4 px-4 py-4 lg:px-6\",\n classNames?.messages,\n )}\n >\n {messages.map((message, idx) =>\n renderMessage ? (\n <React.Fragment key={idx}>{renderMessage(message)}</React.Fragment>\n ) : (\n <div\n key={idx}\n className={`flex flex-col gap-1 ${\n message.authority === authority ? \"items-end\" : \"items-start\"\n }`}\n >\n {message.authority !== authority && (\n <span className=\"max-w-[160px] truncate text-xs font-medium text-triad-dark-100 dark:text-white\">\n {message.name}\n </span>\n )}\n\n <div\n className={cn(\n \"max-w-[75%] rounded-2xl px-4 py-2.5\",\n message.authority === authority\n ? \"rounded-br-sm text-white\"\n : \"rounded-bl-sm bg-black/5 text-triad-dark-100 dark:bg-white/5 dark:text-white\",\n )}\n style={\n message.authority === authority\n ? { backgroundColor: primary }\n : undefined\n }\n >\n <p className=\"text-sm break-words\">{message.message}</p>\n </div>\n\n <span className=\"text-[10px] text-black opacity-50 dark:text-white\">\n {format(message.timestamp, \"HH:mm\")}\n </span>\n </div>\n ),\n )}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"border-t border-black/10 px-4 py-4 dark:border-white/10 lg:px-6\">\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n maxLength={CHAT_MAX_MESSAGE_LENGTH}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 rounded-xl bg-black/5 px-4 py-2.5 text-sm text-app-gray-400 outline-none dark:bg-white/5 dark:text-white\",\n classNames?.input,\n )}\n />\n\n <button\n onClick={handleSendMessage}\n disabled={!inputValue.trim() || (!authority && !onConnectWallet)}\n className={cn(\n \"flex size-10 items-center justify-center rounded-xl transition-colors bg-app-dark-400\",\n classNames?.button,\n )}\n style={{\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n >\n <Send color={theme?.buttonSend.color} size={18} />\n </button>\n </div>\n </div>\n </div>\n </div>\n </>\n );\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useState,useRef,useEffect}from'react';import {X,Send}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import
|
|
1
|
+
import {jsx,jsxs,Fragment}from'react/jsx-runtime';import z,{useState,useRef,useEffect}from'react';import {X,Send}from'lucide-react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import T from'socket.io-client';import {format}from'date-fns';var W=({label:r,variant:h="primary",className:x="",...c})=>jsx("button",{className:`px-4 py-2 rounded-lg font-medium transition-colors ${{primary:"bg-blue-600 text-white hover:bg-blue-700",secondary:"bg-gray-200 text-gray-800 hover:bg-gray-300"}[h]} ${x}`,...c,children:r});function p(...r){return twMerge(clsx(r))}var D=T("https://beta.triadfi.co",{path:"/socket.io",reconnection:true,reconnectionAttempts:5,reconnectionDelay:1e3,randomizationFactor:.5,timeout:5e3,transports:["websocket"]}),a=D;function w({authority:r,customerAuthority:h}){let[x,c]=useState([]),[l,b]=useState(0),n=h?.trim();return useEffect(()=>{if(!n)return;let u=s=>{c(s);},m=s=>{c(f=>[...f,s]);},o=s=>{b(s.count);},g=()=>{let s=r?{authority:r,customerAuthority:n}:{customerAuthority:n};a.emit("chat:join",s,f=>{f?.error&&console.error("join error",f.error);});};return a.on("chat:history",u),a.on("chat:message",m),a.on("chat:users",o),a.on("connect",g),g(),()=>{a.off("chat:history",u),a.off("chat:message",m),a.off("chat:users",o),a.off("connect",g);}},[r,n]),{messages:x,onlineCount:l,sendMessage:u=>new Promise(m=>{if(!r||!n){m({error:"INVALID_REQUEST"});return}a.emit("chat:message",{authority:r,customerAuthority:n,message:u},o=>{o?.error&&console.error("send error",o.error),m(o??{error:"INTERNAL_ERROR"});});})}}var K=200;function ie({isOpen:r,onClose:h,title:x="Live Chat",theme:c,classNames:l,renderMessage:b,renderHeader:n,authority:d,customerAuthority:u,onConnectWallet:m}){let[o,g]=useState(""),s=useRef(null),f=c?.primaryColor||"#FF3D00",N=c?.background||"var(--chat-bg)",{sendMessage:R,onlineCount:M,messages:y}=w({authority:d||void 0,customerAuthority:u}),L=()=>{s.current?.scrollIntoView({behavior:"smooth"});};useEffect(()=>{L();},[y]);let v=()=>{let e=o.trim();if(e){if(!d){m?.();return}g(""),R(e);}},A=e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),v());};return r?jsxs(Fragment,{children:[jsx("style",{children:`
|
|
2
2
|
:root {
|
|
3
3
|
--chat-bg: #ffffff;
|
|
4
4
|
}
|
|
5
5
|
.dark {
|
|
6
6
|
--chat-bg: #15181D;
|
|
7
7
|
}
|
|
8
|
-
`}),jsxs("div",{onClick:
|
|
8
|
+
`}),jsxs("div",{onClick:e=>e.stopPropagation(),className:p("fixed inset-0 z-[110] flex flex-col overflow-hidden bg-white dark:bg-app-dark-400 lg:inset-auto lg:right-6 lg:top-[75px] lg:max-w-[340px] lg:max-h-[calc(100vh-90px)] lg:border lg:border-black/10 lg:shadow-2xl lg:dark:border-white/10","lg:h-[800px]",l?.container),style:{background:N},children:[n?n():jsxs("div",{className:p("flex items-center justify-between border-b border-black/10 px-4 py-4 dark:border-white/10 lg:px-6",l?.header),children:[jsxs("div",{className:"flex items-center gap-3",children:[jsx("h2",{className:"text-lg font-semibold text-triad-dark-100 dark:text-white",children:x}),jsxs("div",{className:"flex items-center gap-1.5",children:[jsx("div",{className:"size-2 rounded-full bg-green-500 animate-pulse"}),jsxs("span",{className:"text-xs text-app-gray-400 dark:text-white",children:[M," online"]})]})]}),jsx("button",{onClick:h,children:jsx(X,{className:"text-black dark:text-white",size:18})})]}),jsxs("div",{className:p("flex-1 overflow-y-auto space-y-4 px-4 py-4 lg:px-6",l?.messages),children:[y.map((e,C)=>b?jsx(z.Fragment,{children:b(e)},C):jsxs("div",{className:`flex flex-col gap-1 ${e.authority===d?"items-end":"items-start"}`,children:[e.authority!==d&&jsx("span",{className:"max-w-[160px] truncate text-xs font-medium text-triad-dark-100 dark:text-white",children:e.name}),jsx("div",{className:p("max-w-[75%] rounded-2xl px-4 py-2.5",e.authority===d?"rounded-br-sm text-white":"rounded-bl-sm bg-black/5 text-triad-dark-100 dark:bg-white/5 dark:text-white"),style:e.authority===d?{backgroundColor:f}:void 0,children:jsx("p",{className:"text-sm break-words",children:e.message})}),jsx("span",{className:"text-[10px] text-black opacity-50 dark:text-white",children:format(e.timestamp,"HH:mm")})]},C)),jsx("div",{ref:s})]}),jsx("div",{className:"border-t border-black/10 px-4 py-4 dark:border-white/10 lg:px-6",children:jsx("div",{className:"flex flex-col gap-2",children:jsxs("div",{className:"flex items-center gap-2",children:[jsx("input",{value:o,onChange:e=>g(e.target.value),onKeyDown:A,maxLength:K,placeholder:"Digite sua mensagem...",className:p("flex-1 rounded-xl bg-black/5 px-4 py-2.5 text-sm text-app-gray-400 outline-none dark:bg-white/5 dark:text-white",l?.input)}),jsx("button",{onClick:v,disabled:!o.trim()||!d&&!m,className:p("flex size-10 items-center justify-center rounded-xl transition-colors bg-app-dark-400",l?.button),style:{opacity:o.trim()?1:.5},children:jsx(Send,{color:c?.buttonSend.color,size:18})})]})})})]})]}):null}export{W as ButtonWidget,ie as LiveChat};//# sourceMappingURL=index.mjs.map
|
|
9
9
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ButtonWidget.tsx","../src/utils/cn.ts","../src/utils/socket.ts","../src/hooks/useChat.ts","../src/components/LiveChat.tsx"],"names":["ButtonWidget","label","variant","className","props","jsx","cn","classes","twMerge","clsx","socket","io","socket_default","useChat","authority","customerAuthority","messages","setMessages","useState","onlineCount","setOnlineCount","useEffect","handleHistory","msgs","handleMessage","msg","prev","handleUsers","data","message","res","LiveChat","isOpen","onClose","title","theme","classNames","renderMessage","renderHeader","fallbackMessages","onConnectWallet","inputValue","setInputValue","messagesEndRef","useRef","primary","background","sendMessage","scrollToBottom","handleSendMessage","handleKeyDown","e","jsxs","Fragment","X","idx","format","Send","m"],"mappings":"6PASO,IAAMA,CAAAA,CAA4C,CAAC,CACxD,KAAA,CAAAC,EACA,OAAA,CAAAC,CAAAA,CAAU,UACV,SAAA,CAAAC,CAAAA,CAAY,GACZ,GAAGC,CACL,IAQIC,GAAAA,CAAC,QAAA,CAAA,CACC,UAAW,CAAA,mDAAA,EAPE,CACf,QAAS,0CAAA,CACT,SAAA,CAAW,6CACb,CAAA,CAIyCH,CAAO,CAAC,CAAA,CAAA,EAAIC,CAAS,GACzD,GAAGC,CAAAA,CAEH,SAAAH,CAAAA,CACH,ECvBG,SAASK,CAAAA,CAAAA,GAAMC,CAAAA,CAAuB,CAC3C,OAAOC,OAAAA,CAAQC,KAAKF,CAAO,CAAC,CAC9B,CCJA,IAAMG,CAAAA,CAASC,EAAG,yBAAA,CAA2B,CAC3C,KAAM,YAAA,CACN,YAAA,CAAc,KACd,oBAAA,CAAsB,CAAA,CACtB,kBAAmB,GAAA,CACnB,mBAAA,CAAqB,GACrB,OAAA,CAAS,GAAA,CACT,WAAY,CAAC,WAAW,CAC1B,CAAC,CAAA,CAEMC,CAAAA,CAAQF,ECAR,SAASG,CAAAA,CAAQ,CACtB,SAAA,CAAAC,CAAAA,CACA,kBAAAC,CACF,CAAA,CAGG,CACD,GAAM,CAACC,EAAUC,CAAW,CAAA,CAAIC,QAAAA,CAAwB,EAAE,CAAA,CACpD,CAACC,CAAAA,CAAaC,CAAc,EAAIF,QAAAA,CAAS,CAAC,EAEhD,OAAAG,SAAAA,CAAU,IAAM,CACd,GAAI,CAACP,CAAAA,EAAa,CAACC,EAAmB,OAEtCH,CAAAA,CAAO,KAAK,WAAA,CAAa,CAAE,SAAA,CAAAE,CAAAA,CAAW,iBAAA,CAAAC,CAAkB,CAAC,CAAA,CAEzD,IAAMO,EAAiBC,CAAAA,EAAwB,CAC7CN,EAAYM,CAAI,EAClB,EAEMC,CAAAA,CAAiBC,CAAAA,EAAqB,CAC1CR,CAAAA,CAAaS,CAAAA,EAAS,CAAC,GAAGA,CAAAA,CAAMD,CAAG,CAAC,EACtC,CAAA,CAEME,CAAAA,CAAeC,CAAAA,EAAc,CACjCR,EAAeQ,CAAAA,CAAK,KAAK,EAC3B,CAAA,CAEA,OAAAhB,EAAO,EAAA,CAAG,cAAA,CAAgBU,CAAa,CAAA,CACvCV,CAAAA,CAAO,GAAG,cAAA,CAAgBY,CAAa,EACvCZ,CAAAA,CAAO,EAAA,CAAG,aAAce,CAAW,CAAA,CAE5B,IAAM,CACXf,CAAAA,CAAO,IAAA,CAAK,aAAc,CAAE,SAAA,CAAAE,EAAW,iBAAA,CAAAC,CAAkB,CAAC,CAAA,CAE1DH,CAAAA,CAAO,IAAI,cAAA,CAAgBU,CAAa,EACxCV,CAAAA,CAAO,GAAA,CAAI,eAAgBY,CAAa,CAAA,CACxCZ,EAAO,GAAA,CAAI,YAAA,CAAce,CAAW,EACtC,CACF,CAAA,CAAG,CAACb,CAAAA,CAAWC,CAAiB,CAAC,CAAA,CAc1B,CACL,SAAAC,CAAAA,CACA,WAAA,CAAAG,EACA,WAAA,CAfmBU,CAAAA,EAAoB,CACvCjB,CAAAA,CAAO,IAAA,CACL,eACA,CAAE,SAAA,CAAAE,EAAW,iBAAA,CAAAC,CAAAA,CAAmB,OAAA,CAAAc,CAAQ,CAAA,CACvCC,CAAAA,EAAa,CACRA,CAAAA,CAAI,KAAA,EACN,QAAQ,KAAA,CAAM,YAAA,CAAcA,EAAI,KAAK,EAEzC,CACF,EACF,CAMA,CACF,CCxBO,SAASC,EAAAA,CAAS,CACvB,OAAAC,CAAAA,CACA,OAAA,CAAAC,EACA,KAAA,CAAAC,CAAAA,CAAQ,YACR,KAAA,CAAAC,CAAAA,CACA,WAAAC,CAAAA,CACA,aAAA,CAAAC,EACA,YAAA,CAAAC,CAAAA,CACA,UAAAxB,CAAAA,CACA,iBAAA,CAAAC,CAAAA,CACA,gBAAA,CAAAwB,CAAAA,CACA,eAAA,CAAAC,CACF,CAAA,CAAkB,CAChB,GAAM,CAACC,CAAAA,CAAYC,CAAa,CAAA,CAAIxB,QAAAA,CAAS,EAAE,CAAA,CACzCyB,CAAAA,CAAiBC,OAAuB,IAAI,CAAA,CAC5CC,EAAUV,CAAAA,EAAO,YAAA,EAAgB,UACjCW,CAAAA,CAAaX,CAAAA,EAAO,UAAA,EAAc,gBAAA,CAElC,CAAE,WAAA,CAAAY,EAAa,WAAA,CAAA5B,CAAAA,CAAa,SAAAH,CAAS,CAAA,CAAIH,EAAQ,CACrD,SAAA,CAAWC,GAAa,MAAA,CACxB,iBAAA,CAAmBC,CACrB,CAAC,CAAA,CAEKiC,EAAiB,IAAM,CAC3BL,EAAe,OAAA,EAAS,cAAA,CAAe,CAAE,QAAA,CAAU,QAAS,CAAC,EAC/D,CAAA,CAEAtB,SAAAA,CAAU,IAAM,CACd2B,CAAAA,GACF,CAAA,CAAG,CAAChC,CAAQ,CAAC,CAAA,CAEb,IAAMiC,CAAAA,CAAoB,IAAM,CACzBR,CAAAA,CAAW,IAAA,KAChBC,CAAAA,CAAc,EAAE,CAAA,CAChBK,CAAAA,CAAYN,CAAU,CAAA,EACxB,EAEMS,CAAAA,CAAiBC,CAAAA,EAA2B,CAC5CA,CAAAA,CAAE,GAAA,GAAQ,SAAW,CAACA,CAAAA,CAAE,WAC1BA,CAAAA,CAAE,cAAA,GACFF,CAAAA,EAAkB,EAEtB,EAEA,OAAKjB,CAAAA,CAGHoB,KAAAC,QAAAA,CAAA,CACE,QAAA,CAAA,CAAAhD,GAAAA,CAAC,OAAA,CAAA,CAAO,QAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA,CAON,CAAA,CAGF+C,IAAAA,CAAC,KAAA,CAAA,CACC,OAAA,CAAUD,CAAAA,EAAMA,CAAAA,CAAE,eAAA,EAAgB,CAClC,SAAA,CAAW7C,CAAAA,CACT,sHAAA,CACA8B,CAAAA,EAAY,SACd,CAAA,CACA,KAAA,CAAO,CACL,GAAA,CAAK,MAAA,CACL,KAAA,CAAO,OAAA,CACP,MAAA,CAAStB,CAAAA,CAAqB,OAAA,CAAT,MAAA,CACrB,SAAA,CAAW,oBAAA,CACX,UAAA,CAAAgC,CACF,CAAA,CAGC,QAAA,CAAA,CAAAR,CAAAA,CACCA,CAAAA,EAAa,CAEbc,IAAAA,CAAC,KAAA,CAAA,CACC,SAAA,CAAW9C,CAAAA,CACT,2FAAA,CACA8B,CAAAA,EAAY,MACd,CAAA,CAEA,QAAA,CAAA,CAAAgB,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,yBAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,IAAA,CAAA,CAAG,SAAA,CAAU,2DAAA,CACX,QAAA,CAAA6B,CAAAA,CACH,CAAA,CACAkB,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,2BAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,OAAI,SAAA,CAAU,gDAAA,CAAiD,CAAA,CAChE+C,IAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,2CAAA,CACb,QAAA,CAAA,CAAAjC,CAAAA,CAAY,SAAA,CAAA,CACf,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CACAd,GAAAA,CAAC,QAAA,CAAA,CAAO,OAAA,CAAS4B,CAAAA,CACf,QAAA,CAAA5B,GAAAA,CAACiD,CAAAA,CAAA,CAAE,SAAA,CAAU,4BAAA,CAA6B,IAAA,CAAM,EAAA,CAAI,CAAA,CACtD,CAAA,CAAA,CACF,CAAA,CAIDxC,CAAAA,EACCsC,IAAAA,CAAC,KAAA,CAAA,CACC,SAAA,CAAW9C,CAAAA,CACT,4CAAA,CACA8B,CAAAA,EAAY,QACd,CAAA,CAEC,QAAA,CAAA,CAAApB,CAAAA,CAAS,GAAA,CAAI,CAACa,CAAAA,CAAS0B,CAAAA,GACtBlB,CAAAA,CACEA,CAAAA,CAAcR,CAAO,CAAA,CAErBuB,IAAAA,CAAC,KAAA,CAAA,CAEC,SAAA,CAAW,CAAA,oBAAA,EACTvB,CAAAA,CAAQ,SAAA,GAAcf,CAAAA,CAClB,WAAA,CACA,aACN,CAAA,CAAA,CAEC,QAAA,CAAA,CAAAe,CAAAA,CAAQ,SAAA,GAAcf,CAAAA,EACrBT,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,wGACb,QAAA,CAAAwB,CAAAA,CAAQ,IAAA,CACX,CAAA,CAGFxB,GAAAA,CAAC,KAAA,CAAA,CACC,SAAA,CAAWC,CAAAA,CACT,qCAAA,CACAuB,CAAAA,CAAQ,SAAA,GAAcf,CAAAA,CAClB,0BAAA,CACA,8EACN,CAAA,CACA,KAAA,CACEe,CAAAA,CAAQ,SAAA,GAAcf,CAAAA,CAClB,CAAE,eAAA,CAAiB+B,CAAQ,CAAA,CAC3B,MAAA,CAGN,QAAA,CAAAxC,GAAAA,CAAC,GAAA,CAAA,CAAE,SAAA,CAAU,qBAAA,CAAuB,QAAA,CAAAwB,CAAAA,CAAQ,OAAA,CAAQ,CAAA,CACtD,CAAA,CAEAxB,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,mDAAA,CACb,QAAA,CAAAmD,MAAAA,CAAO3B,CAAAA,CAAQ,SAAA,CAAW,OAAO,CAAA,CACpC,CAAA,CAAA,CAAA,CA/BK0B,CAgCP,CAEJ,CAAA,CACAlD,GAAAA,CAAC,KAAA,CAAA,CAAI,GAAA,CAAKsC,CAAAA,CAAgB,CAAA,CAAA,CAC5B,CAAA,CAGFtC,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,yDAAA,CACZ,QAAA,CAACS,CAAAA,CAmCAsC,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,yBAAA,CACb,UAAA/C,GAAAA,CAAC,OAAA,CAAA,CACC,KAAA,CAAOoC,CAAAA,CACP,QAAA,CAAWU,CAAAA,EAAMT,CAAAA,CAAcS,CAAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAC7C,SAAA,CAAWD,CAAAA,CACX,WAAA,CAAY,wBAAA,CACZ,SAAA,CAAW5C,CAAAA,CACT,iHAAA,CACA8B,CAAAA,EAAY,KACd,CAAA,CACF,CAAA,CAEA/B,GAAAA,CAAC,QAAA,CAAA,CACC,OAAA,CAAS4C,CAAAA,CACT,QAAA,CAAU,CAACR,CAAAA,CAAW,IAAA,EAAK,CAC3B,SAAA,CAAWnC,CAAAA,CACT,uEAAA,CACA8B,CAAAA,EAAY,MACd,CAAA,CACA,KAAA,CAAO,CACL,eAAA,CAAiBK,CAAAA,CAAW,IAAA,EAAK,CAAII,CAAAA,CAAU,MAAA,CAC/C,OAAA,CAASJ,CAAAA,CAAW,IAAA,EAAK,CAAI,CAAA,CAAI,EACnC,CAAA,CAEA,QAAA,CAAApC,GAAAA,CAACoD,IAAAA,CAAA,CAAK,IAAA,CAAM,EAAA,CAAI,SAAA,CAAU,kCAAA,CAAmC,CAAA,CAC/D,CAAA,CAAA,CACF,CAAA,CA5DAL,IAAAA,CAAC,OAAI,SAAA,CAAU,uEAAA,CAEb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,KAAA,CAAA,CACC,SAAA,CAAU,sDAAA,CACV,KAAA,CAAO,CACL,UAAA,CAAY,CAAA,EAAGwC,CAAO,CAAA,EAAA,CACxB,CAAA,CAEA,QAAA,CAAAxC,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,SAAA,CAAU,QAAA,CAAA,WAAA,CAAE,CAAA,CAC9B,CAAA,CAGA+C,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,qBAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,kDAAA,CACb,QAAA,CAAAkC,CAAAA,EAAkB,aAAA,EAAiB,eAAA,CACtC,CAAA,CACAlC,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,qDAAA,CACb,QAAA,CAAAkC,CAAAA,EAAkB,WAAA,EACjB,kEAAA,CACJ,CAAA,CAAA,CACF,CAAA,CAGCC,CAAAA,EACCnC,GAAAA,CAAC,QAAA,CAAA,CACC,OAAA,CAASmC,CAAAA,CACT,KAAA,CAAO,CAAE,eAAA,CAAiBK,CAAQ,CAAA,CAClC,SAAA,CAAU,6IAAA,CAET,QAAA,CAAAN,CAAAA,EAAkB,aAAA,EAAiB,gBAAA,CACtC,CAAA,CAAA,CAEJ,CAAA,CA8BJ,GACF,CAAA,CAGAlC,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,6EAAA,CAEb,QAAA,CAAA+C,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,sBAAA,CAEb,QAAA,CAAA,CAAAA,IAAAA,CAAC,KAAA,CAAA,CACC,SAAA,CAAW9C,CAAAA,CACT,2FAAA,CACA8B,CAAAA,EAAY,MACd,CAAA,CAEA,QAAA,CAAA,CAAAgB,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,yBAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,IAAA,CAAA,CAAG,SAAA,CAAU,2DAAA,CACX,QAAA,CAAA6B,CAAAA,CACH,CAAA,CACAkB,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,2BAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,gDAAA,CAAiD,CAAA,CAChE+C,IAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,2CAAA,CACb,QAAA,CAAA,CAAAjC,CAAAA,CAAY,SAAA,CAAA,CACf,CAAA,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CACAd,GAAAA,CAAC,QAAA,CAAA,CAAO,OAAA,CAAS4B,CAAAA,CACf,QAAA,CAAA5B,GAAAA,CAACiD,CAAAA,CAAA,CAAE,SAAA,CAAU,4BAAA,CAA6B,IAAA,CAAM,EAAA,CAAI,EACtD,CAAA,CAAA,CACF,CAAA,CAGAF,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,4CAAA,CACZ,QAAA,CAAA,CAAApC,CAAAA,CAAS,GAAA,CAAI,CAAC0C,CAAAA,CAAGH,CAAAA,GAChBlD,GAAAA,CAAC,KAAA,CAAA,CAAe,QAAA,CAAAqD,CAAAA,CAAE,OAAA,CAAA,CAARH,CAAgB,CAC3B,CAAA,CACDlD,GAAAA,CAAC,KAAA,CAAA,CAAI,GAAA,CAAKsC,CAAAA,CAAgB,CAAA,CAAA,CAC5B,CAAA,CAGAtC,GAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,kCAAA,CACZ,QAAA,CAACS,CAAAA,CAiBAsC,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,mBAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,OAAA,CAAA,CACC,KAAA,CAAOoC,CAAAA,CACP,QAAA,CAAWU,CAAAA,EAAMT,CAAAA,CAAcS,CAAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAC7C,SAAA,CAAWD,CAAAA,CACX,WAAA,CAAY,wBAAA,CACZ,SAAA,CAAW5C,CAAAA,CACT,gHAAA,CACA8B,CAAAA,EAAY,KACd,CAAA,CACF,CAAA,CACA/B,GAAAA,CAAC,QAAA,CAAA,CACC,SAAA,CAAWC,CAAAA,CACT,uEAAA,CACA8B,GAAY,MACd,CAAA,CACA,KAAA,CAAO,CACL,eAAA,CAAiBK,CAAAA,CAAW,IAAA,EAAK,CAAII,CAAAA,CAAU,MAAA,CAC/C,OAAA,CAASJ,CAAAA,CAAW,IAAA,EAAK,CAAI,CAAA,CAAI,EACnC,CAAA,CACA,OAAA,CAASQ,CAAAA,CAET,QAAA,CAAA5C,GAAAA,CAACoD,IAAAA,CAAA,CACC,SAAA,CAAU,kCAAA,CACV,IAAA,CAAM,EAAA,CACR,CAAA,CACF,CAAA,CAAA,CACF,CAAA,CA3CAL,IAAAA,CAAC,KAAA,CAAA,CAAI,UAAU,6DAAA,CACb,QAAA,CAAA,CAAA/C,GAAAA,CAAC,MAAA,CAAA,CAAK,SAAA,CAAU,kEAAA,CACb,QAAA,CAAAkC,CAAAA,EAAkB,WAAA,EACjB,uCAAA,CACJ,CAAA,CACCC,CAAAA,EACCnC,GAAAA,CAAC,QAAA,CAAA,CACC,OAAA,CAASmC,CAAAA,CACT,KAAA,CAAO,CAAE,eAAA,CAAiBK,CAAQ,CAAA,CAClC,SAAA,CAAU,2GAAA,CAET,QAAA,CAAAN,CAAAA,EAAkB,aAAA,EAAiB,aAAA,CACtC,CAAA,CAAA,CAEJ,CAAA,CA+BJ,CAAA,CAAA,CACF,CAAA,CACF,CAAA,CAAA,CACF,EApQkB,IAsQtB","file":"index.mjs","sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport interface ButtonWidgetProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n label: string;\n variant?: 'primary' | 'secondary';\n}\n\nexport const ButtonWidget: React.FC<ButtonWidgetProps> = ({ \n label, \n variant = 'primary', \n className = '',\n ...props \n}) => {\n const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors';\n const variants = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\n secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300'\n };\n\n return (\n <button \n className={`${baseStyles} ${variants[variant]} ${className}`}\n {...props}\n >\n {label}\n </button>\n );\n};\n","import type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...classes: ClassValue[]) {\n return twMerge(clsx(classes));\n}\n","import io from \"socket.io-client\";\n\nconst socket = io(\"https://beta.triadfi.co\", {\n path: \"/socket.io\",\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n randomizationFactor: 0.5,\n timeout: 5000,\n transports: [\"websocket\"],\n});\n\nexport default socket;\n","import socket from \"@/utils/socket\";\nimport { useEffect, useState } from \"react\";\n\nexport interface ChatMessage {\n authority?: string;\n name: string;\n image: string;\n predictionsCount: number;\n message: string;\n timestamp: number;\n}\n\nexport function useChat({\n authority,\n customerAuthority,\n}: {\n authority?: string;\n customerAuthority: string;\n}) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [onlineCount, setOnlineCount] = useState(0);\n\n useEffect(() => {\n if (!authority || !customerAuthority) return;\n\n socket.emit(\"chat:join\", { authority, customerAuthority });\n\n const handleHistory = (msgs: ChatMessage[]) => {\n setMessages(msgs);\n };\n\n const handleMessage = (msg: ChatMessage) => {\n setMessages((prev) => [...prev, msg]);\n };\n\n const handleUsers = (data: any) => {\n setOnlineCount(data.count);\n };\n\n socket.on(\"chat:history\", handleHistory);\n socket.on(\"chat:message\", handleMessage);\n socket.on(\"chat:users\", handleUsers);\n\n return () => {\n socket.emit(\"chat:leave\", { authority, customerAuthority });\n\n socket.off(\"chat:history\", handleHistory);\n socket.off(\"chat:message\", handleMessage);\n socket.off(\"chat:users\", handleUsers);\n };\n }, [authority, customerAuthority]);\n\n const sendMessage = (message: string) => {\n socket.emit(\n \"chat:message\",\n { authority, customerAuthority, message },\n (res: any) => {\n if (res.error) {\n console.error(\"send error\", res.error);\n }\n },\n );\n };\n\n return {\n messages,\n onlineCount,\n sendMessage,\n };\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { X, Send } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useChat, ChatMessage } from \"@/hooks/useChat\";\nimport { format } from \"date-fns\";\n\nexport interface LiveChatTheme {\n primaryColor?: string;\n background?: string;\n textColor?: string;\n}\n\nexport interface LiveChatClassNames {\n container?: string;\n header?: string;\n messages?: string;\n input?: string;\n button?: string;\n}\n\nexport interface LiveChatProps {\n isOpen: boolean;\n onClose: () => void;\n\n // ui\n title?: string;\n\n // customization\n theme?: LiveChatTheme;\n classNames?: LiveChatClassNames;\n\n // slots\n renderMessage?: (message: ChatMessage) => React.ReactNode;\n renderHeader?: () => React.ReactNode;\n authority: string | null;\n customerAuthority: string;\n onConnectWallet?: () => void;\n fallbackMessages?: {\n connectWallet: string;\n description: string;\n };\n}\n\nexport function LiveChat({\n isOpen,\n onClose,\n title = \"Live Chat\",\n theme,\n classNames,\n renderMessage,\n renderHeader,\n authority,\n customerAuthority,\n fallbackMessages,\n onConnectWallet,\n}: LiveChatProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const primary = theme?.primaryColor || \"#FF3D00\";\n const background = theme?.background || \"var(--chat-bg)\";\n\n const { sendMessage, onlineCount, messages } = useChat({\n authority: authority || undefined,\n customerAuthority: customerAuthority,\n });\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n const handleSendMessage = () => {\n if (!inputValue.trim()) return;\n setInputValue(\"\");\n sendMessage(inputValue);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSendMessage();\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <>\n <style>{`\n :root {\n --chat-bg: #ffffff;\n }\n .dark {\n --chat-bg: #15181D;\n }\n `}</style>\n\n {/* Desktop */}\n <div\n onClick={(e) => e.stopPropagation()}\n className={cn(\n \"fixed right-6 z-[110] hidden lg:flex flex-col overflow-hidden shadow-2xl border border-black/10 dark:border-white/10\",\n classNames?.container,\n )}\n style={{\n top: \"75px\",\n width: \"340px\",\n height: !authority ? \"auto\" : \"800px\",\n maxHeight: \"calc(100vh - 90px)\",\n background,\n }}\n >\n {/* Header */}\n {renderHeader ? (\n renderHeader()\n ) : (\n <div\n className={cn(\n \"flex items-center justify-between px-6 py-4 border-b border-black/10 dark:border-white/10\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"dark:text-white text-black\" size={18} />\n </button>\n </div>\n )}\n\n {/* Messages */}\n {authority && (\n <div\n className={cn(\n \"flex-1 overflow-y-auto px-6 py-4 space-y-4\",\n classNames?.messages,\n )}\n >\n {messages.map((message, idx) =>\n renderMessage ? (\n renderMessage(message)\n ) : (\n <div\n key={idx}\n className={`flex flex-col gap-1 ${\n message.authority === authority\n ? \"items-end\"\n : \"items-start\"\n }`}\n >\n {message.authority !== authority && (\n <span className=\"text-xs font-medium text-triad-dark-100 dark:text-white truncate max-w-[120px] truncate max-w-[160px]\">\n {message.name}\n </span>\n )}\n\n <div\n className={cn(\n \"max-w-[75%] rounded-2xl px-4 py-2.5\",\n message.authority === authority\n ? \"text-white rounded-br-sm\"\n : \"bg-black/5 dark:bg-white/5 text-triad-dark-100 dark:text-white rounded-bl-sm\",\n )}\n style={\n message.authority === authority\n ? { backgroundColor: primary }\n : undefined\n }\n >\n <p className=\"text-sm break-words\">{message.message}</p>\n </div>\n\n <span className=\"text-[10px] opacity-50 text-black dark:text-white\">\n {format(message.timestamp, \"HH:mm\")}\n </span>\n </div>\n ),\n )}\n <div ref={messagesEndRef} />\n </div>\n )}\n {/* Input */}\n <div className=\"px-6 py-4 border-t border-black/10 dark:border-white/10\">\n {!authority ? (\n <div className=\"flex flex-col items-center justify-center gap-3 py-4 px-3 text-center\">\n {/* Icon */}\n <div\n className=\"flex items-center justify-center size-12 rounded-2xl\"\n style={{\n background: `${primary}15`,\n }}\n >\n <span className=\"text-xl\">💬</span>\n </div>\n\n {/* Text */}\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-sm font-semibold text-black dark:text-white\">\n {fallbackMessages?.connectWallet || \"Entre no chat\"}\n </span>\n <span className=\"text-xs text-black/60 dark:text-white max-w-[220px]\">\n {fallbackMessages?.description ||\n \"Conecte sua carteira para participar das conversas em tempo real\"}\n </span>\n </div>\n\n {/* Button */}\n {onConnectWallet && (\n <button\n onClick={onConnectWallet}\n style={{ backgroundColor: primary }}\n className=\"mt-2 w-full max-w-[220px] px-4 py-2.5 text-white text-sm font-semibold rounded-xl transition-all hover:opacity-90 active:scale-95 shadow-sm\"\n >\n {fallbackMessages?.connectWallet || \"Connect Wallet\"}\n </button>\n )}\n </div>\n ) : (\n <div className=\"flex items-center gap-2\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 px-4 py-2.5 rounded-xl bg-black/5 text-app-gray-400 dark:bg-white/5 dark:text-white text-sm outline-none\",\n classNames?.input,\n )}\n />\n\n <button\n onClick={handleSendMessage}\n disabled={!inputValue.trim()}\n className={cn(\n \"flex items-center justify-center size-10 rounded-xl transition-colors\",\n classNames?.button,\n )}\n style={{\n backgroundColor: inputValue.trim() ? primary : undefined,\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n >\n <Send size={18} className=\"dark:text-white text-app-gay-400\" />\n </button>\n </div>\n )}\n </div>\n </div>\n\n {/* Mobile */}\n <div className=\"fixed inset-0 z-[110] flex flex-col lg:hidden bg-white dark:bg-app-dark-400\">\n {/* Reaproveita mesma estrutura */}\n <div className=\"flex-1 flex flex-col\">\n {/* Header */}\n <div\n className={cn(\n \"flex items-center justify-between px-6 py-4 border-b border-black/10 dark:border-white/10\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"dark:text-white text-black\" size={18} />\n </button>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto px-4 py-4 space-y-4\">\n {messages.map((m, idx) => (\n <div key={idx}>{m.message}</div>\n ))}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div className=\"p-4 border-t flex flex-col gap-2\">\n {!authority ? (\n <div className=\"flex-1 flex flex-col items-center justify-center gap-2 py-2\">\n <span className=\"text-sm font-medium text-black/60 dark:text-white/60 text-center\">\n {fallbackMessages?.description ||\n \"Faça login para participar do chat\"}\n </span>\n {onConnectWallet && (\n <button\n onClick={onConnectWallet}\n style={{ backgroundColor: primary }}\n className=\"px-4 py-2 text-white text-sm font-semibold rounded-xl transition-opacity hover:opacity-90 active:scale-95\"\n >\n {fallbackMessages?.connectWallet || \"Fazer Login\"}\n </button>\n )}\n </div>\n ) : (\n <div className=\"flex gap-2 w-full\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 px-4 py-2.5 rounded-xl bg-black/5 text-app-gay-400 dark:bg-white/5 dark:text-white text-sm outline-none\",\n classNames?.input,\n )}\n />\n <button\n className={cn(\n \"flex items-center justify-center size-10 rounded-xl transition-colors\",\n classNames?.button,\n )}\n style={{\n backgroundColor: inputValue.trim() ? primary : undefined,\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n onClick={handleSendMessage}\n >\n <Send\n className=\"dark:text-white text-app-gay-400\"\n size={18}\n />\n </button>\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ButtonWidget.tsx","../src/utils/cn.ts","../src/utils/socket.ts","../src/hooks/useChat.ts","../src/components/LiveChat.tsx"],"names":["ButtonWidget","label","variant","className","props","jsx","cn","classes","twMerge","clsx","socket","io","socket_default","useChat","authority","customerAuthority","messages","setMessages","useState","onlineCount","setOnlineCount","normalizedCustomerAuthority","useEffect","handleHistory","msgs","handleMessage","msg","prev","handleUsers","data","joinChat","joinPayload","res","message","resolve","CHAT_MAX_MESSAGE_LENGTH","LiveChat","isOpen","onClose","title","theme","classNames","renderMessage","renderHeader","onConnectWallet","inputValue","setInputValue","messagesEndRef","useRef","primary","background","sendMessage","scrollToBottom","handleSendMessage","handleKeyDown","jsxs","Fragment","X","idx","React","format","Send"],"mappings":"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","file":"index.mjs","sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport interface ButtonWidgetProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n label: string;\n variant?: 'primary' | 'secondary';\n}\n\nexport const ButtonWidget: React.FC<ButtonWidgetProps> = ({ \n label, \n variant = 'primary', \n className = '',\n ...props \n}) => {\n const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors';\n const variants = {\n primary: 'bg-blue-600 text-white hover:bg-blue-700',\n secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300'\n };\n\n return (\n <button \n className={`${baseStyles} ${variants[variant]} ${className}`}\n {...props}\n >\n {label}\n </button>\n );\n};\n","import type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...classes: ClassValue[]) {\n return twMerge(clsx(classes));\n}\n","import io from \"socket.io-client\";\n\nconst socket = io(\"https://beta.triadfi.co\", {\n path: \"/socket.io\",\n reconnection: true,\n reconnectionAttempts: 5,\n reconnectionDelay: 1000,\n randomizationFactor: 0.5,\n timeout: 5000,\n transports: [\"websocket\"],\n});\n\nexport default socket;\n","import socket from \"@/utils/socket\";\nimport { useEffect, useState } from \"react\";\n\nexport interface ChatMessage {\n authority?: string;\n name: string;\n image: string;\n predictionsCount: number;\n message: string;\n timestamp: number;\n}\n\ninterface ChatUsersPayload {\n count: number;\n}\n\ninterface ChatResponse {\n error: string | null;\n}\n\nexport function useChat({\n authority,\n customerAuthority,\n}: {\n authority?: string;\n customerAuthority: string;\n}) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [onlineCount, setOnlineCount] = useState(0);\n const normalizedCustomerAuthority = customerAuthority?.trim();\n\n useEffect(() => {\n if (!normalizedCustomerAuthority) return;\n\n const handleHistory = (msgs: ChatMessage[]) => {\n setMessages(msgs);\n };\n\n const handleMessage = (msg: ChatMessage) => {\n setMessages((prev) => [...prev, msg]);\n };\n\n const handleUsers = (data: ChatUsersPayload) => {\n setOnlineCount(data.count);\n };\n\n const joinChat = () => {\n const joinPayload = authority\n ? { authority, customerAuthority: normalizedCustomerAuthority }\n : { customerAuthority: normalizedCustomerAuthority };\n\n socket.emit(\"chat:join\", joinPayload, (res: ChatResponse) => {\n if (res?.error) {\n console.error(\"join error\", res.error);\n }\n });\n };\n\n socket.on(\"chat:history\", handleHistory);\n socket.on(\"chat:message\", handleMessage);\n socket.on(\"chat:users\", handleUsers);\n socket.on(\"connect\", joinChat);\n joinChat();\n\n return () => {\n socket.off(\"chat:history\", handleHistory);\n socket.off(\"chat:message\", handleMessage);\n socket.off(\"chat:users\", handleUsers);\n socket.off(\"connect\", joinChat);\n };\n }, [authority, normalizedCustomerAuthority]);\n\n const sendMessage = (message: string): Promise<ChatResponse> =>\n new Promise((resolve) => {\n if (!authority || !normalizedCustomerAuthority) {\n resolve({ error: \"INVALID_REQUEST\" });\n return;\n }\n\n socket.emit(\n \"chat:message\",\n { authority, customerAuthority: normalizedCustomerAuthority, message },\n (res: ChatResponse) => {\n if (res?.error) {\n console.error(\"send error\", res.error);\n }\n resolve(res ?? { error: \"INTERNAL_ERROR\" });\n },\n );\n });\n\n return {\n messages,\n onlineCount,\n sendMessage,\n };\n}\n","\"use client\";\n\nimport React, { useState, useRef, useEffect } from \"react\";\nimport { X, Send } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useChat, ChatMessage } from \"@/hooks/useChat\";\nimport { format } from \"date-fns\";\n\nexport interface LiveChatTheme {\n primaryColor?: string;\n background?: string;\n textColor?: string;\n buttonSend: {\n color: string;\n } \n}\n\nexport interface LiveChatClassNames {\n container?: string;\n header?: string;\n messages?: string;\n input?: string;\n button?: string;\n}\n\nexport interface LiveChatProps {\n isOpen: boolean;\n onClose: () => void;\n\n // ui\n title?: string;\n\n // customization\n theme?: LiveChatTheme;\n classNames?: LiveChatClassNames;\n\n // slots\n renderMessage?: (message: ChatMessage) => React.ReactNode;\n renderHeader?: () => React.ReactNode;\n authority: string | null;\n customerAuthority: string;\n onConnectWallet?: () => void;\n fallbackMessages?: {\n connectWallet: string;\n description: string;\n };\n}\n\nconst CHAT_MAX_MESSAGE_LENGTH = 200;\n\nexport function LiveChat({\n isOpen,\n onClose,\n title = \"Live Chat\",\n theme,\n classNames,\n renderMessage,\n renderHeader,\n authority,\n customerAuthority,\n onConnectWallet,\n}: LiveChatProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const primary = theme?.primaryColor || \"#FF3D00\";\n const background = theme?.background || \"var(--chat-bg)\";\n\n const { sendMessage, onlineCount, messages } = useChat({\n authority: authority || undefined,\n customerAuthority: customerAuthority,\n });\n\n const scrollToBottom = () => {\n messagesEndRef.current?.scrollIntoView({ behavior: \"smooth\" });\n };\n\n useEffect(() => {\n scrollToBottom();\n }, [messages]);\n\n const handleSendMessage = () => {\n const message = inputValue.trim();\n if (!message) return;\n\n if (!authority) {\n onConnectWallet?.();\n return;\n }\n\n setInputValue(\"\");\n sendMessage(message);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSendMessage();\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <>\n <style>{`\n :root {\n --chat-bg: #ffffff;\n }\n .dark {\n --chat-bg: #15181D;\n }\n `}</style>\n\n <div\n onClick={(e) => e.stopPropagation()}\n className={cn(\n \"fixed inset-0 z-[110] flex flex-col overflow-hidden bg-white dark:bg-app-dark-400 lg:inset-auto lg:right-6 lg:top-[75px] lg:max-w-[340px] lg:max-h-[calc(100vh-90px)] lg:border lg:border-black/10 lg:shadow-2xl lg:dark:border-white/10\",\n \"lg:h-[800px]\",\n classNames?.container,\n )}\n style={{ background }}\n >\n {renderHeader ? (\n renderHeader()\n ) : (\n <div\n className={cn(\n \"flex items-center justify-between border-b border-black/10 px-4 py-4 dark:border-white/10 lg:px-6\",\n classNames?.header,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-lg font-semibold text-triad-dark-100 dark:text-white\">\n {title}\n </h2>\n <div className=\"flex items-center gap-1.5\">\n <div className=\"size-2 rounded-full bg-green-500 animate-pulse\" />\n <span className=\"text-xs text-app-gray-400 dark:text-white\">\n {onlineCount} online\n </span>\n </div>\n </div>\n <button onClick={onClose}>\n <X className=\"text-black dark:text-white\" size={18} />\n </button>\n </div>\n )}\n\n <div\n className={cn(\n \"flex-1 overflow-y-auto space-y-4 px-4 py-4 lg:px-6\",\n classNames?.messages,\n )}\n >\n {messages.map((message, idx) =>\n renderMessage ? (\n <React.Fragment key={idx}>{renderMessage(message)}</React.Fragment>\n ) : (\n <div\n key={idx}\n className={`flex flex-col gap-1 ${\n message.authority === authority ? \"items-end\" : \"items-start\"\n }`}\n >\n {message.authority !== authority && (\n <span className=\"max-w-[160px] truncate text-xs font-medium text-triad-dark-100 dark:text-white\">\n {message.name}\n </span>\n )}\n\n <div\n className={cn(\n \"max-w-[75%] rounded-2xl px-4 py-2.5\",\n message.authority === authority\n ? \"rounded-br-sm text-white\"\n : \"rounded-bl-sm bg-black/5 text-triad-dark-100 dark:bg-white/5 dark:text-white\",\n )}\n style={\n message.authority === authority\n ? { backgroundColor: primary }\n : undefined\n }\n >\n <p className=\"text-sm break-words\">{message.message}</p>\n </div>\n\n <span className=\"text-[10px] text-black opacity-50 dark:text-white\">\n {format(message.timestamp, \"HH:mm\")}\n </span>\n </div>\n ),\n )}\n <div ref={messagesEndRef} />\n </div>\n\n <div className=\"border-t border-black/10 px-4 py-4 dark:border-white/10 lg:px-6\">\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <input\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n maxLength={CHAT_MAX_MESSAGE_LENGTH}\n placeholder=\"Digite sua mensagem...\"\n className={cn(\n \"flex-1 rounded-xl bg-black/5 px-4 py-2.5 text-sm text-app-gray-400 outline-none dark:bg-white/5 dark:text-white\",\n classNames?.input,\n )}\n />\n\n <button\n onClick={handleSendMessage}\n disabled={!inputValue.trim() || (!authority && !onConnectWallet)}\n className={cn(\n \"flex size-10 items-center justify-center rounded-xl transition-colors bg-app-dark-400\",\n classNames?.button,\n )}\n style={{\n opacity: inputValue.trim() ? 1 : 0.5,\n }}\n >\n <Send color={theme?.buttonSend.color} size={18} />\n </button>\n </div>\n </div>\n </div>\n </div>\n </>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -10,6 +10,9 @@ export interface LiveChatTheme {
|
|
|
10
10
|
primaryColor?: string;
|
|
11
11
|
background?: string;
|
|
12
12
|
textColor?: string;
|
|
13
|
+
buttonSend: {
|
|
14
|
+
color: string;
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export interface LiveChatClassNames {
|
|
@@ -43,6 +46,8 @@ export interface LiveChatProps {
|
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
const CHAT_MAX_MESSAGE_LENGTH = 200;
|
|
50
|
+
|
|
46
51
|
export function LiveChat({
|
|
47
52
|
isOpen,
|
|
48
53
|
onClose,
|
|
@@ -53,7 +58,6 @@ export function LiveChat({
|
|
|
53
58
|
renderHeader,
|
|
54
59
|
authority,
|
|
55
60
|
customerAuthority,
|
|
56
|
-
fallbackMessages,
|
|
57
61
|
onConnectWallet,
|
|
58
62
|
}: LiveChatProps) {
|
|
59
63
|
const [inputValue, setInputValue] = useState("");
|
|
@@ -75,9 +79,16 @@ export function LiveChat({
|
|
|
75
79
|
}, [messages]);
|
|
76
80
|
|
|
77
81
|
const handleSendMessage = () => {
|
|
78
|
-
|
|
82
|
+
const message = inputValue.trim();
|
|
83
|
+
if (!message) return;
|
|
84
|
+
|
|
85
|
+
if (!authority) {
|
|
86
|
+
onConnectWallet?.();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
79
90
|
setInputValue("");
|
|
80
|
-
sendMessage(
|
|
91
|
+
sendMessage(message);
|
|
81
92
|
};
|
|
82
93
|
|
|
83
94
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
@@ -100,28 +111,21 @@ export function LiveChat({
|
|
|
100
111
|
}
|
|
101
112
|
`}</style>
|
|
102
113
|
|
|
103
|
-
{/* Desktop */}
|
|
104
114
|
<div
|
|
105
115
|
onClick={(e) => e.stopPropagation()}
|
|
106
116
|
className={cn(
|
|
107
|
-
"fixed
|
|
117
|
+
"fixed inset-0 z-[110] flex flex-col overflow-hidden bg-white dark:bg-app-dark-400 lg:inset-auto lg:right-6 lg:top-[75px] lg:max-w-[340px] lg:max-h-[calc(100vh-90px)] lg:border lg:border-black/10 lg:shadow-2xl lg:dark:border-white/10",
|
|
118
|
+
"lg:h-[800px]",
|
|
108
119
|
classNames?.container,
|
|
109
120
|
)}
|
|
110
|
-
style={{
|
|
111
|
-
top: "75px",
|
|
112
|
-
width: "340px",
|
|
113
|
-
height: !authority ? "auto" : "800px",
|
|
114
|
-
maxHeight: "calc(100vh - 90px)",
|
|
115
|
-
background,
|
|
116
|
-
}}
|
|
121
|
+
style={{ background }}
|
|
117
122
|
>
|
|
118
|
-
{/* Header */}
|
|
119
123
|
{renderHeader ? (
|
|
120
124
|
renderHeader()
|
|
121
125
|
) : (
|
|
122
126
|
<div
|
|
123
127
|
className={cn(
|
|
124
|
-
"flex items-center justify-between
|
|
128
|
+
"flex items-center justify-between border-b border-black/10 px-4 py-4 dark:border-white/10 lg:px-6",
|
|
125
129
|
classNames?.header,
|
|
126
130
|
)}
|
|
127
131
|
>
|
|
@@ -137,213 +141,87 @@ export function LiveChat({
|
|
|
137
141
|
</div>
|
|
138
142
|
</div>
|
|
139
143
|
<button onClick={onClose}>
|
|
140
|
-
<X className="dark:text-white
|
|
144
|
+
<X className="text-black dark:text-white" size={18} />
|
|
141
145
|
</button>
|
|
142
146
|
</div>
|
|
143
147
|
)}
|
|
144
148
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
149
|
+
<div
|
|
150
|
+
className={cn(
|
|
151
|
+
"flex-1 overflow-y-auto space-y-4 px-4 py-4 lg:px-6",
|
|
152
|
+
classNames?.messages,
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
|
+
{messages.map((message, idx) =>
|
|
156
|
+
renderMessage ? (
|
|
157
|
+
<React.Fragment key={idx}>{renderMessage(message)}</React.Fragment>
|
|
158
|
+
) : (
|
|
159
|
+
<div
|
|
160
|
+
key={idx}
|
|
161
|
+
className={`flex flex-col gap-1 ${
|
|
162
|
+
message.authority === authority ? "items-end" : "items-start"
|
|
163
|
+
}`}
|
|
164
|
+
>
|
|
165
|
+
{message.authority !== authority && (
|
|
166
|
+
<span className="max-w-[160px] truncate text-xs font-medium text-triad-dark-100 dark:text-white">
|
|
167
|
+
{message.name}
|
|
168
|
+
</span>
|
|
169
|
+
)}
|
|
170
|
+
|
|
157
171
|
<div
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
className={cn(
|
|
173
|
+
"max-w-[75%] rounded-2xl px-4 py-2.5",
|
|
160
174
|
message.authority === authority
|
|
161
|
-
? "
|
|
162
|
-
: "
|
|
163
|
-
}`}
|
|
164
|
-
>
|
|
165
|
-
{message.authority !== authority && (
|
|
166
|
-
<span className="text-xs font-medium text-triad-dark-100 dark:text-white truncate max-w-[120px] truncate max-w-[160px]">
|
|
167
|
-
{message.name}
|
|
168
|
-
</span>
|
|
175
|
+
? "rounded-br-sm text-white"
|
|
176
|
+
: "rounded-bl-sm bg-black/5 text-triad-dark-100 dark:bg-white/5 dark:text-white",
|
|
169
177
|
)}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
)}
|
|
178
|
-
style={
|
|
179
|
-
message.authority === authority
|
|
180
|
-
? { backgroundColor: primary }
|
|
181
|
-
: undefined
|
|
182
|
-
}
|
|
183
|
-
>
|
|
184
|
-
<p className="text-sm break-words">{message.message}</p>
|
|
185
|
-
</div>
|
|
186
|
-
|
|
187
|
-
<span className="text-[10px] opacity-50 text-black dark:text-white">
|
|
188
|
-
{format(message.timestamp, "HH:mm")}
|
|
189
|
-
</span>
|
|
178
|
+
style={
|
|
179
|
+
message.authority === authority
|
|
180
|
+
? { backgroundColor: primary }
|
|
181
|
+
: undefined
|
|
182
|
+
}
|
|
183
|
+
>
|
|
184
|
+
<p className="text-sm break-words">{message.message}</p>
|
|
190
185
|
</div>
|
|
191
|
-
),
|
|
192
|
-
)}
|
|
193
|
-
<div ref={messagesEndRef} />
|
|
194
|
-
</div>
|
|
195
|
-
)}
|
|
196
|
-
{/* Input */}
|
|
197
|
-
<div className="px-6 py-4 border-t border-black/10 dark:border-white/10">
|
|
198
|
-
{!authority ? (
|
|
199
|
-
<div className="flex flex-col items-center justify-center gap-3 py-4 px-3 text-center">
|
|
200
|
-
{/* Icon */}
|
|
201
|
-
<div
|
|
202
|
-
className="flex items-center justify-center size-12 rounded-2xl"
|
|
203
|
-
style={{
|
|
204
|
-
background: `${primary}15`,
|
|
205
|
-
}}
|
|
206
|
-
>
|
|
207
|
-
<span className="text-xl">💬</span>
|
|
208
|
-
</div>
|
|
209
186
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<span className="text-sm font-semibold text-black dark:text-white">
|
|
213
|
-
{fallbackMessages?.connectWallet || "Entre no chat"}
|
|
214
|
-
</span>
|
|
215
|
-
<span className="text-xs text-black/60 dark:text-white max-w-[220px]">
|
|
216
|
-
{fallbackMessages?.description ||
|
|
217
|
-
"Conecte sua carteira para participar das conversas em tempo real"}
|
|
187
|
+
<span className="text-[10px] text-black opacity-50 dark:text-white">
|
|
188
|
+
{format(message.timestamp, "HH:mm")}
|
|
218
189
|
</span>
|
|
219
190
|
</div>
|
|
191
|
+
),
|
|
192
|
+
)}
|
|
193
|
+
<div ref={messagesEndRef} />
|
|
194
|
+
</div>
|
|
220
195
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<button
|
|
224
|
-
onClick={onConnectWallet}
|
|
225
|
-
style={{ backgroundColor: primary }}
|
|
226
|
-
className="mt-2 w-full max-w-[220px] px-4 py-2.5 text-white text-sm font-semibold rounded-xl transition-all hover:opacity-90 active:scale-95 shadow-sm"
|
|
227
|
-
>
|
|
228
|
-
{fallbackMessages?.connectWallet || "Connect Wallet"}
|
|
229
|
-
</button>
|
|
230
|
-
)}
|
|
231
|
-
</div>
|
|
232
|
-
) : (
|
|
196
|
+
<div className="border-t border-black/10 px-4 py-4 dark:border-white/10 lg:px-6">
|
|
197
|
+
<div className="flex flex-col gap-2">
|
|
233
198
|
<div className="flex items-center gap-2">
|
|
234
199
|
<input
|
|
235
200
|
value={inputValue}
|
|
236
201
|
onChange={(e) => setInputValue(e.target.value)}
|
|
237
202
|
onKeyDown={handleKeyDown}
|
|
203
|
+
maxLength={CHAT_MAX_MESSAGE_LENGTH}
|
|
238
204
|
placeholder="Digite sua mensagem..."
|
|
239
205
|
className={cn(
|
|
240
|
-
"flex-1 px-4 py-2.5
|
|
206
|
+
"flex-1 rounded-xl bg-black/5 px-4 py-2.5 text-sm text-app-gray-400 outline-none dark:bg-white/5 dark:text-white",
|
|
241
207
|
classNames?.input,
|
|
242
208
|
)}
|
|
243
209
|
/>
|
|
244
210
|
|
|
245
211
|
<button
|
|
246
212
|
onClick={handleSendMessage}
|
|
247
|
-
disabled={!inputValue.trim()}
|
|
213
|
+
disabled={!inputValue.trim() || (!authority && !onConnectWallet)}
|
|
248
214
|
className={cn(
|
|
249
|
-
"flex items-center justify-center
|
|
215
|
+
"flex size-10 items-center justify-center rounded-xl transition-colors bg-app-dark-400",
|
|
250
216
|
classNames?.button,
|
|
251
217
|
)}
|
|
252
218
|
style={{
|
|
253
|
-
backgroundColor: inputValue.trim() ? primary : undefined,
|
|
254
219
|
opacity: inputValue.trim() ? 1 : 0.5,
|
|
255
220
|
}}
|
|
256
221
|
>
|
|
257
|
-
<Send size={18}
|
|
222
|
+
<Send color={theme?.buttonSend.color} size={18} />
|
|
258
223
|
</button>
|
|
259
224
|
</div>
|
|
260
|
-
)}
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
{/* Mobile */}
|
|
265
|
-
<div className="fixed inset-0 z-[110] flex flex-col lg:hidden bg-white dark:bg-app-dark-400">
|
|
266
|
-
{/* Reaproveita mesma estrutura */}
|
|
267
|
-
<div className="flex-1 flex flex-col">
|
|
268
|
-
{/* Header */}
|
|
269
|
-
<div
|
|
270
|
-
className={cn(
|
|
271
|
-
"flex items-center justify-between px-6 py-4 border-b border-black/10 dark:border-white/10",
|
|
272
|
-
classNames?.header,
|
|
273
|
-
)}
|
|
274
|
-
>
|
|
275
|
-
<div className="flex items-center gap-3">
|
|
276
|
-
<h2 className="text-lg font-semibold text-triad-dark-100 dark:text-white">
|
|
277
|
-
{title}
|
|
278
|
-
</h2>
|
|
279
|
-
<div className="flex items-center gap-1.5">
|
|
280
|
-
<div className="size-2 rounded-full bg-green-500 animate-pulse" />
|
|
281
|
-
<span className="text-xs text-app-gray-400 dark:text-white">
|
|
282
|
-
{onlineCount} online
|
|
283
|
-
</span>
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
<button onClick={onClose}>
|
|
287
|
-
<X className="dark:text-white text-black" size={18} />
|
|
288
|
-
</button>
|
|
289
|
-
</div>
|
|
290
|
-
|
|
291
|
-
{/* Messages */}
|
|
292
|
-
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-4">
|
|
293
|
-
{messages.map((m, idx) => (
|
|
294
|
-
<div key={idx}>{m.message}</div>
|
|
295
|
-
))}
|
|
296
|
-
<div ref={messagesEndRef} />
|
|
297
|
-
</div>
|
|
298
|
-
|
|
299
|
-
{/* Input */}
|
|
300
|
-
<div className="p-4 border-t flex flex-col gap-2">
|
|
301
|
-
{!authority ? (
|
|
302
|
-
<div className="flex-1 flex flex-col items-center justify-center gap-2 py-2">
|
|
303
|
-
<span className="text-sm font-medium text-black/60 dark:text-white/60 text-center">
|
|
304
|
-
{fallbackMessages?.description ||
|
|
305
|
-
"Faça login para participar do chat"}
|
|
306
|
-
</span>
|
|
307
|
-
{onConnectWallet && (
|
|
308
|
-
<button
|
|
309
|
-
onClick={onConnectWallet}
|
|
310
|
-
style={{ backgroundColor: primary }}
|
|
311
|
-
className="px-4 py-2 text-white text-sm font-semibold rounded-xl transition-opacity hover:opacity-90 active:scale-95"
|
|
312
|
-
>
|
|
313
|
-
{fallbackMessages?.connectWallet || "Fazer Login"}
|
|
314
|
-
</button>
|
|
315
|
-
)}
|
|
316
|
-
</div>
|
|
317
|
-
) : (
|
|
318
|
-
<div className="flex gap-2 w-full">
|
|
319
|
-
<input
|
|
320
|
-
value={inputValue}
|
|
321
|
-
onChange={(e) => setInputValue(e.target.value)}
|
|
322
|
-
onKeyDown={handleKeyDown}
|
|
323
|
-
placeholder="Digite sua mensagem..."
|
|
324
|
-
className={cn(
|
|
325
|
-
"flex-1 px-4 py-2.5 rounded-xl bg-black/5 text-app-gay-400 dark:bg-white/5 dark:text-white text-sm outline-none",
|
|
326
|
-
classNames?.input,
|
|
327
|
-
)}
|
|
328
|
-
/>
|
|
329
|
-
<button
|
|
330
|
-
className={cn(
|
|
331
|
-
"flex items-center justify-center size-10 rounded-xl transition-colors",
|
|
332
|
-
classNames?.button,
|
|
333
|
-
)}
|
|
334
|
-
style={{
|
|
335
|
-
backgroundColor: inputValue.trim() ? primary : undefined,
|
|
336
|
-
opacity: inputValue.trim() ? 1 : 0.5,
|
|
337
|
-
}}
|
|
338
|
-
onClick={handleSendMessage}
|
|
339
|
-
>
|
|
340
|
-
<Send
|
|
341
|
-
className="dark:text-white text-app-gay-400"
|
|
342
|
-
size={18}
|
|
343
|
-
/>
|
|
344
|
-
</button>
|
|
345
|
-
</div>
|
|
346
|
-
)}
|
|
347
225
|
</div>
|
|
348
226
|
</div>
|
|
349
227
|
</div>
|
package/src/hooks/useChat.ts
CHANGED
|
@@ -10,6 +10,14 @@ export interface ChatMessage {
|
|
|
10
10
|
timestamp: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
interface ChatUsersPayload {
|
|
14
|
+
count: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ChatResponse {
|
|
18
|
+
error: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
export function useChat({
|
|
14
22
|
authority,
|
|
15
23
|
customerAuthority,
|
|
@@ -19,11 +27,10 @@ export function useChat({
|
|
|
19
27
|
}) {
|
|
20
28
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
21
29
|
const [onlineCount, setOnlineCount] = useState(0);
|
|
30
|
+
const normalizedCustomerAuthority = customerAuthority?.trim();
|
|
22
31
|
|
|
23
32
|
useEffect(() => {
|
|
24
|
-
if (!
|
|
25
|
-
|
|
26
|
-
socket.emit("chat:join", { authority, customerAuthority });
|
|
33
|
+
if (!normalizedCustomerAuthority) return;
|
|
27
34
|
|
|
28
35
|
const handleHistory = (msgs: ChatMessage[]) => {
|
|
29
36
|
setMessages(msgs);
|
|
@@ -33,34 +40,54 @@ export function useChat({
|
|
|
33
40
|
setMessages((prev) => [...prev, msg]);
|
|
34
41
|
};
|
|
35
42
|
|
|
36
|
-
const handleUsers = (data:
|
|
43
|
+
const handleUsers = (data: ChatUsersPayload) => {
|
|
37
44
|
setOnlineCount(data.count);
|
|
38
45
|
};
|
|
39
46
|
|
|
47
|
+
const joinChat = () => {
|
|
48
|
+
const joinPayload = authority
|
|
49
|
+
? { authority, customerAuthority: normalizedCustomerAuthority }
|
|
50
|
+
: { customerAuthority: normalizedCustomerAuthority };
|
|
51
|
+
|
|
52
|
+
socket.emit("chat:join", joinPayload, (res: ChatResponse) => {
|
|
53
|
+
if (res?.error) {
|
|
54
|
+
console.error("join error", res.error);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
40
59
|
socket.on("chat:history", handleHistory);
|
|
41
60
|
socket.on("chat:message", handleMessage);
|
|
42
61
|
socket.on("chat:users", handleUsers);
|
|
62
|
+
socket.on("connect", joinChat);
|
|
63
|
+
joinChat();
|
|
43
64
|
|
|
44
65
|
return () => {
|
|
45
|
-
socket.emit("chat:leave", { authority, customerAuthority });
|
|
46
|
-
|
|
47
66
|
socket.off("chat:history", handleHistory);
|
|
48
67
|
socket.off("chat:message", handleMessage);
|
|
49
68
|
socket.off("chat:users", handleUsers);
|
|
69
|
+
socket.off("connect", joinChat);
|
|
50
70
|
};
|
|
51
|
-
}, [authority,
|
|
71
|
+
}, [authority, normalizedCustomerAuthority]);
|
|
52
72
|
|
|
53
|
-
const sendMessage = (message: string) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
const sendMessage = (message: string): Promise<ChatResponse> =>
|
|
74
|
+
new Promise((resolve) => {
|
|
75
|
+
if (!authority || !normalizedCustomerAuthority) {
|
|
76
|
+
resolve({ error: "INVALID_REQUEST" });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
socket.emit(
|
|
81
|
+
"chat:message",
|
|
82
|
+
{ authority, customerAuthority: normalizedCustomerAuthority, message },
|
|
83
|
+
(res: ChatResponse) => {
|
|
84
|
+
if (res?.error) {
|
|
85
|
+
console.error("send error", res.error);
|
|
86
|
+
}
|
|
87
|
+
resolve(res ?? { error: "INTERNAL_ERROR" });
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
});
|
|
64
91
|
|
|
65
92
|
return {
|
|
66
93
|
messages,
|