Skip to content

Commit

Permalink
Merge pull request #10 from Jaagrav/main
Browse files Browse the repository at this point in the history
Feature(s) Add: Collections Page
  • Loading branch information
saviomartin committed Aug 5, 2021
2 parents fa69ebc + 2b308d4 commit 7a0f1a6
Show file tree
Hide file tree
Showing 17 changed files with 613 additions and 21 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Code House comes up with a whole bunch of amazing features to provide you the be
- 📬 **Newsletter** (once a week)
- 💻 **Fully Responsive**
- 🔤 **Reply to a comment**
- 📩 **Create Collections with Bookmarks**
- 📱 **Drag and Drop to arrange collections**

Code House is the next revolutionary app to hunt the best cheat sheets for all types ✨️

Expand Down Expand Up @@ -148,6 +150,7 @@ After your PR got merged, you'll be automatically appared on [contributors page]
- [Material UI](http://material-ui.com/): for components
- [Animate.css](https://animate.style/): for smooth Animations
- [AOS](https://michalsnik.github.io/aos/): for scroll animations
- [React Beautiful DND](https://react-beautiful-dnd.netlify.app/): For Drag and Drop support on Collections Page
- [Vercel](http://vercel.com/): for hosting

## 🌈 What's next
Expand All @@ -161,7 +164,6 @@ Here are some idea that is coming really soon 👀
- Markdown support for feature requests
- Twitter and Facebook Auth, **In Progress ⏳️**
- Perform Operations with API
- Create Collection when bookmarking cheatsheets
- Featuring Cheatsheets on day basis
- Generate Cover Image for missing ones
- Settings page
Expand Down
25 changes: 16 additions & 9 deletions components/core/InfoBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,31 @@ const InfoBar = ({
const [text, setText] = useState("");

// destructuring currentPost[0]
const { id, cheatsheet_name, website_url, upvotes, comments } =
let { id, cheatsheet_name, website_url, upvotes, comments } =
currentPost.length > 0 && currentPost[0];

// fetching bookmarked cheatsheets and check if already bookmarked or not
const fetchBookmarkedCheatsheets = () => {
if (bookmarks.some((cheatsheet) => cheatsheet.id === id)) {
setIsBookMarked(true);
} else {
setIsBookMarked(false);
let bookmarked = false;
for(let bookmark of bookmarks) {
if(bookmark.id === id){
bookmarked = true;
}
}
setIsBookMarked(bookmarked);
};

// use effect to handle it
useEffect(() => {
fetchBookmarkedCheatsheets();
}, [bookmarks]);
if(currentPost.length > 0) {
id = currentPost[0].id;
cheatsheet_name = currentPost[0].cheatsheet_name;
website_url = currentPost[0].website_url;
upvotes = currentPost[0].upvotes;
comments = currentPost[0].comments;
}
return fetchBookmarkedCheatsheets();
}, [bookmarks, currentPost]);

useEffect(() => {
// normal state
Expand Down Expand Up @@ -251,8 +260,6 @@ const InfoBar = ({
}
};

console.log(comments);

return (
<div className="w-full lg:w-[65%] xl:w-[65%] h-full min-h-[90vh] bg-white rounded-md white-light-shadow border border-[#ddd] p-7 dark:bg-[#1F1F1F] dark:border-[#555] dark:text-white">
{loading ? (
Expand Down
4 changes: 4 additions & 0 deletions components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ export { default as FeatureComponent } from "./utils/FeatureComponent";
export { default as BmcButton } from "./utils/BmcButton";
export { default as TwitterBtn } from "./utils/TwitterBtn";
export { default as Loader } from "./utils/Loader";
export { default as Dnd } from "./utils/Dnd";
export { default as BookmarksItem} from "./utils/Bookmarks";
export { default as BookmarksDialog } from "./utils/BookmarksDialog";
export { default as Collection } from "./utils/Collection";
10 changes: 6 additions & 4 deletions components/utils/BookMarkItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { Btn } from "..";
// axios for data fetching
import axios from "axios";

const BookMarkItem = ({ data, bookmarks, fetchBookmarks }) => {
const BookMarkItem = ({ data, bookmarks, fetchBookmarks, animated, interactive }) => {
if(animated === undefined || animated === null) animated = true;
if(interactive === undefined || interactive === null) interactive = true;
const [meta, setMetadata] = useState([]);
const [loading, setLoading] = useState(false);

Expand Down Expand Up @@ -118,9 +120,9 @@ const BookMarkItem = ({ data, bookmarks, fetchBookmarks }) => {

return (
<div
className="cursor-pointer flex justify-start items-center flex-col p-5 px-4 rounded-md duration-500 white-light-shadow bg-white m-2 w-10/12 md:w-5/12 lg:w-3/12 border border-[#ddd] hover:border-[#3d5eff98] item-hover-text parent-for-image-scale h-[325px] parent-for-image-scale dark:border-[#555] dark:bg-[#1F1F1F] dark:text-white"
className={`whitespace-normal cursor-pointer flex justify-start items-center flex-col p-5 px-4 rounded-md duration-500 white-light-shadow bg-white w-[340px] border border-[#ddd] hover:border-[#3d5eff98] item-hover-text parent-for-image-scale h-[325px] parent-for-image-scale dark:border-[#555] dark:bg-[#1F1F1F] dark:text-white ${interactive ? "pointer-events-auto" : "pointer-events-none"}`}
onClick={goToCheatSheetPage}
data-aos="fade-left"
data-aos={animated ? "fade-left" : "none"}
>
{loading ? (
<div className="w-full h-full">
Expand Down Expand Up @@ -164,7 +166,7 @@ const BookMarkItem = ({ data, bookmarks, fetchBookmarks }) => {
>
{url.hostname && url.hostname}
</a>
<a href={website_url} target="_blank">
<a href={website_url} target="_blank" className="font-bold text-lg duration-500 hover:text-[#3d5eff]">
<h1 className="font-bold text-lg duration-500 hover:text-[#3d5eff]">
{cheatsheet_name.length > 50
? cheatsheet_name.slice(0, 50) + "..."
Expand Down
65 changes: 65 additions & 0 deletions components/utils/Bookmarks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useEffect, useState } from "react";

import {Droppable, Draggable} from "react-beautiful-dnd";

import BookMarkItem from "./BookMarkItem";

import {FiPlus, FiTrash2} from "react-icons/fi";
import {MdApps} from "react-icons/md";

export default function BookmarksItem({collectionId, bookmarkData, bookmarkIDs, entities, setEntities, bookmarks, fetchBookmarks}) {
const deleteBookmark = (index) => {
let collections = entities.collections;
let bookmarks = entities.bookmarks;
delete bookmarks[collections[collectionId].bookmarkIDs[index]];

collections[collectionId].bookmarkIDs.splice(index, 1);

console.log(collections, bookmarks);
setEntities({ ...entities, collections, bookmarks })
}

return (
<Droppable droppableId={collectionId} type="bookmark" direction="horizontal">
{
provided => (
<div
className="overflow-x-auto overflow-y-hidden w-full min-h-[355px] whitespace-nowrap flex"
{...provided.droppableProps}
ref={provided.innerRef}
>
{
bookmarkIDs ? (
bookmarkIDs.map((bookmarkID, index) => {
const contentIndex = parseInt(bookmarkID.replace("bookmark-", "")) - 1,
content = bookmarkData[contentIndex].content;
return <Draggable draggableId={bookmarkID} index={index} key={bookmarkID}>
{
provided => (
<div className="inline-block mt-4 mr-4" {...provided.draggableProps} style={provided.draggableProps.style} ref={provided.innerRef}>
<div className="w-[340px] min-h-[355px]">
<div className="rounded-t-lg dark:bg-[#ffffff20] w-min p-2 flex justify-start items-center mb-[-4px]">
<div {...provided.dragHandleProps}>
<MdApps className="mx-2 text-white text-xl" />
</div>
<FiTrash2 className="mx-2 text-white text-lg cursor-pointer" onClick={() => deleteBookmark(index)} />
</div>
<BookMarkItem
data={content}
bookmarks={bookmarks}
fetchBookmarks={fetchBookmarks}
animated={false}
/>
</div>
</div>
)
}
</Draggable>
})) : ("")
}
</div>
)
}
</Droppable>
)
}
51 changes: 51 additions & 0 deletions components/utils/BookmarksDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, {useEffect, useState } from "react";

import {Backdrop} from "@material-ui/core";
import BookMarkItem from './BookMarkItem';
import {FiX} from 'react-icons/fi';

export default function BookmarksDialog({entities, setEntities, bookmarks, fetchBookmarks, showBookmarks, setShowBookmarks}) {
const addToCollection = (data) => {
const newBookmarkID = `bookmark-${Object.keys(entities.bookmarks).length + 1}`;
let bookmarks = entities.bookmarks;
const newBookmark = {
id: newBookmarkID,
content: data
}
bookmarks.push(newBookmark);

let collections = entities.collections;
if(!collections[showBookmarks].bookmarkIDs) collections[showBookmarks].bookmarkIDs = [];

let bookmarkIDs = collections[showBookmarks].bookmarkIDs;
bookmarkIDs.push(newBookmarkID);

setEntities({ ...entities, bookmarks, collections });
setShowBookmarks(null);
}

return (
<Backdrop open={!!showBookmarks} className="z-[100000] flex items-center justify-center bg-[#00000042]" onClick={() => setShowBookmarks(false)}>
<div className="glassmorphism rounded-lg h-3/4 w-3/4 p-6 overflow-y-auto" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between">
<h1 className="text-[#fff] text-3xl">All Bookmarks</h1>
<FiX className="cursor-pointer text-[#fff] text-lg" onClick={e => setShowBookmarks(false)} />
</div>
<div className="mt-4 flex flex-wrap items-center justify-start">
{bookmarks.map((cheatsheet, key) => (
<div className="cursor-pointer mb-4 mr-4" onClick={() => addToCollection(cheatsheet)}>
<BookMarkItem
data={cheatsheet}
key={key}
bookmarks={bookmarks}
fetchBookmarks={fetchBookmarks}
animated={false}
interactive={false}
/>
</div>
))}
</div>
</div>
</Backdrop>
)
}
60 changes: 60 additions & 0 deletions components/utils/Collection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, {useEffect, useState, useRef} from "react";

import {Draggable} from "react-beautiful-dnd";

import BookmarksItem from "./Bookmarks";

import {FiPlus, FiTrash2} from "react-icons/fi";
import {MdApps} from "react-icons/md";

export default function Collection({entities, setEntities, collectionId, index, showBookmarks, setShowBookmarks, bookmarks, fetchBookmarks}) {
const [collectionTitle, setCollectionTitle] = useState(entities.collections[collectionId].title),
collectionTitleRef = useRef();

const deleteCollection = () => {
let collections = entities.collections;
delete collections[collectionId];

let collectionOrder = entities.collectionOrder;
collectionOrder.splice(collectionOrder.indexOf(collectionId), 1);

setEntities({ ...entities, collections, collectionOrder });
}

useEffect(() => {
let collections = entities.collections;
collections[collectionId].title = collectionTitle;

setEntities({...entities, collections});
}, [collectionTitle]);

return (
<>
{
entities?.collections[collectionId]?.id && <Draggable draggableId={entities.collections[collectionId].id} index={index} isDragDisabled={false}>
{
provided => (
<div className="mb-8 rounded-lg bg-[#ffffff20] dark:bg-[#ffffff20] border-b-[1px] border-[#eeeeee30]" {...provided.draggableProps} ref={provided.innerRef} style={provided.draggableProps.style}>
<div className="h-full w-full p-4">
<div className="flex items-center justify-between">
<div className="" {...provided.dragHandleProps}>
<MdApps className="mb-1 mr-2 text-white text-xl" />
</div>
<input spellCheck={false} value={collectionTitle} onChange={(e) => setCollectionTitle(e.target.value)} className="text-2xl w-full font-bold text-[#fff] bg-transparent cursor-text" />
<div className="flex items-center justify-between">
<FiTrash2 className="text-white text-lg cursor-pointer ml-2" onClick={deleteCollection} />
<FiPlus className="text-white text-lg cursor-pointer ml-2" onClick={() => {setShowBookmarks(collectionId)}}/>
</div>
</div>
<div className="h-full w-full">
<BookmarksItem collectionId={collectionId} bookmarkData={entities.bookmarks} bookmarkIDs={entities.collections[collectionId].bookmarkIDs} entities={entities} setEntities={setEntities} bookmarks={bookmarks} fetchBookmarks={fetchBookmarks}/>
</div>
</div>
</div>
)
}
</Draggable>
}
</>
)
}

1 comment on commit 7a0f1a6

@vercel
Copy link

@vercel vercel bot commented on 7a0f1a6 Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.