add_user_conv impl
This commit is contained in:
12
src/App.jsx
12
src/App.jsx
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { createContext } from 'react'
|
||||||
import reactLogo from './assets/react.svg'
|
import reactLogo from './assets/react.svg'
|
||||||
import viteLogo from '/vite.svg'
|
import viteLogo from '/vite.svg'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
@@ -8,15 +9,20 @@ import { API_BASE } from './config.js'
|
|||||||
import Login from './components/login.jsx'
|
import Login from './components/login.jsx'
|
||||||
import MainApp from './components/MainApp.jsx'
|
import MainApp from './components/MainApp.jsx'
|
||||||
|
|
||||||
|
export const ClientContext = createContext(null);
|
||||||
|
|
||||||
|
export function userContext() {}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [loading, setLoading] = useState(true); // true while checking auth
|
const [loading, setLoading] = useState(true); // true while checking auth
|
||||||
const [loggedIn, setLoggedIn] = useState(false);
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
|
const [clientId, setClientId] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function checkAuth() {
|
async function checkAuth() {
|
||||||
try {
|
try {
|
||||||
// This endpoint should validate the refresh token cookie and return an access token
|
// This endpoint should validate the refresh token cookie and return an access token
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/auth/login_refresh_token`, {
|
const res = await fetch(`${API_BASE}/auth/login_refresh_token`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -28,7 +34,9 @@ export default function App() {
|
|||||||
device_id : "147ac10b-58cc-4372-a567-0e02b2c3d479", // or persistent device ID
|
device_id : "147ac10b-58cc-4372-a567-0e02b2c3d479", // or persistent device ID
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("sent checkAuth request")
|
console.log("sent checkAuth request")
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
console.log(res)
|
console.log(res)
|
||||||
console.log("checkAuth request not ok")
|
console.log("checkAuth request not ok")
|
||||||
@@ -40,6 +48,8 @@ export default function App() {
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
console.log(data)
|
console.log(data)
|
||||||
localStorage.setItem("access_tokens", JSON.stringify(data.tokens)); // store for API calls
|
localStorage.setItem("access_tokens", JSON.stringify(data.tokens)); // store for API calls
|
||||||
|
setClientId(data.user_id)
|
||||||
|
|
||||||
setLoggedIn(true);
|
setLoggedIn(true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -57,7 +67,7 @@ export default function App() {
|
|||||||
|
|
||||||
// Show main app if logged in, otherwise show login
|
// Show main app if logged in, otherwise show login
|
||||||
return loggedIn ? (
|
return loggedIn ? (
|
||||||
<MainApp />
|
<MainApp resClientId ={clientId} />
|
||||||
) : (
|
) : (
|
||||||
<Login onLogin={() => setLoggedIn(true)} />
|
<Login onLogin={() => setLoggedIn(true)} />
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ export function useHotel() {
|
|||||||
return useContext(HotelContext);
|
return useContext(HotelContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HotelProvider({ accessToken, children }) {
|
export function HotelProvider({ accessToken, children, resClientId}) {
|
||||||
const [rooms, setRooms] = useState([]);
|
const [rooms, setRooms] = useState([]);
|
||||||
const [conversations, setConversations] = useState([]);
|
const [conversations, setConversations] = useState([]);
|
||||||
|
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
|
const [usersById, setUsersById] = useState([]);
|
||||||
|
const [clientId, setClientId] = useState(resClientId|| null);
|
||||||
|
|
||||||
const tokens = JSON.parse(accessToken);
|
const tokens = JSON.parse(accessToken);
|
||||||
const accessTokenSingle = tokens[0];
|
const accessTokenSingle = tokens[0];
|
||||||
|
|
||||||
|
|
||||||
// --- API FUNCTIONS ---
|
// --- API FUNCTIONS ---
|
||||||
async function fetchRooms() {
|
async function fetchRooms() {
|
||||||
const res = await fetch( `${API_BASE}/rooms/rooms`, {
|
const res = await fetch( `${API_BASE}/rooms/rooms`, {
|
||||||
@@ -46,7 +50,17 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return res.json();
|
|
||||||
|
const users = await res.json();
|
||||||
|
|
||||||
|
const map = {};
|
||||||
|
for (const u of users) {
|
||||||
|
map[u.id] = u.username;
|
||||||
|
}
|
||||||
|
setUsersById(map);
|
||||||
|
|
||||||
|
// setUsers(users)
|
||||||
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchConversations() {
|
async function fetchConversations() {
|
||||||
@@ -71,7 +85,7 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
timestamp: "2025-09-25 11:05:33",
|
timestamp: "2025-09-25 11:05:33",
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(JSON.stringify(payload));
|
//console.log(JSON.stringify(payload));
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/chat/get_message`, {
|
const res = await fetch(`${API_BASE}/chat/get_message`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -87,15 +101,42 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
//return res.json
|
//return res.json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchConvUsers({ conv_id }) {
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
conv_id,
|
||||||
|
timestamp: "2025-09-25 11:05:33",
|
||||||
|
};
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(payload));
|
||||||
|
|
||||||
|
const res = await fetch(`${API_BASE}/chat/get_conv_users/${conv_id}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessTokenSingle}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
|
||||||
|
//return res.json
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function sendMessage({ conv_id, message }) {
|
async function sendMessage({ conv_id, message }) {
|
||||||
if (!conv_id) return;
|
if (!conv_id) {
|
||||||
console.log("conv_id null at sendMessage")
|
console.log("conv_id null at sendMessage")
|
||||||
|
return
|
||||||
|
};
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
conv_id,
|
conv_id,
|
||||||
message
|
message
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(JSON.stringify(payload));
|
//console.log(JSON.stringify(payload));
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/chat/send_message`, {
|
const res = await fetch(`${API_BASE}/chat/send_message`, {
|
||||||
method: "POST" ,
|
method: "POST" ,
|
||||||
@@ -107,6 +148,27 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addUserToConv({ conv_id, users}) {
|
||||||
|
if (!conv_id || users) {
|
||||||
|
console.log("error in convs or user to add")
|
||||||
|
};
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
conv_id,
|
||||||
|
users
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch(`${API_BASE}/chat/add_users_conv`, {
|
||||||
|
method: "PUT" ,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessTokenSingle}`,
|
||||||
|
"Content-Type" : "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// --- INITIAL DATA LOADING ---
|
// --- INITIAL DATA LOADING ---
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -119,12 +181,15 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
fetchHotelUsers(),
|
fetchHotelUsers(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
setClientId(resClientId);
|
||||||
setRooms(roomsData);
|
setRooms(roomsData);
|
||||||
setConversations(convData);
|
setConversations(convData);
|
||||||
|
//console.log("USERS =",users)
|
||||||
setUsers(usersData);
|
setUsers(usersData);
|
||||||
}
|
}
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
//console.log("USERS 2 =",usersById)
|
||||||
}, [accessToken]);
|
}, [accessToken]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -133,9 +198,14 @@ export function HotelProvider({ accessToken, children }) {
|
|||||||
rooms,
|
rooms,
|
||||||
conversations,
|
conversations,
|
||||||
users,
|
users,
|
||||||
|
usersById,
|
||||||
|
clientId,
|
||||||
updateRoomStatus,
|
updateRoomStatus,
|
||||||
fetchMessages,
|
fetchMessages,
|
||||||
|
fetchConvUsers,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
|
fetchHotelUsers,
|
||||||
|
addUserToConv,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import ChatWidget from "./widget/chatWidget"
|
|||||||
|
|
||||||
import "./MainApp.css"
|
import "./MainApp.css"
|
||||||
|
|
||||||
export default function MainAppWrapper() {
|
export default function MainAppWrapper({resClientId}) {
|
||||||
const accessToken = localStorage.getItem("access_tokens");
|
const accessToken = localStorage.getItem("access_tokens");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotelProvider accessToken={accessToken}>
|
<HotelProvider accessToken={accessToken} resClientId={resClientId}>
|
||||||
<MainApp />
|
<MainApp />
|
||||||
</HotelProvider>
|
</HotelProvider>
|
||||||
);
|
);
|
||||||
@@ -37,7 +37,4 @@ function MainApp() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
.messagebox{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* your messages */
|
||||||
|
.msg--mine {
|
||||||
|
align-self: flex-end;
|
||||||
|
background-color: #464b43;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* others */
|
||||||
|
.msg--theirs {
|
||||||
|
align-self: flex-start;
|
||||||
|
background-color: #473f3f;
|
||||||
|
}
|
||||||
.grid {
|
.grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-items: space-around;
|
justify-items: space-around;
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
import { useHotel } from "../HotelContext";
|
import { useHotel } from "../HotelContext";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
//import {fetchMessage} from .
|
//import {fetchMessage} from .
|
||||||
|
|
||||||
import "./chatWidget.css"
|
import "./chatWidget.css"
|
||||||
|
|
||||||
export default function ChatWidget({convlist}) {
|
export default function ChatWidget({convlist}) {
|
||||||
const [messages, setMessages] = useState([]);
|
const { fetchMessages, sendMessage,
|
||||||
const { fetchMessages, sendMessage } = useHotel();
|
usersById, clientId, fetchConvUsers,
|
||||||
const [activeConvId, setActiveConvId] = useState(null);
|
addUserToConv
|
||||||
|
} = useHotel();
|
||||||
|
|
||||||
|
const [activeConvId, setActiveConvId] = useState(null);
|
||||||
|
const [showAddUsers, setShowAddUsers] = useState(false);
|
||||||
|
|
||||||
const handleOpenConv = async (conv_id) => {
|
const handleOpenConv = async (conv_id) => {
|
||||||
setActiveConvId(conv_id);
|
setActiveConvId(conv_id);
|
||||||
const msg = await fetchMessages({ conv_id });
|
|
||||||
setMessages(msg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("client id in chat widget");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chatWidget">
|
<div className="chatWidget">
|
||||||
<div className="convlist">
|
<div className="convlist">
|
||||||
@@ -25,12 +29,39 @@ export default function ChatWidget({convlist}) {
|
|||||||
key={conv.id}
|
key={conv.id}
|
||||||
id={conv.id}
|
id={conv.id}
|
||||||
title={conv.title}
|
title={conv.title}
|
||||||
onOpenConv={handleOpenConv}
|
onOpenConv={setActiveConvId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<div className="convUtils">
|
||||||
|
<button
|
||||||
|
disabled={!activeConvId}
|
||||||
|
onClick={() => setShowAddUsers(v => !v)}
|
||||||
|
>
|
||||||
|
Add users
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MessagesBox messages={messages} />
|
{showAddUsers && activeConvId && (
|
||||||
|
<AddUsersMenu
|
||||||
|
convId={activeConvId}
|
||||||
|
usersById={usersById}
|
||||||
|
fetchConvUsers={fetchConvUsers}
|
||||||
|
onValidate={addUserToConv}
|
||||||
|
onClose={() => setShowAddUsers(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{activeConvId && (
|
||||||
|
<MessagesBox
|
||||||
|
conv_id={activeConvId}
|
||||||
|
fetchMessages={fetchMessages}
|
||||||
|
usersById={usersById}
|
||||||
|
clientId={clientId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SenderBox
|
<SenderBox
|
||||||
conv_id={activeConvId}
|
conv_id={activeConvId}
|
||||||
onSend={sendMessage}
|
onSend={sendMessage}
|
||||||
@@ -41,9 +72,11 @@ export default function ChatWidget({convlist}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SenderBox({ conv_id, onSend, disabled }) {
|
function SenderBox({ conv_id, onSend, disabled }) {
|
||||||
|
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
|
|
||||||
const handleSend = () => {
|
const handleSend = () => {
|
||||||
|
console.log("conv_Id: ",conv_id)
|
||||||
if (!text.trim() || !conv_id) return;
|
if (!text.trim() || !conv_id) return;
|
||||||
|
|
||||||
onSend({
|
onSend({
|
||||||
@@ -55,17 +88,78 @@ function SenderBox({ conv_id, onSend, disabled }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="senderbox">
|
<div hidden={disabled} className="senderbox">
|
||||||
<input
|
<input
|
||||||
value={text}
|
value={text}
|
||||||
onChange={e => setText(e.target.value)}
|
onChange={e => setText(e.target.value)}
|
||||||
placeholder="Type a message…"
|
placeholder="Type a message…"
|
||||||
/>
|
/>
|
||||||
<button disabled={disabled} onClick={handleSend}>Send</button>
|
<button onClick={handleSend}>Send</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AddUsersMenu({ convId, usersById, fetchConvUsers, onValidate, onClose }) {
|
||||||
|
const [selected, setSelected] = useState(new Set());
|
||||||
|
|
||||||
|
//new function to fetch already present users in the conv
|
||||||
|
//let current = fetchConvUsers(convId);
|
||||||
|
|
||||||
|
const toggleUser = (id) => {
|
||||||
|
setSelected(prev => {
|
||||||
|
const next = new Set(prev);
|
||||||
|
next.has(id) ? next.delete(id) : next.add(id);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidate = () => {
|
||||||
|
if (selected.size === 0) return;
|
||||||
|
|
||||||
|
onValidate({
|
||||||
|
conv_id: convId,
|
||||||
|
users: [...selected],
|
||||||
|
});
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function load() {
|
||||||
|
console.log("fetching conv users ar :", convId);
|
||||||
|
const ids = await fetchConvUsers({ conv_id: convId });
|
||||||
|
setSelected(new Set(ids));
|
||||||
|
}
|
||||||
|
load();
|
||||||
|
}, [convId, fetchConvUsers]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="addUsersMenu">
|
||||||
|
<h3>Add users</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{Object.entries(usersById).map(([id, name]) => (
|
||||||
|
<li key={id}>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selected.has(Number(id))}
|
||||||
|
onChange={() => toggleUser(Number(id))}
|
||||||
|
/>
|
||||||
|
{name}
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button onClick={handleValidate}>Validate</button>
|
||||||
|
<button onClick={onClose}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function ConvCard({id, title, onOpenConv}) {
|
function ConvCard({id, title, onOpenConv}) {
|
||||||
return(
|
return(
|
||||||
@@ -77,21 +171,43 @@ function ConvCard({id, title, onOpenConv}) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function MessagesBox({ messages }) {
|
function MessagesBox({ conv_id, fetchMessages, usersById, clientId }) {
|
||||||
|
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function load() {
|
||||||
|
const msg = await fetchMessages({ conv_id });
|
||||||
|
setMessages(msg);
|
||||||
|
}
|
||||||
|
load();
|
||||||
|
}, [conv_id]);
|
||||||
|
|
||||||
if (!messages || messages.length === 0) {
|
if (!messages || messages.length === 0) {
|
||||||
return <div className="messagesbox empty">No messages</div>
|
return <div className="messagesbox empty">No messages</div>
|
||||||
}
|
}
|
||||||
console.log("FETCH RESULT =", messages);
|
//console.log("FETCH RESULT =", messages);
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="messagebox">
|
<div className="messagebox">
|
||||||
{messages.map(msg => (
|
{messages
|
||||||
<div key = {msg.id} className = "msg">
|
.slice() // don’t mutate state
|
||||||
<p><strong>Sender:</strong> {msg.sender_id}</p>
|
.sort((a, b) => new Date(a.sent_at) - new Date(b.sent_at))
|
||||||
|
.map(msg => {
|
||||||
|
const isMine = msg.sender_id === clientId
|
||||||
|
|
||||||
|
return(
|
||||||
|
<div key = {msg.id}
|
||||||
|
className={`msg ${isMine ? "msg--mine" : "msg--theirs"}`}>
|
||||||
|
<p>
|
||||||
|
<strong>Sender:</strong>{" "}
|
||||||
|
{usersById[msg.sender_id] ?? "Unknown user"}
|
||||||
|
</p>
|
||||||
<p>{msg.content}</p>
|
<p>{msg.content}</p>
|
||||||
<small>{msg.sent_at}</small>
|
<small>{msg.sent_at}</small>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
//export const API_BASE = "http://79.137.75.155:8080";
|
||||||
export const API_BASE = "http://localhost:7080";
|
export const API_BASE = "http://localhost:7080";
|
||||||
Reference in New Issue
Block a user