Advertisement
ec1117

Untitled

Jul 12th, 2022
876
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import logo from './logo.svg';
  2. import './App.css';
  3. import React, { useState, useEffect, useRef } from 'react';
  4. import { AiOutlineSearch, AiOutlineFolder,  } from 'react-icons/ai'
  5. import { BsThreeDotsVertical } from 'react-icons/bs'
  6. import { MdOutlineKeyboardArrowRight, MdKeyboardArrowDown, MdHttp } from 'react-icons/md'
  7. import classNames from 'classnames';
  8. import axios from 'axios'
  9. import FolderPopup from './components/FolderPopup';
  10. import listenForOutsideClicks from './utils/listenForOutsideClicks';
  11.  
  12. /*
  13. properties of a file
  14.  - title
  15.  - depth
  16.  - par_id
  17.  - id
  18.  - array of child_id
  19.  - type
  20.  - link if bookmark
  21. */
  22.  
  23. function App() {
  24.  
  25.   const [files, setFiles] = useState([]);
  26.   const [curId, setCurID] = useState(1);
  27.   const [optionsOpen, setOptionsOpen] = useState(false);
  28.   const [linkPopupOpen, setLinkPopupOpen] = useState(false);
  29.   const [folderPopupOpen, setFolderPopupOpen] = useState(false);
  30.   const [collapsed, setCollapsed] = useState([])
  31.   const [searchQuery, setSearchQuery] = useState("")
  32.   const [Flistening, setFListening] = useState(false);
  33.   const [Llistening, setLListening] = useState(false);
  34.  
  35.   useEffect( () => {
  36.     async function fetchAll() {
  37.       const ret = await axios.get('/polls/getAll');
  38.       console.log(ret);
  39.       setFiles(ret.data)
  40.      
  41.       let tmpCollapsed = [];
  42.         ret.data.forEach( x => {
  43.           if(x.type==="Folder" && !collapsed.find(y => y.id===x.id)){
  44.             tmpCollapsed.push({"id":x.id, "isCollapsed": false});
  45.           }
  46.         })
  47.       setCollapsed(tmpCollapsed);
  48.     }
  49.     fetchAll();
  50.    
  51.    
  52.   }, [])
  53.  
  54.   useEffect( ()=> {
  55.     if(folderPopupOpen){
  56.       setFListening(false);
  57.     } else setFListening(true);
  58.   }, [folderPopupOpen])
  59.  
  60.   useEffect( ()=> {
  61.     setLListening(!linkPopupOpen)
  62.   }, [linkPopupOpen])
  63.  
  64.   return (
  65.     <div>
  66.       {/* <div className="resize cursor-sw-resize bg-pink-400">
  67.         REsize me
  68.       </div> */}
  69.       <NavBar optionsOpen={optionsOpen} setOptionsOpen={setOptionsOpen} setFolderPopupOpen={setFolderPopupOpen} setLinkPopupOpen={setLinkPopupOpen}
  70.       searchQuery={searchQuery} setSearchQuery={setSearchQuery}/>
  71.      
  72.       {
  73.         folderPopupOpen && <FolderPopup setFolderPopupOpen={setFolderPopupOpen} curId={curId} setFiles={setFiles} files={files}
  74.         Flistening={Flistening} setFListening={setFListening}/>
  75.         // Flistening kinda redundant cuz is just folderPopupOpen
  76.       }
  77.       {
  78.         linkPopupOpen && <LinkPopup setLinkPopupOpen={setLinkPopupOpen} curId={curId} setFiles={setFiles}
  79.         listening={Llistening} setListening={setLListening}
  80.         // listening={!linkPopupOpen} setListening={setLinkPopupOpen} this part doesn't seem too work bc too fast and registers click already?
  81.         />
  82.       }
  83.      
  84.       <div className="fixed min-w-fit h-screen resize cursor-e-resize">
  85.         <SideBar2 files={files} curId={curId} setCurID={setCurID} collapsed={collapsed} setCollapsed={setCollapsed}/>
  86.       </div>
  87.      
  88.       <div className="absolute left-60 rounded-xl right-10 mr-10 shadow-xl border-[1px] border-gray-200 overflow-hidden">
  89.         <Viewport content={files.find( x => x.id === curId)} files={files} setCurID={setCurID} collapsed={collapsed} setCollapsed={setCollapsed}
  90.         searchQuery={searchQuery} setSearchQuery={setSearchQuery}
  91.         />
  92.       </div>
  93.     </div>
  94.   );
  95. }
  96.  
  97. function LinkPopup({setLinkPopupOpen, curId, files, listening, setListening}) {
  98.    
  99.   const [formName, setFormName] = useState("");
  100.   const [formURL, setFormURL] = useState("");
  101.   const menuRef = useRef(null);
  102.  
  103.   const SaveButton = async () => {
  104.     setLinkPopupOpen(false);
  105.     var bodyFormData = new FormData();
  106.     let tmp = formURL;
  107.     if(!formURL.includes("http")){
  108.       tmp = "http://"+formURL;
  109.       setFormURL(tmp);
  110.     }
  111.     bodyFormData.append('link', tmp);
  112.     bodyFormData.append('title', formName);
  113.     bodyFormData.append('par_id', curId);
  114.     for (var key of bodyFormData.entries()) {
  115.       console.log(key[0] + ', ' + key[1]);
  116.     }
  117.     const ret = await axios.post('/polls/addLink/', bodyFormData);
  118.     console.log(ret);
  119.   }
  120.   const CancelButton = () => {
  121.     setLinkPopupOpen(false);
  122.   }
  123.   const nameChange = (e) => {
  124.     setFormName(e.target.value);
  125.   }
  126.   const URLChange = (e) => {
  127.     setFormURL(e.target.value);
  128.   }
  129.  
  130.   useEffect(listenForOutsideClicks(
  131.     listening,
  132.     setListening,
  133.     menuRef,
  134.     setLinkPopupOpen,
  135.   ));
  136.  
  137.   return (
  138.     <div>
  139.       <div className="bg-cover bg-gray-600 z-40 opacity-75 absolute left-0 right-0 top-0 bottom-0">
  140.       </div>
  141.       <div ref={menuRef} className="bg-white absolute w-[30rem] h-72 left-2/4 top-2/4 rounded-lg z-50 -translate-x-1/2 -translate-y-1/2">
  142.         <div className="ml-10 font-semibold mt-6 mb-3"> Add Bookmark
  143.         </div>
  144.         <div className="ml-10 mb-8">
  145.           <div className="text-[10px] font-bold text-gray-700 mb-2">
  146.             Name
  147.           </div>
  148.           <div>
  149.             <input className="bg-gray-200 border-b-2 border-white focus:outline-none rounded-sm pl-2 h-7 w-11/12
  150.            focus:mb-2 focus:border-blue-500 transition" value={formName} onChange={nameChange}></input>
  151.           </div>
  152.         </div>
  153.         <div className="ml-10">
  154.         <div className="text-[10px] font-bold text-gray-700 mb-2">
  155.             URL
  156.           </div>
  157.           <div>
  158.             <input className="bg-gray-200 border-b-2 focus:outline-none rounded-sm pl-2 h-7 w-11/12
  159.            focus:mb-2 focus:border-blue-500 transition" value={formURL} onChange={URLChange}></input>
  160.           </div>
  161.         </div>
  162.         <div className="absolute bottom-0 right-0">
  163.           <button onClick={CancelButton} className="bg-white border-gray-300 border-[1px] rounded-md w-16 h-8 mr-4 text-blue-600 font-bold text-sm hover:bg-blue-50">Cancel</button>
  164.           <button onClick={SaveButton} className="bg-blue-700 rounded-md w-16 h-8 mr-4 mb-4 text-white font-semibold text-sm hover:bg-blue-500">Save</button>
  165.         </div>
  166.       </div>
  167.     </div>
  168.    
  169.   )
  170. }
  171.  
  172. function NavBar({optionsOpen, setOptionsOpen, setFolderPopupOpen, setLinkPopupOpen, searchQuery, setSearchQuery}) {
  173.   const optionsClicked = async() => {
  174.     const ret = await axios.get('/polls/1/getBookmark')
  175.     setOptionsOpen(!optionsOpen);
  176.   }
  177.   const optionsFocusedOut = (e) => {
  178.     setOptionsOpen(false)
  179.   }
  180.   const newBookmark = () => {
  181.     setLinkPopupOpen(true);
  182.     setOptionsOpen(false);
  183.   }
  184.   const newFolder = () => {
  185.     setFolderPopupOpen(true);
  186.     setOptionsOpen(false);
  187.   }
  188.   const searchChange = (e) => {
  189.     setSearchQuery(e.target.value)
  190.   }
  191.  
  192.   return (
  193.     <div className="flex h-10 mt-1 mb-3 items-center">
  194.       <img src={require('./img/chromeIcon.png')} className="h-6 w-6 mx-4"></img>
  195.       <div className=" text-2xl font-sans font-semibold">
  196.         Bookmarks
  197.       </div>
  198.       <div className="border-gray-300 rounded-3xl flex flex-1 ml-8 z-10 h-full bg-gray-100
  199.            text-sm mx-3 items-center">
  200.         <AiOutlineSearch className="h-6 w-6 hover:bg-gray-300 hover: cursor-pointer rounded-full ml-2"/>
  201.         <input className="pl-3 focus:outline-none bg-gray-100 w-full"
  202.           placeholder="Search bookmarks"
  203.           type="search" onChange={searchChange} value={searchQuery}/>
  204.       </div>
  205.       <div className="h-8 w-8 mx-3 hover:bg-gray-300 rounded-full" onClick={optionsClicked} onBlur={optionsFocusedOut}>
  206.         {!optionsOpen && <BsThreeDotsVertical className="hover:cursor-pointer h-4 w-4 top-4 right-5 absolute mx-auto" onBlur={optionsFocusedOut}/>
  207.         }
  208.       </div>
  209.       {optionsOpen && <div className="shadow-2xl border-gray-100 border-[1px] rounded-md bg-white absolute top-2 right-3 w-40 z-10 flex flex-col justify-start text-sm">
  210.           <div className="ml-5 mt-3 my-2">
  211.             <button onClick={newBookmark}> Add new bookmark</button>
  212.           </div>
  213.           <div className="ml-5 mb-3">
  214.             <button onClick={newFolder}> Add new folder</button>
  215.           </div>
  216.         </div>
  217.       }
  218.     </div>
  219.   )
  220. }
  221.  
  222. function SideBar2({files, curId, setCurID, dfsNode=1, collapsed, setCollapsed}) {
  223.   let tmp = files.find(x => x.id===dfsNode);
  224.   let foldersBel=[]
  225.   if(tmp){
  226.     let childArr = tmp.child_id.split(',').slice(1);
  227.     childArr.forEach( x => {
  228.       let z = files.find(y => y.id === parseInt(x))
  229.       if (z && z.type=="Folder"){
  230.         foldersBel.push(z)
  231.       }
  232.     })
  233.   }
  234.  
  235.   return (
  236.     <div>
  237.       {
  238.         foldersBel.map( (x,ind) => {
  239.           return <div className="">
  240.             <div className={classNames("hover:bg-gray-200 rounded-r-full",
  241.             {
  242.               // TODO: should be taking from child if child hovered
  243.             'bg-blue-200': x.id===curId,
  244.             'hover: bg-blue-200': x.id===curId
  245.             }
  246.             )}>
  247.               <File key={ind} val={x} curId={curId} setCurID={setCurID} collapsed={collapsed} setCollapsed={setCollapsed}/>
  248.             </div>
  249.             { collapsed && collapsed.find(y=> y.id===x.id) &&
  250.               !collapsed.find(y=> y.id===x.id).isCollapsed?
  251.               <SideBar2 files={files} curId={curId} setCurID={setCurID} dfsNode={x.id} collapsed={collapsed} setCollapsed={setCollapsed}/>:
  252.               <div>
  253.               </div>
  254.             }
  255.           </div>
  256.         })
  257.       }
  258.     </div>
  259.  
  260.   )
  261. }
  262. function File({val, curId, setCurID, collapsed, setCollapsed}) {
  263.   const handleClick = () => {
  264.     setCurID(val.id);
  265.   }
  266.   const handleCollapseClick = () => {
  267.     let ind = collapsed.findIndex(x => x.id === val.id);
  268.     if(ind){
  269.       let tmp = [...collapsed];
  270.       let OBJ=collapsed[ind];
  271.       tmp[ind] = {"id":val.id, "isCollapsed":!OBJ.isCollapsed}
  272.       setCollapsed(tmp);
  273.     }
  274.   }
  275.   let TMP=false;
  276.   if(collapsed){
  277.     let TMP2=collapsed.find(x => x.id===val.id)
  278.     if(TMP2){
  279.       TMP = TMP2.isCollapsed;
  280.     }
  281.   }
  282.  
  283.   return <div >
  284.     <div className={classNames('hover:cursor-pointer rounded-r-full h-10 flex flex-col',
  285.       {
  286.         'bg-blue-200':val.id===curId,
  287.         'hover: bg-blue-200':val.id===curId,
  288.         [`pl-${val.depth}`]: true
  289.       }
  290.     )} onClick={handleClick}>
  291.       <div className="flex">
  292.         {val.type=="Folder" &&
  293.             <div className="flex mt-2 mr-2">
  294.               <div className="rounded-full hover:bg-gray-500 py-0.5" onClick={handleCollapseClick}>
  295.                 { !TMP ? <MdKeyboardArrowDown className="h-6 w-6 mr-1"/>:
  296.                 <MdOutlineKeyboardArrowRight className="h-6 w-6 mr-1"/>
  297.                 }
  298.               </div>
  299.               <AiOutlineFolder className="h-6 w-6"/>
  300.             </div>
  301.             }
  302.         <div className="pt-2 inline-block">
  303.           {val.title}
  304.         </div>
  305.       </div>
  306.     </div>
  307.   </div>
  308. }
  309.  
  310. function Viewport({content,files, setCurID, searchQuery, setSearchQuery}){
  311.  
  312.   const [focusedCard, setCardFocus] = useState(-1);
  313.   const [cardOptionsOpen, setCardOptionsOpen] = useState(false);
  314.   const [cardOptionsID, setCardOptionsID] = useState(-1);
  315.   const [Olistening, setOlistening] = useState(false);
  316.  
  317.   useEffect( ()=> {
  318.       setOlistening(!cardOptionsOpen)
  319.   }, [cardOptionsOpen])
  320.  
  321.   if(content === undefined){
  322.     return <div></div>
  323.   }
  324.   //files search for id
  325.   let childArr = content.child_id.split(",");
  326.  
  327.   var Cards = [];
  328.   if(searchQuery.length>0){
  329.     files.forEach(x => {
  330.       if(x.title.includes(searchQuery) || x.link.includes(searchQuery)){
  331.         Cards.push( <Card cardID={x.id} files={files} setCurID={setCurID}
  332.           setCardFocus={setCardFocus} focusedCard={focusedCard}
  333.           cardOptionsOpen={cardOptionsOpen} setCardOptionsOpen={setCardOptionsOpen} cardOptionsID={cardOptionsID} setCardOptionsID={setCardOptionsID}/>)
  334.       }
  335.     })
  336.   } else {
  337.     childArr.map( (ID, ind) => {
  338.       Cards.push( <Card cardID={ID} files={files} setCurID={setCurID}
  339.         setCardFocus={setCardFocus} focusedCard={focusedCard}
  340.         cardOptionsOpen={cardOptionsOpen} setCardOptionsOpen={setCardOptionsOpen} cardOptionsID={cardOptionsID} setCardOptionsID={setCardOptionsID}/>)
  341.     })
  342.   }
  343.   return Cards;
  344. }
  345.  
  346. function Card({cardID, files, setCurID, setCardFocus, focusedCard, cardOptionsID, setCardOptionsID, cardOptionsOpen, setCardOptionsOpen}){
  347.  
  348.   const handleClick = (e) => {
  349.     setCardFocus(parseInt(cardID));
  350.     if(e.detail>=2){
  351.       const itemSelected = files.find( x => parseInt(cardID) === x.id);
  352.       if (itemSelected && itemSelected.type==="Folder"){
  353.         setCurID(parseInt(cardID));
  354.       } else {
  355.         window.open(itemSelected.link)
  356.       }
  357.     }
  358.   }
  359.   const handleOptionsClick = () => {
  360.     setCardOptionsOpen(true);
  361.     setCardOptionsID(cardID);
  362.     console.log("HEY", cardID);
  363.   }
  364.   const handleOpenNewTabClick = () => {
  365.     window.open(files.find(x => x.id===parseInt(cardID)).link);
  366.   }
  367.   const handleDeleteBookmarkClick = async () => {
  368.     // await axios.delete('/')
  369.   }
  370.   const handleEditBookmarkClick = () => {
  371.   }
  372.   const handleOpenNewWindowClick = () => {
  373.     window.open(files.find(x => x.id===parseInt(cardID)).link,"_blank", "fullscreen=1",
  374.     "location=1",
  375.     "titlebar=1",
  376.     "status=1",
  377.     "menubar=1",);
  378.   }
  379.  
  380.   let parsed = parseInt(cardID);
  381.   if(isNaN(parsed)){
  382.     return <div/>
  383.   }
  384.   let tmp = files.find( x => x.id === parsed)
  385.   let imgStr = tmp.link+"/favicon.ico"
  386.   let imgFound = true;
  387.   // try {
  388.   //   axios.get(imgStr);
  389.   // } catch (error) {
  390.   //   console.log("ASJDKLsa");
  391.   //   imgFound=false;
  392.   // }
  393.   if(tmp === undefined){
  394.     console.log("IS UNDEFINED");
  395.   } else {
  396.     return <div onClick={handleClick} className={classNames("cursor-pointer py-2 pl-6 flex",
  397.       {
  398.         'bg-blue-200': parseInt(cardID) === focusedCard
  399.       })}>
  400.       { tmp.type==="Folder"?
  401.         <AiOutlineFolder className="h-6 w-6 mr-4"/>:
  402.           imgFound?
  403.           <img src={imgStr} className="w-6 h-6 mr-4" />
  404.           :<img src="http://google.com/favicon.ico" className="w-6 h-6 mr-4" />
  405.       }
  406.       {tmp.title}
  407.       <div className="absolute right-4 mt-1">
  408.         <div onClick={handleOptionsClick} className="hover:bg-gray-200 absolute right-4 mt-1 rounded-full">
  409.           <BsThreeDotsVertical />
  410.         </div>
  411.         { cardOptionsOpen && cardID===cardOptionsID &&
  412.           <div className="shadow-xl border-gray-100 border-[1px] bg-white rounded-md relative top-0 right-3 w-44 z-10 flex flex-col justify-start text-sm">
  413.             <div className="ml-5 my-3">
  414.               <button onClick={handleOpenNewTabClick}> Open in New Tab</button>
  415.             </div>
  416.             <div className="ml-5 mb-3">
  417.               <button onClick={handleOpenNewWindowClick}> Open in New Window</button>
  418.             </div>
  419.             <div className="ml-5 mb-3">
  420.               <button onClick={handleEditBookmarkClick}>Edit Bookmark</button>
  421.             </div>
  422.             <div className="ml-5 mb-3">
  423.               <button onClick={handleDeleteBookmarkClick}>Delete Bookmark</button>
  424.             </div>
  425.         </div>
  426.         }
  427.       </div>
  428.     </div>
  429.   }
  430. }
  431.  
  432. export default App;
  433.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement