| const [createChat, setCreateChat] = useState(false); | const [createChat, setCreateChat] = useState(false); | ||||
| const [chatName, setChatName] = useState(""); | const [chatName, setChatName] = useState(""); | ||||
| const [requestedRooms, setRequestedRooms] = useState([]); | const [requestedRooms, setRequestedRooms] = useState([]); | ||||
| const { rooms, status: chatStatus, error, notifications, activeRoom } = useSelector( | |||||
| (state) => state.chat | |||||
| ); | |||||
| const { | |||||
| rooms, | |||||
| status: chatStatus, | |||||
| error, | |||||
| notifications, | |||||
| activeRoom, | |||||
| } = useSelector((state) => state.chat); | |||||
| const { | const { | ||||
| chosenRoom, | chosenRoom, | ||||
| status: requestsStatus, | status: requestsStatus, | ||||
| dispatch( | dispatch( | ||||
| chatActions.saveContextId({ connId: data.connId, userId: user.id }) | chatActions.saveContextId({ connId: data.connId, userId: user.id }) | ||||
| ); | ); | ||||
| console.log("Join room",data) | |||||
| console.log("Join room", data); | |||||
| setChatMessage(data); | setChatMessage(data); | ||||
| } | } | ||||
| }); | }); | ||||
| chatActions.saveContextId({ connId: data.connId, userId: user.id }) | chatActions.saveContextId({ connId: data.connId, userId: user.id }) | ||||
| ); | ); | ||||
| } | } | ||||
| console.log("Send group message",data) | |||||
| console.log("Send group message", data); | |||||
| setChatMessage(data); | setChatMessage(data); | ||||
| }); | }); | ||||
| } | } | ||||
| senderId: chatMessage.userId, | senderId: chatMessage.userId, | ||||
| updatedAtUtc: null, | updatedAtUtc: null, | ||||
| username: user.username, | username: user.username, | ||||
| isAccessMessage: chatMessage.isAccessMessage | |||||
| isAccessMessage: chatMessage.isAccessMessage, | |||||
| }) | }) | ||||
| ); | ); | ||||
| } | } | ||||
| <div> | <div> | ||||
| {rooms.map((room, index) => ( | {rooms.map((room, index) => ( | ||||
| <div | <div | ||||
| className="border-bottom d-flex" | |||||
| className="border-bottom roomsBtn d-flex bg-light" | |||||
| key={index} | key={index} | ||||
| onClick={() => joinRoom(room)} | onClick={() => joinRoom(room)} | ||||
| > | > | ||||
| <button | <button | ||||
| className="text-start w-100 py-3 px-3 btn btn-light h-100" | |||||
| className="text-start w-100 py-2 px-3 btn btn-light h-100" | |||||
| onClick={showRoomMessagesHandler.bind(this, room)} | onClick={showRoomMessagesHandler.bind(this, room)} | ||||
| > | > | ||||
| {room.name} | {room.name} | ||||
| <div> | <div> | ||||
| {acceptedRequests.length > 0 && ( | {acceptedRequests.length > 0 && ( | ||||
| <div> | <div> | ||||
| <h1>Accepted</h1> | |||||
| <h5 className="text-start w-100 ps-3 text-light py-2 pt-3"> | |||||
| Accepted Rooms | |||||
| </h5> | |||||
| {acceptedRequests.map((n, index) => ( | {acceptedRequests.map((n, index) => ( | ||||
| <div | <div | ||||
| className="border-bottom d-flex" | |||||
| className="border-bottom roomsBtn d-flex bg-light" | |||||
| key={index} | key={index} | ||||
| onClick={() => joinRoom(n)} | onClick={() => joinRoom(n)} | ||||
| > | > | ||||
| {notificationCounter(n) && ( | {notificationCounter(n) && ( | ||||
| <div className="h-100 d-flex align-items-center justify-content-center"> | |||||
| <span | |||||
| style={{ | |||||
| padding: "0.5rem 1rem", | |||||
| backgroundColor: "red", | |||||
| borderRadius: "50%", | |||||
| color: "white", | |||||
| }} | |||||
| > | |||||
| {notificationCounter(n)} | |||||
| </span> | |||||
| <div className="notification rounded-circle my-auto ms-3"> | |||||
| {notificationCounter(n)} | |||||
| </div> | </div> | ||||
| )} | )} | ||||
| <button | <button | ||||
| className="text-start w-100 py-3 px-3 btn btn-light h-100" | |||||
| className="text-start w-100 py-2 px-3 btn btn-light h-100" | |||||
| onClick={showRoomMessagesHandler.bind(this, n)} | onClick={showRoomMessagesHandler.bind(this, n)} | ||||
| > | > | ||||
| {n.name} | {n.name} | ||||
| )} | )} | ||||
| {pendingRequests.length > 0 && ( | {pendingRequests.length > 0 && ( | ||||
| <div> | <div> | ||||
| <h1>Pending</h1> | |||||
| <h5 className="text-start w-100 ps-3 text-light py-2 pt-3"> | |||||
| Pending Requests | |||||
| </h5> | |||||
| {pendingRequests.map((n, index) => ( | {pendingRequests.map((n, index) => ( | ||||
| <div className="border-bottom d-flex" key={index}> | |||||
| <button className="text-start w-100 py-3 px-3 btn btn-light h-100"> | |||||
| <div | |||||
| className="border-bottom roomsBtn bg-light d-flex" | |||||
| key={index} | |||||
| > | |||||
| <button className="text-start w-100 py-2 px-3 btn btn-light h-100"> | |||||
| {n.name} | {n.name} | ||||
| </button> | </button> | ||||
| <button className="btn btn-light"> | <button className="btn btn-light"> | ||||
| )} | )} | ||||
| {availableRequests.length > 0 && ( | {availableRequests.length > 0 && ( | ||||
| <div> | <div> | ||||
| <h1>Available</h1> | |||||
| <h5 className="text-start w-100 ps-3 text-light py-2 pt-3"> | |||||
| Available Rooms | |||||
| </h5> | |||||
| {availableRequests.map((n, index) => ( | {availableRequests.map((n, index) => ( | ||||
| <div className="border-bottom d-flex" key={index}> | |||||
| <button className="text-start w-100 py-3 px-3 btn btn-light h-100"> | |||||
| <div | |||||
| onClick={(e) => openModal(n)} | |||||
| className="border-bottom roomsBtn bg-light d-flex" | |||||
| key={index} | |||||
| > | |||||
| <button className="text-start w-100 py-2 px-3 btn btn-light h-100"> | |||||
| {n.name} | {n.name} | ||||
| </button> | </button> | ||||
| <button className="btn btn-light" onClick={(e) => openModal(n)}> | |||||
| <button className="btn btn-light"> | |||||
| <FaSignInAlt /> | <FaSignInAlt /> | ||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| open={showModal} | open={showModal} | ||||
| acceptHandler={dialogHandler} | acceptHandler={dialogHandler} | ||||
| /> | /> | ||||
| <div className=" h-100-auto-overflow"> | |||||
| <div className="bg-light w-100 mb-3 border-bottom d-flex justify-content-between align-items-center"> | |||||
| <h3 className="p-0 m-0 py-3 text-center w-50 fw-light text-muted"> | |||||
| Chat | |||||
| </h3> | |||||
| <div className="p-0 h-100-auto-overflow"> | |||||
| <div className="pe-4 bg-transparent w-100 mb-3 p-0 border-bottom border-light d-flex justify-content-between align-items-center"> | |||||
| <h4 className="p-0 m-0 py-3 w-100 text-start ps-3 text-light bg-transparent"> | |||||
| Chat Rooms | |||||
| </h4> | |||||
| {user?.roles[0] === "Support" && ( | {user?.roles[0] === "Support" && ( | ||||
| <button | <button | ||||
| className="btn btn-light" | |||||
| className="btn p-0 m-0 btn-light pt-0 mt-0 px-3 pb-2 pt-1" | |||||
| onClick={(e) => setCreateChat(true)} | onClick={(e) => setCreateChat(true)} | ||||
| > | > | ||||
| <FiPlus /> | |||||
| <FiPlus className="m-0 p-0" /> | |||||
| </button> | </button> | ||||
| )} | )} | ||||
| </div> | </div> | ||||
| {/* ovo ce biti zamenjeno konkretnim podacima */} | {/* ovo ce biti zamenjeno konkretnim podacima */} | ||||
| {(!error && requestedRooms && chatStatus === "pendingFetchRooms") || | {(!error && requestedRooms && chatStatus === "pendingFetchRooms") || | ||||
| chatStatus === "pendingAddRoom" ? ( | chatStatus === "pendingAddRoom" ? ( | ||||
| <p>Loading</p> | |||||
| <div className="spinner-border mt-3" role="status"> | |||||
| <span className="visually-hidden">Loading...</span> | |||||
| </div> | |||||
| ) : ( | ) : ( | ||||
| getView() | getView() | ||||
| )} | )} |
| }; | }; | ||||
| return ( | return ( | ||||
| <div className="px-3 bg-light-transparent rounded h-100 d-flex flex-column"> | |||||
| <div style={{ height: "80px" }}> | |||||
| <h2 className="py-1 m-0">{room.name}</h2> | |||||
| <div className="p-2 position-relative bg-main rounded h-100 d-flex flex-column"> | |||||
| <div className="p-2 px-3 pt-3 position-relative bg-light rounded h-100 d-flex flex-column"> | |||||
| <div style={{ height: "60px" }}> | |||||
| <h2 className="p-0 m-0">{room.name}</h2> | |||||
| <button | <button | ||||
| onClick={leftRoomHandler} | onClick={leftRoomHandler} | ||||
| style={{ | |||||
| border: 0, | |||||
| backgroundColor: "transparent", | |||||
| padding: 0, | |||||
| margin: 0, | |||||
| textDecoration: "underline", | |||||
| }} | |||||
| className='btn btn-danger mb-2 leave-btn' | |||||
| > | > | ||||
| Leave Room | Leave Room | ||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| <div className="messages p-3 border d-flex flex-column-reverse"> | |||||
| <div className="messages bg-white border "> | |||||
| <div className="overlay p-3 px-4 d-flex flex-column-reverse"> | |||||
| {/* mapirane poruke */} | {/* mapirane poruke */} | ||||
| {user && | {user && | ||||
| messages | messages | ||||
| {console.log("Message", n, "User id", user.id)} | {console.log("Message", n, "User id", user.id)} | ||||
| <p | <p | ||||
| className={`p-2 px-4 mb-0 rounded message ${ | className={`p-2 px-4 mb-0 rounded message ${ | ||||
| n.senderId !== user.id | |||||
| ? "bg-primary text-light" | |||||
| : "bg-light text-dark" | |||||
| n.isAccessMessage === true | |||||
| ? "text-muted small" | |||||
| : n.senderId !== user.id | |||||
| ? "bg-main-primary text-light chatMsg" | |||||
| : "bg-light text-dark chatMsg" | |||||
| }`} | }`} | ||||
| > | > | ||||
| {n.content} | {n.content} | ||||
| ) : ( | ) : ( | ||||
| "" | "" | ||||
| )} | )} | ||||
| {n.isAccessMessage !== true && n.username} | |||||
| {n.isAccessMessage !== true && n.senderId !== user?.id && n.username} | |||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| )) | )) | ||||
| .reverse()} | .reverse()} | ||||
| <div ref={messagesEndRef} /> | |||||
| <div ref={messagesEndRef} /></div> | |||||
| </div> | </div> | ||||
| <TypingBar id={activeRoom.id} /> | <TypingBar id={activeRoom.id} /> | ||||
| </InputGroup> | </InputGroup> | ||||
| </Form> | </Form> | ||||
| </div> | </div> | ||||
| </div> | |||||
| ); | ); | ||||
| }; | }; | ||||
| className={styles.greenIcon} | className={styles.greenIcon} | ||||
| onClick={onAcceptRequestHandler} | onClick={onAcceptRequestHandler} | ||||
| /> | /> | ||||
| <span className="px-1"></span> | |||||
| <FiXCircle | <FiXCircle | ||||
| className={styles.redIcon} | className={styles.redIcon} | ||||
| onClick={onRejectRequestHandler} | onClick={onRejectRequestHandler} |
| import React from 'react' | |||||
| import { useSelector } from 'react-redux'; | |||||
| import ChatWindow from './ChatWindow'; | |||||
| import React from "react"; | |||||
| import { useSelector } from "react-redux"; | |||||
| import ChatWindow from "./ChatWindow"; | |||||
| const MainContainer = ({showTerm}) => { | |||||
| const MainContainer = ({ showTerm }) => { | |||||
| const { activeRoom } = useSelector((state) => state.chat); | const { activeRoom } = useSelector((state) => state.chat); | ||||
| return ( | return ( | ||||
| <div className='h-100-auto-overflow p-3 w-100'> | |||||
| {showTerm === 'chats' && activeRoom !== null ? <ChatWindow room={activeRoom} /> : ''} | |||||
| {showTerm === 'empty' && ''} | |||||
| <div className="h-100-auto-overflow w-100"> | |||||
| {showTerm === "chats" || showTerm === "requests" ? ( | |||||
| activeRoom !== null ? ( | |||||
| <ChatWindow room={activeRoom} /> | |||||
| ) : ( | |||||
| <div className="w-100 h-100 ps-2 bg-main"> | |||||
| <div className="w-100 h-100 text-main bg-light d-flex justify-content-center align-items-center"> | |||||
| <h1 className="display-6">{showTerm === "chats" && "Please choose a chat room."}</h1> | |||||
| </div> | |||||
| </div> | |||||
| ) | |||||
| ) : ( | |||||
| "" | |||||
| )} | |||||
| {showTerm === "empty" && ""} | |||||
| </div> | </div> | ||||
| ) | |||||
| } | |||||
| ); | |||||
| }; | |||||
| export default MainContainer | |||||
| export default MainContainer; |
| }, []); | }, []); | ||||
| return ( | return ( | ||||
| <div className="w-25 mh-100-vh px-3 bg-light"> | |||||
| <div className="w-25 mh-100-vh px-2 pe-0 bg-main"> | |||||
| {showTerm === "chats" && <ChatList />} | {showTerm === "chats" && <ChatList />} | ||||
| {showTerm === "notifications" && <Activity />} | {showTerm === "notifications" && <Activity />} | ||||
| {showTerm === "requests" && <Requests />} | {showTerm === "requests" && <Requests />} |
| <p className="text-muted p-0 m-0 py-3 pb-4 text-start"> | <p className="text-muted p-0 m-0 py-3 pb-4 text-start"> | ||||
| Please enter your valid credentials | Please enter your valid credentials | ||||
| </p> | </p> | ||||
| <div style={{width:"570px"}} className='text-start alert alert-danger'> | |||||
| {error && | {error && | ||||
| error.map((n) => | |||||
| <p key={n} className="text-light bg-dark d-block p-0 m-0">{n.trim()}</p> | |||||
| error.map((n) => n.trim().length > 0 && | |||||
| " " +n.trim()+"." | |||||
| )} | )} | ||||
| </div> | |||||
| <FormGroup as={Row}> | <FormGroup as={Row}> | ||||
| <Col md="6" className="pe-1"> | <Col md="6" className="pe-1"> | ||||
| <FloatingLabel label="First Name" className="mb-3"> | <FloatingLabel label="First Name" className="mb-3"> |
| }; | }; | ||||
| return status === "pendingFetchRoomsForWhichRequestExist" ? ( | return status === "pendingFetchRoomsForWhichRequestExist" ? ( | ||||
| <p>Loading...</p> | |||||
| <div className="spinner-border" role="status"> | |||||
| <span className="visually-hidden">Loading...</span> | |||||
| </div> | |||||
| ) : ( | ) : ( | ||||
| <div className={styles.container}> | |||||
| <h3 className="p-0 m-0 py-3 text-center w-50 fw-light text-muted"> | |||||
| <div className=" h-100-auto-overflow"> | |||||
| <h4 className="p-0 m-0 py-3 w-100 text-start ps-3 text-light border-bottom border-light mb-2 bg-transparent"> | |||||
| Requests | Requests | ||||
| </h3> | |||||
| </h4> | |||||
| <div className={styles.requestContainer}> | <div className={styles.requestContainer}> | ||||
| {roomsForWhichRequestExist && | {roomsForWhichRequestExist && | ||||
| roomsForWhichRequestExist.length > 0 && | roomsForWhichRequestExist.length > 0 && |
| return ( | return ( | ||||
| <div className="min-vh-100 bg-white border-right px-1 d-flex flex-column pt-5"> | <div className="min-vh-100 bg-white border-right px-1 d-flex flex-column pt-5"> | ||||
| <Link | |||||
| to={'/main'} | |||||
| className="btn btn-white w-100 button-block button-block-flex-column" | |||||
| > | |||||
| <FiBell className="icon-fs" /> | |||||
| <h5 className="small text-muted text-center fw-light">feed</h5> | |||||
| </Link> | |||||
| <Link | <Link | ||||
| to={'/chats'} | to={'/chats'} | ||||
| className="btn btn-white button-block mt-4 w-100 button-block-flex-column" | |||||
| className="btn btn-white button-block w-100 pb-2 button-block-flex-column" | |||||
| > | > | ||||
| <FiMessageSquare className="icon-fs" /> | <FiMessageSquare className="icon-fs" /> | ||||
| <h5 className="small text-muted text-center fw-light">chat</h5> | <h5 className="small text-muted text-center fw-light">chat</h5> | ||||
| (user && user.roles.includes('Support') === true) && | (user && user.roles.includes('Support') === true) && | ||||
| <Link | <Link | ||||
| to={'/requests'} | to={'/requests'} | ||||
| className="btn btn-white button-block mt-4 w-100 button-block-flex-column" | |||||
| className="btn btn-white button-block w-100 button-block-flex-column" | |||||
| > | > | ||||
| <FaArrowAltCircleRight className="icon-fs" /> | <FaArrowAltCircleRight className="icon-fs" /> | ||||
| <h5 className="small text-muted text-center fw-light">Requests</h5> | <h5 className="small text-muted text-center fw-light">Requests</h5> | ||||
| </Link> | </Link> | ||||
| } | } | ||||
| {user && ( | |||||
| <Link | |||||
| to={'/profile'} | |||||
| className="btn btn-white button-block mt-4 w-100 button-block-flex-column" | |||||
| > | |||||
| <FiUser className="icon-fs" /> | |||||
| <h5 className="small text-muted text-center fw-light">profile</h5> | |||||
| </Link> | |||||
| )} | |||||
| <button | <button | ||||
| className="btn btn-white button-block mt-4 w-100 button-block-flex-column" | |||||
| className="btn btn-white button-block w-100 button-block-flex-column" | |||||
| onClick={logOutHandler} | onClick={logOutHandler} | ||||
| > | > | ||||
| <FiLogOut className="icon-fs" /> | <FiLogOut className="icon-fs" /> |
| }; | }; | ||||
| return ( | return ( | ||||
| <Accordion style={{ marginTop: 10 }} expanded={expanded}> | |||||
| <Accordion style={{ marginTop: 5 }} expanded={expanded}> | |||||
| <AccordionSummary | <AccordionSummary | ||||
| expandIcon={<ExpandMoreIcon />} | expandIcon={<ExpandMoreIcon />} | ||||
| aria-controls="panel1a-content" | aria-controls="panel1a-content" |
| } | } | ||||
| /* ======================================= root vars */ | /* ======================================= root vars */ | ||||
| :root { | :root { | ||||
| --main-bg: linear-gradient(45deg,#00c9ff, #92fe9d); | |||||
| --main-bg: linear-gradient(0deg,#00c9ff, #92fe9d); | |||||
| --main: #00c9ff; | --main: #00c9ff; | ||||
| --secondary: #92fe9d; | --secondary: #92fe9d; | ||||
| .bg-main{ | .bg-main{ | ||||
| background: var(--main-bg) !important; | background: var(--main-bg) !important; | ||||
| } | } | ||||
| .bg-main-primary{ | |||||
| background: var(--main) !important; | |||||
| } | |||||
| .bg-light-transparent{ | .bg-light-transparent{ | ||||
| background: rgba(255,255,255,0.75); | background: rgba(255,255,255,0.75); | ||||
| background-size: cover; | |||||
| background-position: center; | |||||
| } | } | ||||
| /* ======================================= heights & widths */ | /* ======================================= heights & widths */ | ||||
| .h-100-auto-overflow{ | .h-100-auto-overflow{ | ||||
| .btn-main{ | .btn-main{ | ||||
| background-color: var(--main) !important; | background-color: var(--main) !important; | ||||
| } | } | ||||
| .leave-btn{ | |||||
| position: absolute; | |||||
| right: 3rem; | |||||
| top: 1.25rem; | |||||
| } | |||||
| .button-block{ | .button-block{ | ||||
| height: 50px; | height: 50px; | ||||
| width: 50px; | width: 50px; | ||||
| margin-bottom: 20px !important; | |||||
| } | |||||
| button.button-block{ | |||||
| margin-top: 10px; | |||||
| } | } | ||||
| .button-block-flex-column{ | .button-block-flex-column{ | ||||
| display: flex; | display: flex; | ||||
| justify-content: center; | justify-content: center; | ||||
| align-items: center; | align-items: center; | ||||
| } | } | ||||
| .roomsBtn{ | |||||
| transition: 0.35s; | |||||
| } | |||||
| .roomsBtn:hover{ | |||||
| transition: 0.35s; | |||||
| padding: 0 20px; | |||||
| transform: scale(1.1); | |||||
| } | |||||
| /* ======================================= flex */ | /* ======================================= flex */ | ||||
| .flex-center{ | .flex-center{ | ||||
| display: flex; | display: flex; | ||||
| font-size: 1.25rem !important; | font-size: 1.25rem !important; | ||||
| text-transform: uppercase; | text-transform: uppercase; | ||||
| } | } | ||||
| .messages{ | .messages{ | ||||
| height: calc(100% - 160px); | |||||
| height: calc(100% - 130px); | |||||
| background: url('/public/background.jpg'); | |||||
| } | |||||
| .overlay{ | |||||
| height: 575px; | |||||
| background-color: rgba(255,255,255,0.9); | |||||
| overflow-y: scroll; | overflow-y: scroll; | ||||
| } | } | ||||
| /* ======================================= overrides */ | /* ======================================= overrides */ | ||||
| .message{ | .message{ | ||||
| max-width: 70%; | max-width: 70%; | ||||
| } | } | ||||
| .notification{ | |||||
| height: 30px; | |||||
| width: 45px; | |||||
| display: flex;justify-content: center;align-items: center; | |||||
| color: white; | |||||
| background-color: crimson; | |||||
| border-radius: 50%; | |||||
| font-size: 0.8rem; | |||||
| } | |||||
| .text-main{ | |||||
| color:var(--main) !important; | |||||
| font-weight: 900; | |||||
| } | |||||
| .chatMsg{ | |||||
| font-size: 1.25rem; | |||||
| } |
| return ( | return ( | ||||
| <Container fluid className='m-0 p-0 min-vh-100 bg-main d-flex justify-content-start'> | <Container fluid className='m-0 p-0 min-vh-100 bg-main d-flex justify-content-start'> | ||||
| <SideNavbar/> | <SideNavbar/> | ||||
| <MiddleContainer showTerm={'notifications'}/> | |||||
| <MainContainer showTerm={'notifications'}/> | |||||
| <MiddleContainer showTerm={'chats'}/> | |||||
| <MainContainer showTerm={'chats'}/> | |||||
| </Container> | </Container> | ||||
| ) | ) | ||||
| } | } |
| <Container fluid className='m-0 p-0 min-vh-100 bg-main d-flex justify-content-start'> | <Container fluid className='m-0 p-0 min-vh-100 bg-main d-flex justify-content-start'> | ||||
| <SideNavbar/> | <SideNavbar/> | ||||
| <MiddleContainer showTerm={'requests'}/> | <MiddleContainer showTerm={'requests'}/> | ||||
| <MainContainer showTerm={'empty'}/> | |||||
| <MainContainer showTerm={'requests'}/> | |||||
| </Container> | </Container> | ||||
| ) | ) | ||||
| } | } |