@pubuduth-aplicy/chat-ui 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # React + TypeScript + Vite
2
+
3
+ This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
+
5
+ Currently, two official plugins are available:
6
+
7
+ - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8
+ - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
+
10
+ ## Expanding the ESLint configuration
11
+
12
+ If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13
+
14
+ - Configure the top-level `parserOptions` property like this:
15
+
16
+ ```js
17
+ export default tseslint.config({
18
+ languageOptions: {
19
+ // other options...
20
+ parserOptions: {
21
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
22
+ tsconfigRootDir: import.meta.dirname,
23
+ },
24
+ },
25
+ })
26
+ ```
27
+
28
+ - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29
+ - Optionally add `...tseslint.configs.stylisticTypeChecked`
30
+ - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
31
+
32
+ ```js
33
+ // eslint.config.js
34
+ import react from 'eslint-plugin-react'
35
+
36
+ export default tseslint.config({
37
+ // Set the react version
38
+ settings: { react: { version: '18.3' } },
39
+ plugins: {
40
+ // Add the react plugin
41
+ react,
42
+ },
43
+ rules: {
44
+ // other rules...
45
+ // Enable its recommended rules
46
+ ...react.configs.recommended.rules,
47
+ ...react.configs['jsx-runtime'].rules,
48
+ },
49
+ })
50
+ ```
@@ -0,0 +1,28 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ },
27
+ },
28
+ )
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@pubuduth-aplicy/chat-ui",
3
+ "version": "2.0.5",
4
+ "description": "This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "src/index.tsx",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsc ",
12
+ "prepare": "npm run build"
13
+ },
14
+ "dependencies": {
15
+ "@tanstack/react-query": "^5.67.2",
16
+ "axios": "^1.8.2",
17
+ "react": "^19.0.0",
18
+ "react-dom": "^19.0.0",
19
+ "socket.io-client": "^4.8.1",
20
+ "zustand": "^5.0.3"
21
+ },
22
+ "devDependencies": {
23
+ "@eslint/js": "^9.21.0",
24
+ "@types/react": "^19.0.10",
25
+ "@types/react-dom": "^19.0.4",
26
+ "@vitejs/plugin-react": "^4.3.4",
27
+ "eslint": "^9.21.0",
28
+ "eslint-plugin-react-hooks": "^5.0.0",
29
+ "eslint-plugin-react-refresh": "^0.4.19",
30
+ "globals": "^15.15.0",
31
+ "typescript": "~5.7.2",
32
+ "typescript-eslint": "^8.24.1",
33
+ "vite": "^6.2.0"
34
+ }
35
+ }
@@ -0,0 +1,15 @@
1
+ // import { ChatWindowProps } from "./types/type";
2
+
3
+
4
+ // export const ChatWindow: React.FC<ChatWindowProps> = ({ userId}) => {
5
+ // // const { messages, sendMessage, sendFile } = useChat(userId);
6
+ // // const [file, setFile] = useState<File | null>(null);
7
+
8
+ // return (
9
+ // <div className="w-full max-w-lg border p-4 rounded-lg shadow-lg bg-white">
10
+ // <MessageList messages={messages} />
11
+ // <FileUploader file={file} setFile={setFile} onUpload={sendFile} />
12
+ // <MessageInput onSend={sendMessage} />
13
+ // </div>
14
+ // );
15
+ // };
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 21 3 C 11.621094 3 4 10.621094 4 20 C 4 29.378906 11.621094 37 21 37 C 24.710938 37 28.140625 35.804688 30.9375 33.78125 L 44.09375 46.90625 L 46.90625 44.09375 L 33.90625 31.0625 C 36.460938 28.085938 38 24.222656 38 20 C 38 10.621094 30.378906 3 21 3 Z M 21 5 C 29.296875 5 36 11.703125 36 20 C 36 28.296875 29.296875 35 21 35 C 12.703125 35 6 28.296875 6 20 C 6 11.703125 12.703125 5 21 5 Z"/></svg>
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,52 @@
1
+ import { Sidebar } from './sidebar/Sidebar'
2
+ // import MessageContainer from './components/messages/MessageContainer'
3
+ // import useConversation from '../../zustand/useConversation';
4
+
5
+
6
+
7
+ export const Chat = () => {
8
+ // const { selectedConversation, setSelectedConversation } = useConversation();
9
+ return (
10
+ <>
11
+ <div className='container mx-auto mb-5'>
12
+ <div className='grid grid-cols-1 md:grid-cols-9 sm:h-[450px] md:h-[550px] rounded-lg overflow-hidden bg-gray-400 bg-clip-padding backdrop-filter backdrop-blur-lg bg-opacity-0'>
13
+ <div/>
14
+ {/* {selectedConversation? (
15
+ <><div className={`xs:hidden md:grid md:col-span-3 border max-h-screen overflow-y-auto`}><Sidebar /></div>
16
+ <div className='md:col-span-4 border max-h-screen overflow-y-auto '>
17
+ <MessageContainer />
18
+ </div></>
19
+ ):( */}
20
+ <><div className='md:col-span-3 border max-h-screen overflow-y-auto '><Sidebar /></div>
21
+ <div className='xs:hidden md:grid md:col-span-4 border '>
22
+ {/* <MessageContainer /> */}
23
+ </div></>
24
+ {/* )} */}
25
+ </div>
26
+ </div>
27
+ </>
28
+ )
29
+ }
30
+
31
+ // const MessageBody = () => {
32
+
33
+
34
+ // return (
35
+ // <><div className='flex-grow px-8 pt-8 text-left text-gray-700 overflow-y-auto'>
36
+ // <div className="relative mb-6 text-left">
37
+ // <div className="text-gray-700">
38
+ // <div className="absolute inset-x-0 top-0">
39
+ // <img src="/images/fR71TFZIDTv2jhvKsOMhC.png" alt className="float-right inline-block h-6 w-6 sm:h-12 sm:w-12 rounded-full" />
40
+ // </div>
41
+ // <div className="relative float-right mr-8 sm:mr-16 inline-block rounded-md bg-blue-700 py-3 px-4 text-white">
42
+ // <p className="text-sm">Hi, John</p>
43
+ // </div>
44
+ // </div>
45
+ // <div className="clear-both flex text-gray-700" />
46
+ // </div>
47
+ // </div><div className="relative mt-4 flex items-start border-t border-gray-300 sm:p-8 py-4 text-left text-gray-700">
48
+ // <input placeholder="Your Message" className="mr-4 overflow-hidden w-full flex-1 cursor-text resize-none whitespace-pre-wrap rounded-md bg-white text-sm py-2 sm:py-0 font-normal text-gray-600 opacity-70 shadow-none outline-none focus:text-gray-600 focus:opacity-100" defaultValue={""} />
49
+ // <button className="relative inline-flex h-10 w-auto flex-initial cursor-pointer items-center justify-center self-center rounded-md bg-blue-700 px-6 text-center align-middle text-sm font-medium text-white outline-none focus:ring-2">Send</button>
50
+ // </div></>
51
+ // );
52
+ // };
@@ -0,0 +1,39 @@
1
+ // /* eslint-disable @typescript-eslint/no-unused-vars */
2
+
3
+ // // import { useAuthContext } from "../../context/AuthContext";
4
+ // // import { extractTime } from "../../utils/extractTime";
5
+ // // import useConversation from "../../zustand/useConversation";
6
+ // const Message = ({message:any}) => {
7
+
8
+ // // const { authUser } = useAuthContext();
9
+ // // const { selectedConversation } = useConversation();
10
+ // // const fromMe = message.senderId === authUser._id;
11
+ // // const formattedTime = extractTime(message.createdAt);
12
+ // // const timeside = fromMe ? "float-right" : "float-left";
13
+ // // const profilePic = fromMe ? authUser.profilePic : selectedConversation?.profilePic;
14
+ // // const bubbleBgColor = fromMe ? "bg-green-400" : "bg-blue-700";
15
+ // // const alignItems = fromMe ? "float-right" : "float-left";
16
+ // // const shakeClass = message.shouldShake ? "shake" : "";
17
+ // // const imageside= fromMe ? "inset-x-0" :"left-0";
18
+
19
+ // return (
20
+ // <>
21
+ // <div className='flex-grow px-8 pt-8 max-h-screen text-left text-gray-700 overflow-y-auto'>
22
+ // <div className="relative mb-6 text-left">
23
+ // <div className="text-gray-700">
24
+ // <div className={`relative block rounded-md py-3 px-4 text-white break-all overflow-wrap`}>
25
+ // <p className="text-sm">dgfddfgfdg</p>
26
+ // <div className='chat-footer opacity-50 text-xs flex gap-1 items-center float-right'>
27
+ // 15.44
28
+ // </div>
29
+ // </div>
30
+ // </div>
31
+ // {/* <div className="clear-both flex text-gray-700" /> */}
32
+ // </div>
33
+ // </div>
34
+
35
+ // </>
36
+ // )
37
+ // }
38
+
39
+ // export default Message
@@ -0,0 +1,81 @@
1
+ // import { useEffect } from "react";
2
+ // // import useConversation from "../../zustand/useConversation";
3
+ // import MessageInput from "./MessageInput";
4
+ // import Messages from "./Messages";
5
+ // // import { Chat, CaretLeft } from "@phosphor-icons/react";
6
+ // // import { useAuthContext } from "../../context/AuthContext";
7
+
8
+
9
+ // const MessageContainer = () => {
10
+ // // const { selectedConversation, setSelectedConversation } = useConversation();
11
+ // const selectedConversation=true
12
+ // // useEffect(() => {
13
+ // // // cleanup function (unmounts)
14
+ // // return () => setSelectedConversation(null);
15
+ // // }, [setSelectedConversation]);
16
+
17
+ // return (
18
+ // <div className='flex flex-col justify-center h-full'>
19
+
20
+ // {!selectedConversation ? (
21
+ // <NoChatSelected />
22
+ // ) : (
23
+ // <>
24
+ // <div className="h-20 sticky top-0 border-b border-gray-300 bg-white py-5 px-8 text-left text-sm text-gray-800 z-50">
25
+ // <div className="grow shrink basis-0 self-stretch justify-start items-center px-4 gap-4 flex">
26
+ // <button className="text-blue-500 md:hidden">
27
+ // {/* <CaretLeft size={25} /> */}
28
+ // </button>
29
+ // <img className="w-10 h-10 rounded-circle" alt="Profile" />
30
+ // <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">
31
+ // <div className="self-stretch justify-start items-center inline-flex">
32
+ // <div className="grow shrink basis-0 text-slate-900 text-base font-semibold font-inter leading-tight">
33
+ // {/* {selectedConversation.username} */}
34
+ // </div>
35
+ // </div>
36
+ // </div>
37
+ // {/* <h4 className=" inline-block py-2 text-left font-sans font-semibold normal-case">Lara Abegnale</h4> */}
38
+ // </div>
39
+ // </div>
40
+
41
+ // {/* <div className="h-14 overflow-x-hidden">
42
+ // <div className="top-0 h-14 px-4 py-4 w-full border-b border-gray-300 justify-start items-start gap-2 inline-flex sticky z-10">
43
+ // <div className="grow shrink basis-0 self-stretch py-2 justify-start items-center gap-4 flex">
44
+ // <button onClick={() => setSelectedConversation(null)} className="text-blue-500 md:hidden">
45
+ // <CaretLeft size={25} />
46
+ // </button>
47
+ // <img className="w-10 h-10 rounded-circle" src={selectedConversation.profile} alt="Profile" />
48
+ // <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">
49
+ // <div className="self-stretch justify-start items-center inline-flex">
50
+ // <div className="grow shrink basis-0 text-slate-900 text-base font-semibold font-inter leading-tight">
51
+ // {selectedConversation.username}
52
+ // </div>
53
+ // </div>
54
+ // </div>
55
+ // </div>
56
+ // </div>
57
+ // </div> */}
58
+
59
+ // <Messages />
60
+ // <MessageInput />
61
+ // </>
62
+ // )}
63
+ // </div>
64
+ // );
65
+ // }
66
+
67
+ // export default MessageContainer;
68
+
69
+ // const NoChatSelected = () => {
70
+ // // const { authUser } = useAuthContext();
71
+
72
+ // return (
73
+ // <div className='flex items-center justify-center w-full h-full'>
74
+ // <div className='px-4 text-center sm:text-lg md:text-xl text-gray-200 font-semibold flex flex-col items-center gap-2'>
75
+ // <p>Welcome </p> {/* Safely access username */}
76
+ // <p>Select a chat to start messaging</p>
77
+ // {/* <Chat className='text-3xl md:text-6xl text-center' /> */}
78
+ // </div>
79
+ // </div>
80
+ // );
81
+ // };
@@ -0,0 +1,37 @@
1
+ // import React, { useState } from 'react';
2
+ // // import { PaperPlaneRight } from '@phosphor-icons/react'; // Assuming you're using icons from Phosphor Icons library
3
+ // // import useSendMessage from '../../hooks/useSendMessage'; // Importing the useSendMessage hook
4
+
5
+ // const MessageInput = () => {
6
+ // const [message, setMessage] = useState(""); // State for storing the message input
7
+ // // const { loading, sendMessage } = useSendMessage(); // Custom hook for sending messages
8
+
9
+ // // const handleSubmit = async (e) => {
10
+ // // e.preventDefault();
11
+ // // if (!message)
12
+ // // return;
13
+ // // await sendMessage(message);
14
+ // // setMessage("");
15
+ // // };
16
+
17
+ // return (
18
+ // <form className='px-4 my-3 sticky bottom-0'>
19
+
20
+ // <div className='w-full '>
21
+ // <input
22
+ // type='text'
23
+ // className='border text-white text-sm border-none block outline-none w-full p-2.5 rounded-2xl bg-slate-500 '
24
+ // placeholder='Send a message'
25
+ // value={message}
26
+ // onChange={(e) => setMessage(e.target.value)} // Update message state as the user types
27
+ // />
28
+ // <button type='submit' className='absolute inset-y-0 end-0 flex items-center pe-8'>
29
+ // send
30
+ // {/* {loading ? <div className='loading loading-spinner'></div> : <PaperPlaneRight />} Show loading spinner if loading */}
31
+ // </button>
32
+ // </div>
33
+ // </form>
34
+ // );
35
+ // };
36
+
37
+ // export default MessageInput;
@@ -0,0 +1,51 @@
1
+ // import { useEffect, useRef } from "react";
2
+ // // import useGetMessages from "../../hooks/useGetMessages";
3
+ // // import MessageSkeleton from "../skeletons/MessageSkeleton";
4
+ // import Message from "./Message";
5
+ // // import useListenMessages from "../../hooks/useListenMessages";
6
+
7
+ // const Messages = () => {
8
+ // // const { messages, loading } = useGetMessages();
9
+ // // useListenMessages();
10
+ // // const lastMessageRef = useRef();
11
+
12
+ // // useEffect(() => {
13
+ // // setTimeout(() => {
14
+ // // lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
15
+ // // }, 100);
16
+ // // }, [messages]);
17
+
18
+ // return (
19
+ // // <div className='px-4 flex-1 overflow-auto'>
20
+ // // {!loading &&
21
+ // // messages.length > 0 &&
22
+ // // messages.map((message) => (
23
+ // // <div key={message._id} ref={lastMessageRef}>
24
+ // // <Message message={message} />
25
+ // // </div>
26
+ // // ))}
27
+
28
+ // // {loading && [...Array(3)].map((_, idx) => <MessageSkeleton key={idx} />)}
29
+ // // {!loading && messages.length === 0 && (
30
+ // // <p className='text-center'>Send a message to start the conversation</p>
31
+ // // )}
32
+ // // </div>
33
+
34
+ // <div className='px-4 flex-1 overflow-auto sm:px-6 lg:px-8'>
35
+ // {/* {!loading &&
36
+ // messages.length > 0 &&
37
+ // messages.map((message) => ( */}
38
+ // {/* <div key={message._id} ref={lastMessageRef}>
39
+ // <Message message={message} />
40
+ // </div> */}
41
+ // {/* // ))} */}
42
+
43
+ // {/* {loading && [...Array(3)].map((_, idx) => <MessageSkeleton key={idx} />)}
44
+ // {!loading && messages.length === 0 && (
45
+ // <p className='text-center'>Send a message to start the conversation</p>
46
+ // )} */}
47
+ // </div>
48
+ // );
49
+ // };
50
+ // export default Messages;
51
+
@@ -0,0 +1,46 @@
1
+ // import { useSocketContext } from "../../context/SocketContext";
2
+ // import useConversation from "../../zustand/useConversation";
3
+
4
+ import useChatUIStore from "../../stores/Zustant";
5
+
6
+ interface ConversationProps {
7
+ conversation: {
8
+ _id: string;
9
+ profilePic: string;
10
+ username: string;
11
+ };
12
+ lastIdx: boolean;
13
+ }
14
+
15
+ const Conversation = ({ conversation, lastIdx }: ConversationProps) => {
16
+ const { setSelectedConversation } = useChatUIStore();
17
+
18
+ // const isSelected = selectedConversation?._id === conversation._id;
19
+ // const { onlineUsers } = useSocketContext();
20
+ // const isOnline = onlineUsers.includes(conversation._id);
21
+ return (
22
+ <>
23
+ <div
24
+ className={` h-[72px] p-2 py-3 hover:bg-sky-500 rounded justify-start items-center cursor-pointer gap-4 inline-flex
25
+ `}
26
+ onClick={() => setSelectedConversation(conversation)}
27
+ >
28
+ <img
29
+ className="w-12 h-12 relative rounded-[100px]"
30
+ src={conversation.profilePic}
31
+ />
32
+ <div className="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">
33
+ <div className="self-stretch justify-start items-center inline-flex">
34
+ <div className="grow shrink basis-0 text-slate-900 text-base font-semibold font-['Inter'] leading-tight">
35
+ {conversation.username}
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ {!lastIdx && <div className="divider my-0 py-0 h-1" />}
42
+ </>
43
+ );
44
+ };
45
+
46
+ export default Conversation;
@@ -0,0 +1,25 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { useGetConversations } from "../../hooks/queries/useChatApi";
4
+ import Conversation from "./Conversation";
5
+
6
+ const Conversations = () => {
7
+ const { data: conversations } = useGetConversations();
8
+
9
+ // const { loading, conversations } = useGetConversations();
10
+ return (
11
+ <div className='py-2 flex flex-col overflow-auto'>
12
+ {conversations.map((conversation:any, idx:any) => (
13
+ <Conversation
14
+ key={conversation._id}
15
+ conversation={conversation}
16
+ lastIdx={idx === conversations.length - 1}
17
+ />
18
+ ))}
19
+
20
+ {/* {loading ? <span className='loading loading-spinner mx-auto'></span> : null} */}
21
+ </div>
22
+ )
23
+ }
24
+
25
+ export default Conversations
@@ -0,0 +1,57 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { useState } from 'react'
3
+ import searchicon from '../../assets/icons8-search.svg'
4
+ import useChatUIStore from '../../stores/Zustant';
5
+ import { useGetConversations } from '../../hooks/queries/useChatApi';
6
+ // import { MagnifyingGlass } from "@phosphor-icons/react"
7
+ // import useGetConversations from "../../hooks/useGetConversations";
8
+ // import useConversation from '../../zustand/useConversation'
9
+ // import toast from 'react-hot-toast';
10
+
11
+ const SearchInput = () => {
12
+
13
+ const [search, setSearch] = useState("");
14
+ const { setSelectedConversation } = useChatUIStore();
15
+ const { data: conversations } = useGetConversations();
16
+
17
+ const handleSubmit = (e:any) => {
18
+ e.preventDefault();
19
+ if (!search) return;
20
+ if (search.length < 3) {
21
+ return
22
+ }
23
+
24
+ const conversation = conversations.find((c: { username: string }) => c.username.toLowerCase().includes(search.toLowerCase()));
25
+
26
+ if (conversation) {
27
+ setSelectedConversation(conversation);
28
+ setSearch("");
29
+ }
30
+ console.error("No such user found!");
31
+ };
32
+
33
+
34
+ return (
35
+ <>
36
+
37
+ <form onSubmit={handleSubmit}>
38
+ <div className="self-stretch h-14 px-4 py-2 justify-start items-center gap-4 inline-flex">
39
+ <div className="grow shrink basis-0 h-10 px-4 py-2 bg-neutral-100 rounded-[22px] justify-start items-center gap-4 flex">
40
+ <img src={searchicon} className="w-6 h-6 pl-[2.50px] pr-[3px] pt-[2.50px] pb-[3px] justify-center items-center flex" />
41
+ <input className="w-full h-5 text-slate-500 bg-neutral-100 outline-none text-base font-normal font-['Inter'] leading-tight"
42
+ placeholder='Search…'
43
+ value={search}
44
+ onChange={(e) => setSearch(e.target.value)}
45
+ />
46
+ </div>
47
+ </div>
48
+ </form>
49
+ </>
50
+
51
+
52
+
53
+
54
+ )
55
+ }
56
+
57
+ export default SearchInput
@@ -0,0 +1,13 @@
1
+ import Conversations from "./Conversations";
2
+ import SearchInput from "./SearchInput";
3
+
4
+ export const Sidebar = () => {
5
+ return (
6
+ <div className='flex-col border-r p-4 flex '>
7
+ <SearchInput />
8
+ <div className='divider px-3'></div>
9
+ <Conversations />
10
+
11
+ </div>
12
+ )
13
+ }
@@ -0,0 +1,12 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { getAllConversationData } from "../../service/sidebarApi";
3
+
4
+
5
+ export const useGetConversations = () => {
6
+ return useQuery({
7
+ queryKey: ['GET_ALL_CONVERSATION'],
8
+ queryFn: getAllConversationData,
9
+ staleTime: 1 * 60 * 1000,
10
+ refetchOnWindowFocus: false,
11
+ });
12
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,2 @@
1
+ export { ChatProvider } from './providers/ChatProvider';
2
+ export { Chat } from './components/Chat';
@@ -0,0 +1,12 @@
1
+ import axios from "axios";
2
+
3
+ export const apiClient = axios.create({
4
+ baseURL: import.meta.env.VITE_APP_BACKEND_PORT,
5
+ timeout: 5000,
6
+ withCredentials:true,
7
+ headers: {
8
+ 'Content-Type': 'application/json',
9
+ },
10
+ });
11
+
12
+
@@ -0,0 +1,5 @@
1
+ export const Path = {
2
+ chat: "/chat",
3
+ getconversation: "/getConversation",
4
+ getmessage: "/getMessage",
5
+ };
@@ -0,0 +1,60 @@
1
+ import React, { createContext, useContext, ReactNode, useEffect, useState } from 'react';
2
+ import { Socket, io } from 'socket.io-client';
3
+ // import { apiClient } from '../lib/api/apiClient';
4
+ // import { S3Client } from '../lib/storage/s3Client';
5
+ // import { CryptoUtils } from '../lib/encryption/cryptoUtils';
6
+
7
+ interface ChatProviderProps {
8
+ // apiUrl: string;
9
+ // s3Config: {
10
+ // bucket: string;
11
+ // region: string;
12
+ // accessKeyId: string;
13
+ // secretAccessKey: string;
14
+ // };
15
+ userId: string; // User ID for identification
16
+ children: ReactNode;
17
+ }
18
+
19
+ interface ChatContextType {
20
+ // s3Client: S3Client;
21
+ socket: Socket;
22
+ // cryptoUtils: CryptoUtils;
23
+ userId: string;
24
+ }
25
+
26
+ const ChatContext = createContext<ChatContextType | null>(null);
27
+
28
+ export const ChatProvider: React.FC<ChatProviderProps> = ({ userId, children }) => {
29
+ const [socket, setSocket] = useState<Socket | null>(null);
30
+ const apiUrl=import.meta.env.VITE_APP_BACKEND_PORT
31
+
32
+ useEffect(() => {
33
+ const socketInstance = io(apiUrl, { auth: { userId } });
34
+ setSocket(socketInstance);
35
+
36
+ return () => {
37
+ socketInstance.disconnect();
38
+ };
39
+ }, [apiUrl, userId]);
40
+
41
+ if (!socket) return null;
42
+
43
+ // const apiClient = new ApiClient(apiUrl);
44
+ // const s3Client = new S3Client(s3Config);
45
+ // const cryptoUtils = new CryptoUtils();
46
+
47
+ return (
48
+ <ChatContext.Provider value={{ socket, userId }}>
49
+ {children}
50
+ </ChatContext.Provider>
51
+ );
52
+ };
53
+
54
+ export const useChatContext = () => {
55
+ const context = useContext(ChatContext);
56
+ if (!context) {
57
+ throw new Error('useChatContext must be used within a ChatProvider');
58
+ }
59
+ return context;
60
+ };
@@ -0,0 +1,14 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { apiClient } from "../lib/api/apiClient";
3
+ import { Path } from "../lib/api/endpoint";
4
+
5
+ export const getAllConversationData = async () => {
6
+ try {
7
+ const res = await apiClient.get(Path.getconversation);
8
+ return res.data;
9
+ } catch (error: any) {
10
+ console.log("ERROR: ", error);
11
+ // logger.error(error);
12
+ throw error;
13
+ }
14
+ };
@@ -0,0 +1,35 @@
1
+ import {create} from 'zustand';
2
+
3
+ // interface ChatUIState {
4
+ // isChatOpen: boolean;
5
+ // unreadCount: number;
6
+ // toggleChat: () => void;
7
+ // incrementUnreadCount: () => void;
8
+ // resetUnreadCount: () => void;
9
+ // }
10
+
11
+ interface ChatUIState {
12
+ isChatOpen: boolean;
13
+ unreadCount: number;
14
+ selectedConversation: {
15
+ _id: string;
16
+ profilePic: string;
17
+ username: string;
18
+ } | null;
19
+ setSelectedConversation: (selectedConversation: { _id: string; profilePic: string; username: string; } | null) => void;
20
+ toggleChat: () => void;
21
+ incrementUnreadCount: () => void;
22
+ resetUnreadCount: () => void;
23
+ }
24
+
25
+ const useChatUIStore = create<ChatUIState>((set) => ({
26
+ isChatOpen: false,
27
+ unreadCount: 0,
28
+ selectedConversation: null,
29
+ setSelectedConversation: (selectedConversation) => set({ selectedConversation }),
30
+ toggleChat: () => set((state) => ({ isChatOpen: !state.isChatOpen })),
31
+ incrementUnreadCount: () => set((state) => ({ unreadCount: state.unreadCount + 1 })),
32
+ resetUnreadCount: () => set({ unreadCount: 0 }),
33
+ }));
34
+
35
+ export default useChatUIStore;
@@ -0,0 +1,3 @@
1
+ export interface ChatWindowProps {
2
+ userId: string;
3
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "isolatedModules": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["src"]
26
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" }
5
+ ]
6
+ }