From 21732916bd931a723e0ab8d96e2d362b541b04f1 Mon Sep 17 00:00:00 2001 From: Pudi-Sravan Date: Sun, 23 Jun 2024 04:08:51 +0530 Subject: [PATCH] Delete channel & improved Searching --- .../homepage/Totalsearch/totalsearch.jsx | 132 ++++++++++++++++++ .../Totalsearch/totalsearch.module.css | 59 ++++++++ .../homepage/channels/addchannel.jsx | 107 +++++++------- .../homepage/channels/addchannelmember.jsx | 25 ++-- .../homepage/channels/channelchat.jsx | 86 ++++++++++++ .../homepage/channels/membersofchannel.jsx | 62 ++++---- .../homepage/dm chats/chatboxnav.jsx | 32 ++--- src/Front_end/homepage/homepage.jsx | 10 +- src/Front_end/homepage/homepage.module.css | 14 +- 9 files changed, 417 insertions(+), 110 deletions(-) create mode 100644 src/Front_end/homepage/Totalsearch/totalsearch.jsx create mode 100644 src/Front_end/homepage/Totalsearch/totalsearch.module.css diff --git a/src/Front_end/homepage/Totalsearch/totalsearch.jsx b/src/Front_end/homepage/Totalsearch/totalsearch.jsx new file mode 100644 index 0000000..40b184d --- /dev/null +++ b/src/Front_end/homepage/Totalsearch/totalsearch.jsx @@ -0,0 +1,132 @@ +import { useState, useEffect, useContext } from "react"; +import { fetchUserDmChats, fetchUserchannelsbyid } from "../../database"; +import totalsearchCSS from "./totalsearch.module.css"; +import { Allconvers } from "../../context api/context"; +import { Channelcontext } from "../../context api/channelcontext.jsx"; +import { Chatcontext } from "../../context api/chatcontext"; + +const Totalsearch = () => { + const { currentUser, setDm, setChannelchat, setConformdm, setchat } = + useContext(Allconvers); + const { dispatch } = useContext(Chatcontext); + const { dispatchchannel } = useContext(Channelcontext); + const [dmcontacts, setdmcontacts] = useState([]); + const [channels, setchannels] = useState([]); + const [search, setSearch] = useState(""); + const [searchResults, setSearchResults] = useState([]); + + useEffect(() => { + const fetchdata = async () => { + const fetcheddm = await fetchUserDmChats(currentUser[0]); + const filteredDm = fetcheddm.filter((contact) => contact.showstatus); + setdmcontacts(filteredDm); + const fetchedchannel = await fetchUserchannelsbyid(currentUser[0].id); + setchannels(fetchedchannel); + }; + fetchdata(); + }, [currentUser]); + + useEffect(() => { + if (search.trim() === "") { + setSearchResults([]); + return; + } + + const filteredDmContacts = dmcontacts.filter((contact) => + contact.userinfo.uusername.toLowerCase().includes(search.toLowerCase()) + ); + + const filteredChannels = channels.filter((channel) => + channel.channelname.toLowerCase().includes(search.toLowerCase()) + ); + + setSearchResults([...filteredDmContacts, ...filteredChannels]); + }, [dmcontacts, channels, search]); + + const handleInput = (e) => { + setSearch(e.target.value); + }; + + const handleChannelSelect = (channel) => { + dispatchchannel({ type: "Change_channel", payload: channel }); + resetSearch(); + setDm(false); + setChannelchat(true); + setConformdm(false); + setchat(false); + }; + + const handleDMSelect = (userinfo) => { + dispatch({ type: "Change_user", payload: userinfo }); + resetSearch(); + setDm(false); + setChannelchat(false); + setConformdm(false); + setchat(true); + }; + + const resetSearch = () => { + setSearch(""); + setSearchResults([]); + }; + + const handleBlur = (e) => { + // Check if the relatedTarget (where focus is going) is not inside results + if ( + e.relatedTarget && + !e.currentTarget.contains(e.relatedTarget) && + e.relatedTarget.className !== totalsearchCSS.resultItem + ) { + setShowResults(false); + } + }; + + return ( +
+ setSearchResults([])} + onBlur={handleBlur} + placeholder="Search a Channel or Contact...." + className={totalsearchCSS.input} + /> + {search.trim() !== "" && ( +
+ {searchResults.map((result, index) => ( +
{ + if (result.userinfo) { + handleDMSelect(result.userinfo); + } else { + handleChannelSelect(result); + } + }} + > + {result.userinfo ? ( +
+

DM Contact: {result.userinfo.uusername}

+

Email: {result.userinfo.uemail}

+
+ ) : ( +
+

Channel: {result.channelname}

+

Created by: {result.channelinfo.createdby}

+
+ )} +
+ ))} +
+ )} +
+ ); +}; + +export default Totalsearch; diff --git a/src/Front_end/homepage/Totalsearch/totalsearch.module.css b/src/Front_end/homepage/Totalsearch/totalsearch.module.css new file mode 100644 index 0000000..34b6cd4 --- /dev/null +++ b/src/Front_end/homepage/Totalsearch/totalsearch.module.css @@ -0,0 +1,59 @@ +* { + box-sizing: border-box; + } + + .container { + width: 100%; + max-width: 600px; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; /* Ensure relative positioning for absolute children */ + } + + .input { + width: 100%; + padding: 10px; + font-size: 15px; + height: 85%; + border-radius: 5px; + } + + .results { + width: 100%; + display: grid; + gap: 1px; + position: absolute; + z-index: 2; + top: calc(100% + 10px); /* Adjust top position to create a gap below the input */ + background-color: #fff; /* Ensure a white background for results */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Add shadow for a card-like appearance */ + border-radius: 4px; /* Rounded corners for the results box */ + max-height: 300px; /* Limit max height if needed */ + overflow-y: auto; /* Enable scrolling if results exceed max height */ + } + + .resultItem { + padding: 10px; + border: 1px solid #ccc; + background-color: #f9f9f9; + cursor: pointer; /* Add pointer cursor for clickable items */ + } + + .resultItem:hover { + background-color: #ebebeb; /* Lighten background on hover */ + } + + .item { + padding: 10px; + } + + .dmContact { + border-left: 5px solid #1a73e8; /* Blue color for DM Contacts */ + } + + .channel { + border-left: 5px solid #34a853; /* Green color for Channels */ + } + \ No newline at end of file diff --git a/src/Front_end/homepage/channels/addchannel.jsx b/src/Front_end/homepage/channels/addchannel.jsx index 7b29170..8e9de7c 100644 --- a/src/Front_end/homepage/channels/addchannel.jsx +++ b/src/Front_end/homepage/channels/addchannel.jsx @@ -16,77 +16,76 @@ const Addchannel = () => { const { addchannel, setAddchannel, currentUser } = useContext(Allconvers); const { dispatchchannel } = useContext(Channelcontext); const [currentuserchannels, setCurrentuserchannels] = useState({}); - console.log(currentUser[0]); const channel = useRef(""); + useEffect(() => { const fetchchannel = async () => { const user = await fetchUserchannels(currentUser[0]); if (user) { setCurrentuserchannels(user); - console.log(currentuserchannels); } }; fetchchannel(); - - // Cleanup function to potentially unsubscribe from subscriptions or close connections }, [currentUser]); - useEffect(() => { - console.log(currentuserchannels); - }, [currentuserchannels]); - const clear = () => { channel.current.value = ""; setAddchannel(false); }; - const create = async () => { - const channelname = channel.current.value; + + const createChannel = async () => { + const channelname = channel.current.value.trim(); + if (channelname === "") { + return; // Exit early if channel name is empty + } + const id = uuid(); const creation = await insertchannelid(id, channelname); const insertionlist = await insertchanneldolistid(id); + if (creation && insertionlist) { const checkIdExists = (channels) => channels.some((channel) => channel.channel_id === id); const currentUserIdExists = checkIdExists(currentuserchannels); + if (!currentUserIdExists) { - const { data, error } = await supabase - .from("channels_list") - .update({ - channels: [ - ...currentuserchannels, - { - channel_id: id, - channelname: channelname, - channelinfo: { - createdby: currentUser[0].username, - createdbyid: currentUser[0].id, - adminid: [{ id: currentUser[0].id }], + try { + const { data, error } = await supabase + .from("channels_list") + .update({ + channels: [ + ...currentuserchannels, + { + channel_id: id, + channelname: channelname, + channelinfo: { + createdby: currentUser[0].username, + createdbyid: currentUser[0].id, + adminid: [{ id: currentUser[0].id }], + }, + date: new Date().toISOString(), + allowshow: true, }, - date: new Date().toISOString(), - allowshow: true, - }, - ], - }) - .eq("id", currentUser[0].id) - .select(); + ], + }) + .eq("id", currentUser[0].id) + .select(); - if (error) { - console.error("Error updating channels:", error); - } else { - console.log("channel updated:", data); - const members = await fetchUserchannelmembers(id); - console.log(members); - const newmember = [ - ...members, - { - member_id: currentUser[0].id, - member_name: currentUser[0].username, - member_mail: currentUser[0].email, - }, - ]; - const insert = insertchannelmember(id, newmember); - if (insert) { + if (error) { + console.error("Error updating channels:", error); + } else { + console.log("channel updated:", data); + const members = await fetchUserchannelmembers(id); + const newmember = [ + ...members, + { + member_id: currentUser[0].id, + member_name: currentUser[0].username, + member_mail: currentUser[0].email, + }, + ]; + await insertchannelmember(id, newmember); dispatchchannel({ type: "Change_channel", payload: { @@ -101,12 +100,23 @@ const Addchannel = () => { }, }); } - + } catch (error) { + console.error("Error creating channel:", error); + } finally { setAddchannel(false); } } } }; + + const handleKeyPress = (e) => { + if (e.key === "Enter") { + createChannel(); + } else if (e.key === "Escape") { + clear(); + } + }; + if (addchannel) { return (
@@ -120,13 +130,14 @@ const Addchannel = () => { placeholder="Enter the Channel Name" className={AddchannnelCSS.input} ref={channel} + onKeyDown={handleKeyPress} />
Clear
-
+
Create
@@ -134,6 +145,8 @@ const Addchannel = () => {
); } + + return null; }; export default Addchannel; diff --git a/src/Front_end/homepage/channels/addchannelmember.jsx b/src/Front_end/homepage/channels/addchannelmember.jsx index 2871a97..2e434fc 100644 --- a/src/Front_end/homepage/channels/addchannelmember.jsx +++ b/src/Front_end/homepage/channels/addchannelmember.jsx @@ -38,19 +38,20 @@ const Addmember = () => { } }, [refreshchannel]); - const handleInput = (e) => { - if (e.code === "Enter") { - handleSearch(); - } - }; + const handleInput = async (e) => { + setUsername(e.target.value); // Update username state - const handleSearch = async () => { - const fetchedUser = await UserdetailsbyName(Username); + // Call handleSearch only if the input value is not empty + if (e.target.value.trim() !== "") { + const fetchedUser = await UserdetailsbyName(e.target.value); - if (fetchedUser && fetchedUser.length > 0) { - setUser(fetchedUser); // Update searched user data + if (fetchedUser && fetchedUser.length > 0) { + setUser(fetchedUser); + } else { + setUser(null); // Set user to null to display "No User Found" + } } else { - setUser(null); // Set user to null to display "No User Found" + setUser(null); // Clear user state if input value is empty } }; @@ -154,8 +155,8 @@ const Addmember = () => { type="text" className={addmemberCSS.input} placeholder="Search for a member" - onChange={(e) => setUsername(e.target.value)} - onKeyDown={handleInput} + onChange={handleInput} // Trigger search on input change + value={Username} />
diff --git a/src/Front_end/homepage/channels/channelchat.jsx b/src/Front_end/homepage/channels/channelchat.jsx index 55d492c..1d53fa7 100644 --- a/src/Front_end/homepage/channels/channelchat.jsx +++ b/src/Front_end/homepage/channels/channelchat.jsx @@ -8,12 +8,17 @@ import { IoMdPersonAdd } from "react-icons/io"; import { fetchUserchannelmessages, fetchUserchannels, + updatechannel, + fetchUserchannelsbyid, + Getuserdetails, + allidsinlist, } from "../../database.jsx"; import { Channelcontext } from "../../context api/channelcontext.jsx"; import { ChannelMessage } from "./channelmessage.jsx"; import { IoMdContacts } from "react-icons/io"; import { MdAssignmentAdd } from "react-icons/md"; import { FaTasks } from "react-icons/fa"; +import { RiDeleteBin6Fill } from "react-icons/ri"; export const Channelchats = () => { const textRef = useRef(""); //usestate didnot work but useref worked to make the input clear after updation @@ -254,6 +259,83 @@ export const Channelchats = () => { }; message(); }, [channel_data.channel_id]); + const Removechannel = async () => { + try { + if ( + channel_data.channeladmins.some( + (admin) => admin.id === currentUser[0].id + ) + ) { + const allids = await allidsinlist(); + for (const Id of allids) { + const userChannels = await fetchUserchannelsbyid(Id.id); + const usermail = await Getuserdetails(Id.id); + console.log(usermail); + if (userChannels.length > 0) { + const newchannels = userChannels.filter( + (channel) => channel.channel_id !== channel_data.channel_id + ); + if ( + userChannels?.some( + (channe) => channe.channel_id === channel_data.channel_id + ) + ) { + try { + const response = await fetch( + `http://localhost:${ + import.meta.env.VITE_Backend_Port + }/api/sendUserEmail`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + to: usermail[0].email, + subject: `Channel was deleted`, + message: `A Channel:"${channel_data.channelname}",was deleted by "${currentUser[0].username}"`, + }), + } + ); + if (!response.ok) { + throw new Error("Failed to send email"); + } + console.log("Email sent successfully"); + } catch (error) { + console.error("Error sending email:", error); + } + } + const updatedUserChannels = await updatechannel( + Id.id, + newchannels + ); + + console.log( + "Updated userChannels in database:", + updatedUserChannels + ); + } + } + } + const { error: delerr } = await supabase + .from("channels_message") + .delete() + .eq("channel_id", channel_data.channel_id); + + const channelscurrent = await fetchUserchannelsbyid(currentUser[0].id); + console.log(channelscurrent); + const updatedchannel = channelscurrent.filter( + (channel) => channel.channel_id !== channel_data.channel_id + ); + + const chanresult = await updatechannel( + currentUser[0].id, + updatedchannel + ); + } catch (error) { + console.error("Error removing member:", error); + } + }; return ( <> @@ -278,6 +360,10 @@ export const Channelchats = () => { onClick={() => setaddchannelmember(true)} // Call the function to update state style={{ cursor: "pointer" }} /> + Removechannel()} // Call the function to update state + style={{ cursor: "pointer" }} + /> ) : allowshow && accepted ? ( <> diff --git a/src/Front_end/homepage/channels/membersofchannel.jsx b/src/Front_end/homepage/channels/membersofchannel.jsx index fb464df..b36145e 100644 --- a/src/Front_end/homepage/channels/membersofchannel.jsx +++ b/src/Front_end/homepage/channels/membersofchannel.jsx @@ -242,33 +242,41 @@ const Showmembers = () => { memb(); }, [channel_data.channel_id]); - const handleInput = (e) => { - if (e.code === "Enter") { - handleSearch(); - } + const handleInputChange = (e) => { + setUsername(e.target.value); + handleSearch(); // Call handleSearch on every input change }; - const handleSearch = async () => { - setfetchdone(false); - const currentmembers = await fetchchannelmember(channel_data.channel_id); - const currentmems = currentmembers[0].channel_members; - const matcheds = currentmems.filter( - (currentmem) => - currentmem.member_name.toLowerCase().includes(username.toLowerCase()) //case insensitive search - ); - const detailedMembers = await Promise.all( - matcheds.map(async (memberId) => { - try { - const memberDetails = await Getuserdetails(memberId.member_id); - return memberDetails; - } catch (error) { - console.error(error); - return null; - } - }) - ); - setMembers(detailedMembers.filter(Boolean)); //removes null data - setfetchdone(true); + setfetchdone(false); // Start fetching + + try { + const currentMembers = await fetchchannelmember( + channel_data.channel_id + ); + const currentMems = currentMembers[0].channel_members; + + const matcheds = currentMems.filter((currentMem) => + currentMem.member_name.toLowerCase().includes(username.toLowerCase()) + ); + + const detailedMembers = await Promise.all( + matcheds.map(async (memberId) => { + try { + const memberDetails = await Getuserdetails(memberId.member_id); + return memberDetails; + } catch (error) { + console.error(error); + return null; + } + }) + ); + + setMembers(detailedMembers.filter(Boolean)); + setfetchdone(true); // Fetching done + } catch (error) { + console.error("Error fetching data:", error); + setfetchdone(true); // Ensure to set fetch status to true even on error + } }; const threedotselect = (memberId) => { @@ -659,8 +667,8 @@ const Showmembers = () => { type="text" className={channelmembersCSS.input} placeholder="Search for a member" - onChange={(e) => setUsername(e.target.value)} - onKeyDown={handleInput} + value={username} + onChange={handleInputChange} />
diff --git a/src/Front_end/homepage/dm chats/chatboxnav.jsx b/src/Front_end/homepage/dm chats/chatboxnav.jsx index 5b1fd6a..3cf83bc 100644 --- a/src/Front_end/homepage/dm chats/chatboxnav.jsx +++ b/src/Front_end/homepage/dm chats/chatboxnav.jsx @@ -30,21 +30,21 @@ export default function Searchuser({ currentUser }) { fetchdmdata(); }, [combinedId]); - // Handle search input and fetch user details - const handleSearch = async () => { - const fetchedUser = await UserdetailsbyName(Username); - - if (fetchedUser && fetchedUser.length > 0) { - setUser(fetchedUser); + // Trigger search on input change + const handleInput = async (e) => { + setUsername(e.target.value); // Update username state + + // Call handleSearch only if the input value is not empty + if (e.target.value.trim() !== "") { + const fetchedUser = await UserdetailsbyName(e.target.value); + + if (fetchedUser && fetchedUser.length > 0) { + setUser(fetchedUser); + } else { + setUser(null); // Set user to null to display "No User Found" + } } else { - setUser(null); // Set user to null to display "No User Found" - } - }; - - // Trigger search on Enter key press - const handleInput = (e) => { - if (e.code === "Enter") { - handleSearch(); + setUser(null); // Clear user state if input value is empty } }; @@ -205,8 +205,8 @@ export default function Searchuser({ currentUser }) { name="to" className={chatboxnavCSS.input} placeholder="Find a User" - onKeyDown={handleInput} - onChange={(e) => setUsername(e.target.value)} + onChange={handleInput} // Trigger search on input change + value={Username} // Bind input value to state />
diff --git a/src/Front_end/homepage/homepage.jsx b/src/Front_end/homepage/homepage.jsx index 0d502ef..4210d4e 100644 --- a/src/Front_end/homepage/homepage.jsx +++ b/src/Front_end/homepage/homepage.jsx @@ -30,7 +30,7 @@ import { FaTasks } from "react-icons/fa"; import Viewutask from "./ToDo_list/viewusertask"; import Assigntaskself from "./ToDo_list/mytododlist"; import TodoListChanges from "../Mailing/mailsender"; - +import Totalsearch from "./Totalsearch/totalsearch"; function Home(data) { const { @@ -230,7 +230,6 @@ function Home(data) { return ( <> - {viewchanneltasks ? : <>} {viewtask ? : <>} {assigntask ? : <>} @@ -241,12 +240,12 @@ function Home(data) { {isLoading ? (

Loading...

// Display loading message while fetching ) : ( - -
-
+
+ +
@@ -392,7 +391,6 @@ function Home(data) {
- )} diff --git a/src/Front_end/homepage/homepage.module.css b/src/Front_end/homepage/homepage.module.css index de5ae91..d35934b 100644 --- a/src/Front_end/homepage/homepage.module.css +++ b/src/Front_end/homepage/homepage.module.css @@ -24,10 +24,20 @@ .top { background-color: rgb(74, 1, 83); width: 100%; - height: 4%; + height: 5%; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} +.search { + width: 600px; + height: 100%; + position: relative; } + .bottom { - height: 96%; + height: 95%; width: 100%; background-color: rgb(74, 1, 83); display: flex;