├── README.md
├── public
├── Online.png
├── People.png
├── Person.png
├── Talkto.png
├── apple.png
├── login.png
├── logo.png
├── signIn.png
├── Rectangle 820.png
├── Rectangle 824.png
├── Rectangle 826.png
├── icons
│ ├── clicks.png
│ ├── call-stop.png
│ ├── sound-box.png
│ ├── zoom-chats.png
│ └── double-danger.png
├── router.json
└── vite.svg
├── src
├── App.css
├── App.jsx
├── layouts
│ ├── Main.jsx
│ └── Sidebar.jsx
├── index.css
├── main.jsx
├── router
│ └── index.jsx
├── pages
│ ├── Homepage.jsx
│ ├── VideoChat.jsx
│ ├── StartChatPage.jsx
│ ├── SignIn.jsx
│ ├── SignUp.jsx
│ ├── CallingPage.jsx
│ ├── MainPage.jsx
│ └── ChatPage.jsx
└── assets
│ └── react.svg
├── vite.config.js
├── .gitignore
├── index.html
├── package.json
└── eslint.config.js
/README.md:
--------------------------------------------------------------------------------
1 | # ZoomerangZ-desktop-Frontend
--------------------------------------------------------------------------------
/public/Online.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Online.png
--------------------------------------------------------------------------------
/public/People.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/People.png
--------------------------------------------------------------------------------
/public/Person.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Person.png
--------------------------------------------------------------------------------
/public/Talkto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Talkto.png
--------------------------------------------------------------------------------
/public/apple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/apple.png
--------------------------------------------------------------------------------
/public/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/login.png
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/logo.png
--------------------------------------------------------------------------------
/public/signIn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/signIn.png
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #161515; /* change to your desired background */
3 | }
4 |
--------------------------------------------------------------------------------
/public/Rectangle 820.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Rectangle 820.png
--------------------------------------------------------------------------------
/public/Rectangle 824.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Rectangle 824.png
--------------------------------------------------------------------------------
/public/Rectangle 826.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/Rectangle 826.png
--------------------------------------------------------------------------------
/public/icons/clicks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/icons/clicks.png
--------------------------------------------------------------------------------
/public/icons/call-stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/icons/call-stop.png
--------------------------------------------------------------------------------
/public/icons/sound-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/icons/sound-box.png
--------------------------------------------------------------------------------
/public/icons/zoom-chats.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/icons/zoom-chats.png
--------------------------------------------------------------------------------
/public/icons/double-danger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Vector-6/React-Chatting/HEAD/public/icons/double-danger.png
--------------------------------------------------------------------------------
/public/router.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "path": "/",
4 | "component": "HomePage"
5 | },
6 | {
7 | "path": "/homepage",
8 | "component": "HomePage"
9 | }
10 | ]
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import Router from './router';
3 | import Main from './layouts/Main';
4 |
5 | function App() {
6 | return ;
7 | }
8 |
9 | export default App;
10 |
--------------------------------------------------------------------------------
/src/layouts/Main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Main = ({ children }) => {
4 | return
{children}
;
5 | };
6 |
7 | export default Main;
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | ::-webkit-scrollbar{
4 | width: 20px;
5 |
6 | }
7 | ::-webkit-slider-thumb{
8 | color: #349393;
9 | }
10 | ::-webkit-scrollbar-track{
11 | background: 349393;
12 | }
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import './index.css'
4 | import App from './App.jsx'
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react-swc'
3 | import tailwindcss from '@tailwindcss/vite'
4 |
5 | // https://vite.dev/config/
6 | export default defineConfig({
7 | plugins: [
8 | tailwindcss(),
9 | react(),
10 | ],
11 | })
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zoomerang-frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@tailwindcss/vite": "^4.0.17",
14 | "lucide-react": "^0.484.0",
15 | "react": "^19.0.0",
16 | "react-dom": "^19.0.0",
17 | "react-router-dom": "^7.4.0",
18 | "swiper": "^11.2.6",
19 | "tailwindcss": "^4.0.17"
20 | },
21 | "devDependencies": {
22 | "@eslint/js": "^9.21.0",
23 | "@types/react": "^19.0.10",
24 | "@types/react-dom": "^19.0.4",
25 | "@vitejs/plugin-react-swc": "^3.8.0",
26 | "eslint": "^9.21.0",
27 | "eslint-plugin-react-hooks": "^5.1.0",
28 | "eslint-plugin-react-refresh": "^0.4.19",
29 | "globals": "^15.15.0",
30 | "vite": "^6.2.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
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 |
6 | export default [
7 | { ignores: ['dist'] },
8 | {
9 | files: ['**/*.{js,jsx}'],
10 | languageOptions: {
11 | ecmaVersion: 2020,
12 | globals: globals.browser,
13 | parserOptions: {
14 | ecmaVersion: 'latest',
15 | ecmaFeatures: { jsx: true },
16 | sourceType: 'module',
17 | },
18 | },
19 | plugins: {
20 | 'react-hooks': reactHooks,
21 | 'react-refresh': reactRefresh,
22 | },
23 | rules: {
24 | ...js.configs.recommended.rules,
25 | ...reactHooks.configs.recommended.rules,
26 | 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
27 | 'react-refresh/only-export-components': [
28 | 'warn',
29 | { allowConstantExport: true },
30 | ],
31 | },
32 | },
33 | ]
34 |
--------------------------------------------------------------------------------
/src/router/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
3 | import Homepage from '../pages/Homepage';
4 | import SignIn from '../pages/SignIn';
5 | import SignUp from '../pages/SignUp';
6 | import MainPage from '../pages/MainPage';
7 | import StartChatPage from '../pages/StartChatPage';
8 | import ChatPage from '../pages/ChatPage';
9 | import CallingPage from '../pages/CallingPage';
10 | import VideoChat from '../pages/VideoChat';
11 | import Sidebar from '../layouts/Sidebar';
12 | import Main from '../layouts/Main';
13 |
14 | const AppRouter = () => {
15 | return (
16 |
17 |
18 |
19 |
20 | } />
21 | } />
22 | } />
23 | } />
24 | } />
25 | } />
26 | } />
27 | } />
28 |
29 |
30 |
31 | );
32 | };
33 |
34 | export default AppRouter;
35 |
--------------------------------------------------------------------------------
/src/pages/Homepage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import logo from '../../public/logo.png';
3 | import { Link } from 'react-router-dom';
4 |
5 | const Homepage = () => {
6 | return (
7 |
8 | {/* Header */}
9 |
10 |
11 |

12 |
13 |
14 |
15 |
16 |
17 | {/* Main Content */}
18 |
19 | Enjoy The World In Briefs
20 |
21 | Create viral videos effortlessly with Zoomerang! Trendy effects,
seamless transitions, and powerful
22 | tools all in one app!
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Homepage;
39 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/pages/VideoChat.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function MainPage() {
4 | const funcButtons = [
5 | {
6 | id: 'dopple',
7 | label: 'Dopple Danger',
8 | img: 'double-danger.png',
9 | },
10 | {
11 | id: 'clicks',
12 | label: 'Clicks',
13 | img: 'clicks.png',
14 | },
15 | {
16 | id: 'zoom-chats',
17 | label: 'Zoom Chats',
18 | img: 'zoom-chats.png',
19 | },
20 | {
21 | id: 'sound-box',
22 | label: 'SoundBox',
23 | img: 'sound-box.png',
24 | },
25 | {
26 | id: 'call-stop',
27 | label: '',
28 | img: 'call-stop.png',
29 | },
30 | ];
31 | return (
32 |
33 |
34 |
35 | {/* video group */}
36 |
42 | {/* button group */}
43 |
44 | {funcButtons.map((item) => {
45 | return (
46 |
54 | );
55 | })}
56 |
57 |
58 | {/* zoom chat section */}
59 |
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/src/pages/StartChatPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import logo from '../../public/logo.png';
3 | import talkTo from '../../public/Talkto.png';
4 | import Online from '../../public/Online.png';
5 | import People from '../../public/People.png';
6 |
7 | export default function StartChatPage() {
8 | const [activeButton, setActiveButton] = useState('Trending');
9 |
10 | const handleClick = (buttonName) => {
11 | setActiveButton(buttonName);
12 | };
13 | const [activeTab, setActiveTab] = useState('home');
14 |
15 | // Real profile images
16 | const profileImages = [
17 | 'https://i.pravatar.cc/150?img=4',
18 | 'https://i.pravatar.cc/150?img=5',
19 | 'https://i.pravatar.cc/150?img=6',
20 | 'https://i.pravatar.cc/150?img=1',
21 | 'https://i.pravatar.cc/150?img=7',
22 | 'https://i.pravatar.cc/150?img=8',
23 | 'https://i.pravatar.cc/150?img=9',
24 | 'https://i.pravatar.cc/150?img=2',
25 | 'https://i.pravatar.cc/150?img=3',
26 | 'https://i.pravatar.cc/150?img=5',
27 | 'https://i.pravatar.cc/150?img=4',
28 | 'https://i.pravatar.cc/150?img=7',
29 |
30 | 'https://i.pravatar.cc/150?img=3',
31 | 'https://i.pravatar.cc/150?img=4',
32 | 'https://i.pravatar.cc/150?img=5',
33 | 'https://i.pravatar.cc/150?img=6',
34 | 'https://i.pravatar.cc/150?img=1',
35 | 'https://i.pravatar.cc/150?img=7',
36 | 'https://i.pravatar.cc/150?img=8',
37 | 'https://i.pravatar.cc/150?img=9',
38 | 'https://i.pravatar.cc/150?img=2',
39 | 'https://i.pravatar.cc/150?img=3',
40 | 'https://i.pravatar.cc/150?img=5',
41 | 'https://i.pravatar.cc/150?img=4',
42 | 'https://i.pravatar.cc/150?img=7',
43 |
44 | 'https://i.pravatar.cc/150?img=3',
45 |
46 | 'https://i.pravatar.cc/150?img=3',
47 | 'https://i.pravatar.cc/150?img=4',
48 | 'https://i.pravatar.cc/150?img=5',
49 | 'https://i.pravatar.cc/150?img=6',
50 | 'https://i.pravatar.cc/150?img=1',
51 | 'https://i.pravatar.cc/150?img=7',
52 | 'https://i.pravatar.cc/150?img=8',
53 | 'https://i.pravatar.cc/150?img=9',
54 | 'https://i.pravatar.cc/150?img=2',
55 | 'https://i.pravatar.cc/150?img=3',
56 | 'https://i.pravatar.cc/150?img=5',
57 | 'https://i.pravatar.cc/150?img=4',
58 | 'https://i.pravatar.cc/150?img=7',
59 |
60 | 'https://i.pravatar.cc/150?img=3',
61 | 'https://i.pravatar.cc/150?img=3',
62 | ];
63 | return (
64 |
65 | {/* Main content */}
66 |
67 | {/* Top avatar scroll */}
68 |
69 | {/* Footer */}
70 |
71 |
75 |
76 | handleClick('Trending')}
78 | className={`px-4 py-2 rounded-3xl ${
79 | activeButton === 'Trending' ? 'bg-gradient-to-r from-[#0E3D3F] to-[#000000]' : 'bg-transparent'
80 | }`}
81 | >
82 | Trending
83 |
84 | handleClick('Synapse')}
86 | className={`px-4 py-2 rounded-3xl ${
87 | activeButton === 'Synapse' ? 'bg-gradient-to-r from-[#0E3D3F] to-[#000000]' : 'bg-transparent'
88 | }`}
89 | >
90 | Synapse
91 |
92 |
93 |
94 |
95 |
96 | );
97 | }
98 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layouts/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Home, Users, Mail, Star, LogIn, Menu } from 'lucide-react';
3 | import logo from '../../public/logo.png';
4 | import talkTo from '../../public/Talkto.png';
5 | import Avatar from '../../public/login.png';
6 | import Person from '../../public/Person.png';
7 | import { useLocation } from 'react-router-dom';
8 |
9 | export default function Sidebar({ activeTab, setActiveTab }) {
10 | const [isOpen, setIsOpen] = useState(false);
11 | const location = useLocation();
12 |
13 | const navItems = [
14 | { id: 'home', label: 'Home', Icon: Home },
15 | { id: 'news', label: 'News', Icon: Users },
16 | { id: 'contact', label: 'Contact', Icon: Mail },
17 | { id: 'premium', label: 'Premium', Icon: Star },
18 | ];
19 |
20 | // Hide sidebar on auth pages
21 | const isAuthPage = ['/', '/signIn', '/signUp'].includes(location.pathname);
22 | if (isAuthPage) return null;
23 |
24 | return (
25 | <>
26 | {/* Toggle button for mobile */}
27 | setIsOpen(!isOpen)}
30 | >
31 |
32 |
33 |
34 | {/* Sidebar */}
35 |
40 | {/* Logo */}
41 |
42 |

43 |
44 |
45 | {/* Navigation */}
46 |
67 |
68 | {/* Centered Person Image */}
69 |
70 |

71 |
72 |
73 | {/* Bottom section */}
74 |
75 | {/* Login button */}
76 |
77 |
78 | Login
79 |
80 |
81 | {/* Talk to + Avatar */}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | >
93 | );
94 | }
95 |
--------------------------------------------------------------------------------
/src/pages/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Eye, EyeOff } from 'lucide-react';
3 | import logo from '../../public/logo.png';
4 | import Sign from '../../public/signIn.png';
5 | import appleLogo from '../../public/apple.png';
6 |
7 | const SignIn = () => {
8 | const [showPassword, setShowPassword] = useState(false);
9 | const [username, setUsername] = useState('');
10 | const [password, setPassword] = useState('');
11 |
12 | const handleSignIn = (e) => {
13 | e.preventDefault();
14 | // Add sign-in logic here
15 | console.log('Sign in attempt', { username, password });
16 | };
17 |
18 | return (
19 |
20 |
21 |
22 |

23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
Sign in
31 |
32 | Welcome to Zoomerang-Z New User{' '}
33 |
34 | Join Us
35 |
36 |
37 |
38 |
71 |
72 |
77 |
78 |
79 |
80 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
110 |
111 |
112 |
113 |

114 |
115 |
116 |
117 | );
118 | };
119 |
120 | export default SignIn;
121 |
--------------------------------------------------------------------------------
/src/pages/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Eye, EyeOff } from 'lucide-react';
3 | import logo from '../../public/logo.png';
4 | import Sign from '../../public/signIn.png';
5 | import appleLogo from '../../public/apple.png';
6 |
7 | const SignUp = () => {
8 | const [showPassword, setShowPassword] = useState(false);
9 | const [username, setUsername] = useState('');
10 | const [password, setPassword] = useState('');
11 |
12 | const handleSignIn = (e) => {
13 | e.preventDefault();
14 | // Add sign-in logic here
15 | console.log('Sign in attempt', { username, password });
16 | };
17 |
18 | return (
19 |
20 |
21 |
22 |

23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
Sign Up
31 |
32 | Welcome to Zoomerang-Z New User{' '}
33 |
34 | Join Us
35 |
36 |
37 |
38 |
88 |
89 |
94 |
95 |
96 |
97 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
127 |
128 |
129 |
130 |

131 |
132 |
133 |
134 | );
135 | };
136 |
137 | export default SignUp;
138 |
--------------------------------------------------------------------------------
/src/pages/CallingPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from 'react';
2 | import logo from '../../public/logo.png';
3 | import { Link, Mic, MicOff, Video, VideoOff, Phone, MessageSquare, Volume2, VolumeX } from 'lucide-react';
4 |
5 | export default function CallingPage() {
6 | const [isPanelOpen, setIsPanelOpen] = useState(false);
7 | const [isCameraOn, setIsCameraOn] = useState(true);
8 | const [isMicOn, setIsMicOn] = useState(true);
9 | const [isSoundOn, setIsSoundOn] = useState(true);
10 | const [remoteStream, setRemoteStream] = useState(null);
11 | const [localStream, setLocalStream] = useState(null);
12 | const [messages, setMessages] = useState([]);
13 | const [newMessage, setNewMessage] = useState('');
14 | const [meetingLink, setMeetingLink] = useState('');
15 | const localVideoRef = useRef(null);
16 | const remoteVideoRef = useRef(null);
17 |
18 | useEffect(() => {
19 | const initStreams = async () => {
20 | try {
21 | const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
22 | setLocalStream(stream);
23 | if (localVideoRef.current) {
24 | localVideoRef.current.srcObject = stream;
25 | }
26 | } catch (error) {
27 | console.error('Error accessing media devices:', error);
28 | }
29 | };
30 |
31 | initStreams();
32 | }, []);
33 |
34 | const handleEndCall = () => {
35 | if (localStream) {
36 | localStream.getTracks().forEach((track) => track.stop());
37 | }
38 | // Add call end logic here
39 | };
40 |
41 | const handleSendMessage = (e) => {
42 | e.preventDefault();
43 | if (newMessage.trim()) {
44 | const message = {
45 | text: newMessage,
46 | timestamp: new Date().toISOString(),
47 | sender: 'me',
48 | };
49 | setMessages((prev) => [...prev, message]);
50 | setNewMessage('');
51 | }
52 | };
53 |
54 | const handleJoinMeeting = (e) => {
55 | e.preventDefault();
56 | if (meetingLink.trim()) {
57 | // Add your meeting join logic here
58 | console.log('Joining meeting with link:', meetingLink);
59 | }
60 | };
61 |
62 | return (
63 |
64 | {/* Main content */}
65 |
66 | {/* Header with meeting link input */}
67 |
87 |
88 | {/* Main video area */}
89 |
90 | {remoteStream ? (
91 |
92 | ) : (
93 |
94 |

101 |
John Doe
102 |
Waiting for video...
103 |
104 | )}
105 |
106 |
107 | {/* Bottom controls */}
108 |
109 |
setIsMicOn(!isMicOn)}
112 | >
113 | {isMicOn ? : }
114 |
115 |
setIsCameraOn(!isCameraOn)}
118 | >
119 | {isCameraOn ? : }
120 |
121 |
setIsSoundOn(!isSoundOn)}
124 | >
125 | {isSoundOn ? : }
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
setIsPanelOpen(!isPanelOpen)}>
135 | ⋮
136 |
137 |
138 |
139 | {/* Right side panel */}
140 |
143 | {/* Local video */}
144 |
145 |
146 | {isCameraOn && localStream ? (
147 |
154 | ) : (
155 |
156 |

157 |
158 | )}
159 |
160 |
161 |
162 | {/* Chat area */}
163 |
164 |
165 |
Chat
166 |
167 |
168 | {/* Messages */}
169 |
170 | {messages.map((message, index) => (
171 |
172 |
175 |
{message.text}
176 |
{new Date(message.timestamp).toLocaleTimeString()}
177 |
178 |
179 | ))}
180 |
181 |
182 | {/* Message input */}
183 |
184 |
196 |
197 |
198 |
199 |
200 | );
201 | }
202 |
--------------------------------------------------------------------------------
/src/pages/MainPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import logo from '../../public/logo.png';
3 | import talkTo from '../../public/Talkto.png';
4 | import Online from '../../public/Online.png';
5 | import People from '../../public/People.png';
6 |
7 | export default function MainPage() {
8 | const [activeButton, setActiveButton] = useState('Trending');
9 |
10 | const handleClick = (buttonName) => {
11 | setActiveButton(buttonName);
12 | };
13 | const [activeTab, setActiveTab] = useState('home');
14 |
15 | // Real profile images
16 | const profileImages = [
17 | 'https://i.pravatar.cc/150?img=4',
18 | 'https://i.pravatar.cc/150?img=5',
19 | 'https://i.pravatar.cc/150?img=6',
20 | 'https://i.pravatar.cc/150?img=1',
21 | 'https://i.pravatar.cc/150?img=7',
22 | 'https://i.pravatar.cc/150?img=8',
23 | 'https://i.pravatar.cc/150?img=9',
24 | 'https://i.pravatar.cc/150?img=2',
25 | 'https://i.pravatar.cc/150?img=3',
26 | 'https://i.pravatar.cc/150?img=5',
27 | 'https://i.pravatar.cc/150?img=4',
28 | 'https://i.pravatar.cc/150?img=7',
29 |
30 | 'https://i.pravatar.cc/150?img=3',
31 | 'https://i.pravatar.cc/150?img=4',
32 | 'https://i.pravatar.cc/150?img=5',
33 | 'https://i.pravatar.cc/150?img=6',
34 | 'https://i.pravatar.cc/150?img=1',
35 | 'https://i.pravatar.cc/150?img=7',
36 | 'https://i.pravatar.cc/150?img=8',
37 | 'https://i.pravatar.cc/150?img=9',
38 | 'https://i.pravatar.cc/150?img=2',
39 | 'https://i.pravatar.cc/150?img=3',
40 | 'https://i.pravatar.cc/150?img=5',
41 | 'https://i.pravatar.cc/150?img=4',
42 | 'https://i.pravatar.cc/150?img=7',
43 |
44 | 'https://i.pravatar.cc/150?img=3',
45 |
46 | 'https://i.pravatar.cc/150?img=3',
47 | 'https://i.pravatar.cc/150?img=4',
48 | 'https://i.pravatar.cc/150?img=5',
49 | 'https://i.pravatar.cc/150?img=6',
50 | 'https://i.pravatar.cc/150?img=1',
51 | 'https://i.pravatar.cc/150?img=7',
52 | 'https://i.pravatar.cc/150?img=8',
53 | 'https://i.pravatar.cc/150?img=9',
54 | 'https://i.pravatar.cc/150?img=2',
55 | 'https://i.pravatar.cc/150?img=3',
56 | 'https://i.pravatar.cc/150?img=5',
57 | 'https://i.pravatar.cc/150?img=4',
58 | 'https://i.pravatar.cc/150?img=7',
59 |
60 | 'https://i.pravatar.cc/150?img=3',
61 | 'https://i.pravatar.cc/150?img=3',
62 | ];
63 | return (
64 |
65 | {/* Main content */}
66 |
67 | {/* Top avatar scroll */}
68 |
69 |
70 | {profileImages.map((img, i) => (
71 |
72 |
73 |

74 |
75 |
76 | ))}
77 |
78 |
79 |
80 | {/* Main banner */}
81 |
82 |
83 |
84 |

85 |
86 |
87 |
Snap. Share. Connect....
88 |
89 | Zoomerang-Z - Take, trim, and make the trick. Share, chat, and go viral. #stayzoomedout
90 |
91 |
92 |
93 |
94 |

95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | {/* User Grid */}
103 |
104 |
105 | {/* Top Row */}
106 | {Array(6)
107 | .fill()
108 | .map((_, i) => (
109 |
110 | {i % 2 === 0 ? (
111 | // Split view (2 users)
112 | <>
113 |
114 |
120 |
121 |
122 |
128 |
129 | >
130 | ) : (
131 | // Single user
132 |
138 | )}
139 | {/* Live tag */}
140 | {i % 3 === 2 && (
141 |
LIVE
142 | )}
143 |
144 | ))}
145 |
146 | {/* Bottom Row - Duplicate of top row for this example */}
147 | {Array(6)
148 | .fill()
149 | .map((_, i) => (
150 |
151 | {i % 2 === 0 ? (
152 | // Split view
153 | <>
154 |
155 |
161 |
162 |
163 |
169 |
170 | >
171 | ) : (
172 | // Single user
173 |
179 | )}
180 | {/* Live tag */}
181 | {i % 3 === 2 && (
182 |
LIVE
183 | )}
184 |
185 | ))}
186 |
187 |
188 |
189 | {/* Footer */}
190 |
191 |
192 |
193 | Join
194 |
195 |
196 | handleClick('Trending')}
198 | className={`px-4 py-2 rounded-3xl ${
199 | activeButton === 'Trending' ? 'bg-gradient-to-r from-[#0E3D3F] to-[#000000]' : 'bg-transparent'
200 | }`}
201 | >
202 | Trending
203 |
204 | handleClick('Synapse')}
206 | className={`px-4 py-2 rounded-3xl ${
207 | activeButton === 'Synapse' ? 'bg-gradient-to-r from-[#0E3D3F] to-[#000000]' : 'bg-transparent'
208 | }`}
209 | >
210 | Synapse
211 |
212 |
213 |
214 |
215 |
216 | );
217 | }
218 |
--------------------------------------------------------------------------------
/src/pages/ChatPage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from 'react';
2 | import logo from '../../public/logo.png';
3 | import talkTo from '../../public/Talkto.png';
4 | import Online from '../../public/Online.png';
5 | import People from '../../public/People.png';
6 | import { Settings, User, LogOut, Send, Smile, Paperclip, X } from 'lucide-react';
7 |
8 | // Common emojis list
9 | const commonEmojis = [
10 | '😀',
11 | '😃',
12 | '😄',
13 | '😁',
14 | '😆',
15 | '😅',
16 | '😂',
17 | '🤣',
18 | '😊',
19 | '😇',
20 | '🙂',
21 | '🙃',
22 | '😉',
23 | '😌',
24 | '😍',
25 | '🥰',
26 | '😘',
27 | '😗',
28 | '😙',
29 | '😚',
30 | '😋',
31 | '😛',
32 | '😝',
33 | '😜',
34 | '🤪',
35 | '🤨',
36 | '🧐',
37 | '🤓',
38 | '😎',
39 | '🤩',
40 | '🥳',
41 | '😏',
42 | '😒',
43 | '😞',
44 | '😔',
45 | '😟',
46 | '😕',
47 | '🙁',
48 | '☹️',
49 | '😣',
50 | '😖',
51 | '😫',
52 | '😩',
53 | '🥺',
54 | '😢',
55 | '😭',
56 | '😤',
57 | '😠',
58 | '😡',
59 | '🤬',
60 | '🤯',
61 | '😳',
62 | '🥵',
63 | '🥶',
64 | '😱',
65 | '😨',
66 | '😰',
67 | '😥',
68 | '😓',
69 | '🤗',
70 | '🤔',
71 | '🤭',
72 | '🤫',
73 | '🤥',
74 | '😶',
75 | '😐',
76 | '😑',
77 | '😬',
78 | '🙄',
79 | '😯',
80 | '😦',
81 | '😧',
82 | '😮',
83 | '😲',
84 | '🥱',
85 | '😴',
86 | '🤤',
87 | '😪',
88 | '😵',
89 | '🤐',
90 | '🥴',
91 | '🤢',
92 | '🤮',
93 | '🤧',
94 | '😷',
95 | '🤒',
96 | '🤕',
97 | '🤑',
98 | '🤠',
99 | '😈',
100 | '👿',
101 | '👹',
102 | '👺',
103 | '🤡',
104 | '💩',
105 | '👻',
106 | '💀',
107 | '☠️',
108 | '👽',
109 | '👾',
110 | '🤖',
111 | '🎃',
112 | '😺',
113 | '😸',
114 | '😹',
115 | '😻',
116 | '😼',
117 | '😽',
118 | '🙀',
119 | '😿',
120 | '😾',
121 | '❤️',
122 | '🧡',
123 | '💛',
124 | '💚',
125 | '💙',
126 | '💜',
127 | '🖤',
128 | '🤍',
129 | '🤎',
130 | '💔',
131 | '❣️',
132 | '💕',
133 | '💞',
134 | '💓',
135 | '💗',
136 | '💖',
137 | '💘',
138 | '💝',
139 | '💟',
140 | '👍',
141 | '👎',
142 | '👏',
143 | '🙌',
144 | '👋',
145 | '🤝',
146 | '✌️',
147 | '🤞',
148 | '🤟',
149 | '🤘',
150 | '👌',
151 | '👈',
152 | '👉',
153 | '👆',
154 | '👇',
155 | '☝️',
156 | '✋',
157 | '🤚',
158 | '🖐️',
159 | '🖖',
160 | '👋',
161 | '🤙',
162 | '💪',
163 | '🦾',
164 | '🦿',
165 | '🦵',
166 | '🦶',
167 | '👂',
168 | '🦻',
169 | '👃',
170 | '🧠',
171 | '🦷',
172 | '🦴',
173 | '👀',
174 | '👁️',
175 | '👅',
176 | '👄',
177 | '💋',
178 | '🩸',
179 | '💦',
180 | ];
181 |
182 | export default function StartChatPage() {
183 | const [activeButton, setActiveButton] = useState('Trending');
184 | const [messages, setMessages] = useState([]);
185 | const [newMessage, setNewMessage] = useState('');
186 | const [ws, setWs] = useState(null);
187 | const messagesEndRef = useRef(null);
188 | const [activeTab, setActiveTab] = useState('home');
189 | const [selectedUser, setSelectedUser] = useState(null);
190 | const [showProfileMenu, setShowProfileMenu] = useState(false);
191 | const [userStatus, setUserStatus] = useState('online');
192 | const [displayName, setDisplayName] = useState('You');
193 | const [isEditingName, setIsEditingName] = useState(false);
194 | const [unreadCounts, setUnreadCounts] = useState({
195 | 1: 3, // John Doe has 3 unread messages
196 | 2: 1, // Jane Smith has 1 unread message
197 | 3: 0, // Mike Johnson has no unread messages
198 | });
199 | const [users, setUsers] = useState([
200 | { id: 1, name: 'John Doe', status: 'online', avatar: logo, lastSeen: '2 minutes ago' },
201 | { id: 2, name: 'Jane Smith', status: 'offline', avatar: logo, lastSeen: '1 hour ago' },
202 | { id: 3, name: 'Mike Johnson', status: 'online', avatar: logo, lastSeen: '5 minutes ago' },
203 | ]);
204 | const [showEmojiPicker, setShowEmojiPicker] = useState(false);
205 | const emojiPickerRef = useRef(null);
206 |
207 | useEffect(() => {
208 | // Initialize WebSocket connection
209 | const socket = new WebSocket('ws://localhost:8080');
210 | setWs(socket);
211 |
212 | socket.onmessage = (event) => {
213 | const message = JSON.parse(event.data);
214 | setMessages((prevMessages) => [...prevMessages, message]);
215 | };
216 |
217 | return () => {
218 | socket.close();
219 | };
220 | }, []);
221 |
222 | useEffect(() => {
223 | messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
224 | }, [messages]);
225 |
226 | useEffect(() => {
227 | // Close emoji picker when clicking outside
228 | const handleClickOutside = (event) => {
229 | if (emojiPickerRef.current && !emojiPickerRef.current.contains(event.target)) {
230 | setShowEmojiPicker(false);
231 | }
232 | };
233 |
234 | document.addEventListener('mousedown', handleClickOutside);
235 | return () => {
236 | document.removeEventListener('mousedown', handleClickOutside);
237 | };
238 | }, []);
239 |
240 | const handleEmojiSelect = (emoji) => {
241 | setNewMessage((prev) => prev + emoji);
242 | setShowEmojiPicker(false);
243 | };
244 |
245 | const handleSendMessage = (e) => {
246 | e.preventDefault();
247 | if (newMessage.trim() && ws) {
248 | const message = {
249 | text: newMessage,
250 | timestamp: new Date().toISOString(),
251 | sender: 'me',
252 | senderName: displayName,
253 | };
254 | setMessages((prevMessages) => [...prevMessages, message]);
255 | ws.send(JSON.stringify(message));
256 | setNewMessage('');
257 | }
258 | };
259 |
260 | const handleClick = (buttonName) => {
261 | setActiveButton(buttonName);
262 | };
263 |
264 | const handleUserClick = (user) => {
265 | setSelectedUser(user);
266 | // Reset unread count when user is selected
267 | setUnreadCounts((prev) => ({
268 | ...prev,
269 | [user.id]: 0,
270 | }));
271 | };
272 |
273 | const handleStatusChange = (status) => {
274 | setUserStatus(status);
275 | setShowProfileMenu(false);
276 | };
277 |
278 | const handleNameChange = (e) => {
279 | e.preventDefault();
280 | setDisplayName(e.target.nameInput.value);
281 | setIsEditingName(false);
282 | };
283 |
284 | const handleFileUpload = (e) => {
285 | const file = e.target.files[0];
286 | if (file) {
287 | // Handle file upload logic here
288 | console.log('File selected:', file.name);
289 | }
290 | };
291 |
292 | return (
293 |
294 | {/* Main content */}
295 |
296 | {/* Top bar with user info */}
297 | {selectedUser && (
298 |
299 |
300 |
301 |

302 |
307 |
308 |
309 |
{selectedUser.name}
310 |
311 | {selectedUser.status === 'online' ? 'Online' : `Last seen ${selectedUser.lastSeen}`}
312 |
313 |
314 |
315 |
316 | )}
317 |
318 | {/* Chat messages area */}
319 |
320 | {messages.map((message, index) => (
321 |
322 |
323 |
{message.senderName || 'User'}
324 |
{message.text}
325 |
{new Date(message.timestamp).toLocaleTimeString()}
326 |
327 |
328 | ))}
329 |
330 |
331 |
332 | {/* Message input area */}
333 |
462 |
463 |
464 | {/* Users list */}
465 |
466 |
Online Users
467 |
468 | {users.map((user) => (
469 |
handleUserClick(user)}
472 | className={`flex items-center justify-between p-3 hover:bg-gray-800 rounded-lg cursor-pointer ${
473 | selectedUser?.id === user.id ? 'bg-gray-800' : ''
474 | }`}
475 | >
476 |
477 |
478 |

479 |
484 |
485 |
486 |
{user.name}
487 |
488 | {user.status === 'online' ? 'Online' : `Last seen ${user.lastSeen}`}
489 |
490 |
491 |
492 | {/* Unread message count */}
493 | {unreadCounts[user.id] > 0 && (
494 |
495 | {unreadCounts[user.id]}
496 |
497 | )}
498 |
499 | ))}
500 |
501 |
502 |
503 | );
504 | }
505 |
--------------------------------------------------------------------------------