diff --git a/src/app/store.js b/src/app/store.js index c8eec0a7..80ba98f4 100644 --- a/src/app/store.js +++ b/src/app/store.js @@ -3,6 +3,7 @@ import authReducer from "../features/auth/authSlice"; import userDetailReducer from "../features/userDetail/userDetailSlice"; import goalReducer from "../features/goals/goalSlice"; import blogReducer from "../features/blogs/blogSlice"; +import notesReducer from "../features/notes/notesSlice"; import forumReducer from "../features/forum/forumSlice"; import viewReducer from "../features/feeds/views/viewSlice"; import feedReducer from "../features/feeds/feedsSlice"; @@ -26,6 +27,8 @@ export default configureStore({ blogs: blogReducer, blogComments: blogCommentsReducer, + notes: notesReducer, + forums: forumReducer, feeds: feedReducer, diff --git a/src/components/Common/InputEditor/InputEditor.jsx b/src/components/Common/InputEditor/InputEditor.jsx index b590d7ed..f2adcc50 100644 --- a/src/components/Common/InputEditor/InputEditor.jsx +++ b/src/components/Common/InputEditor/InputEditor.jsx @@ -1,18 +1,23 @@ import React, { useEffect, useState } from "react"; import { InputEditorContainer, InputEditorTheInput, InputEditorLabel, InputEditorPreview } from "./InputEditorElements"; -const InputEditor = ({ content, label }) => { +const InputEditor = ({ content, label, onCopyChanges }) => { const [value, setValue] = useState(""); useEffect(() => { setValue(content); }, [content, label]); + const handleChange = (e) => { + setValue(e.target.value); + onCopyChanges(label, e.target.value); + }; + return ( {label} {value} - setValue(e.target.value)} value={value} /> + ); }; diff --git a/src/components/Common/MarkdownEditor/MarkdownEditor.jsx b/src/components/Common/MarkdownEditor/MarkdownEditor.jsx index ef4e721e..b5468059 100644 --- a/src/components/Common/MarkdownEditor/MarkdownEditor.jsx +++ b/src/components/Common/MarkdownEditor/MarkdownEditor.jsx @@ -9,28 +9,36 @@ import { import rehypeSanitize from "rehype-sanitize"; import "./MarkdownEditor.css"; -const MarkdownEditor = ({ content, label, previewModeOnly }) => { +const MarkdownEditor = ({ content, label, previewModeOnly, onCopyChanges }) => { const [value, setValue] = useState(); useEffect(() => { setValue(content); - }, [content]); + }, [content, label]); if (previewModeOnly) return ; + const handleChange = (value) => { + setValue(value); + onCopyChanges(label, value); + }; return ( {label} { - const [notes, setNotes] = useState(DUMMY_DATA); + const dispatch = useDispatch(); + const { notes } = useSelector(({ notes }) => notes); const [searchTerm, setSearchTerm] = useState(""); const [filteredNotes, setFilteredNotes] = useState([]); const [pickedNote, setPickedNote] = useState({}); const [needToAdd, setNeedToAdd] = useState(false); - useEffect(() => { - const savedNotes = JSON.parse(localStorage.getItem("react-notes-app-data")); - if (savedNotes !== "") { - setNotes((savedNotes) => { - const sortNotes = savedNotes.sort((a, b) => { - if (a.pinned !== b.pinned) { - if (a.pinned === true) return -1; - return 1; - } - return 0; - }); - return [...sortNotes]; - }); - } - }, []); - - useEffect(() => { - localStorage.setItem("react-notes-app-data", JSON.stringify(notes)); - }, [notes]); - useEffect(() => { const newFilteredNotes = notes.filter((note) => { return ( @@ -80,38 +41,11 @@ const NoteApp = () => { setPickedNote(pickedNote !== -1 ? pickedNote : {}); }; const handlePinNote = (noteId) => { - let needToHappen = true; - setNotes((prevNotes) => { - const pinnedNoteIndex = prevNotes.findIndex((note) => note.id === noteId); - if (pinnedNoteIndex > -1 && needToHappen) { - prevNotes[pinnedNoteIndex].pinned = !prevNotes[pinnedNoteIndex].pinned; - } - needToHappen = false; - const sortNotes = prevNotes.sort((a, b) => { - if (a.pinned !== b.pinned) { - if (a.pinned === true) return -1; - return 1; - } - return 0; - }); - return [...sortNotes]; - }); + dispatch(notePin(noteId)); }; const handleOpenAddNewNoteMode = () => { setNeedToAdd(true); - }; - // const addNote = (text) => { - // const newNote = { - // text, - // id: nanoid(), - // }; - // const newNotes = [...notes, newNote]; - // setNotes(newNotes); - // }; - - const handleDataWhenDeleteNote = (id) => { - const newNotes = notes?.filter((note) => note.id !== id); - setNotes(newNotes); + setPickedNote({}); }; const handleCloseMDEditorMode = () => { setNeedToAdd(false); @@ -132,22 +66,19 @@ const NoteApp = () => { onChange={handleSearchTermChange} /> - {filteredNotes} {pickedNote} - - // ); }; diff --git a/src/components/Dashboard/Notetaker/NoteDescription.jsx b/src/components/Dashboard/Notetaker/NoteDescription.jsx index 83a03d85..7d875743 100644 --- a/src/components/Dashboard/Notetaker/NoteDescription.jsx +++ b/src/components/Dashboard/Notetaker/NoteDescription.jsx @@ -14,17 +14,19 @@ import NotePinning from "./NotePinning"; import { RiMore2Fill } from "react-icons/ri"; import MarkdownEditor from "../../Common/MarkdownEditor"; import InputEditor from "../../Common/InputEditor"; +import { useDispatch } from "react-redux"; +import { noteAdd, noteEdit, noteRemove } from "../../../features/notes/notesSlice"; -const NoteDescription = ({ children, onPin, onDelete, needToAdd, onCloseAddMode }) => { +const NoteDescription = ({ children, onPin, needToAdd, onCloseAddMode, onChangePickedNote }) => { + const dispatch = useDispatch(); const [showNote, setShowNote] = useState(children); const [needToEdit, setNeedToEdit] = useState(false); - useEffect(() => { setShowNote(children); }, [children]); const handleDeleteNote = () => { - onDelete(children.id); + dispatch(noteRemove(children.id)); setShowNote({}); }; const handleClose = () => { @@ -32,7 +34,27 @@ const NoteDescription = ({ children, onPin, onDelete, needToAdd, onCloseAddMode onCloseAddMode(false); setShowNote({}); }; - const handleSaveNote = () => { + const handleCopyNoteData = (label, content) => { + setShowNote((prevCopyNote) => { + return { + ...prevCopyNote, + [label]: content, + }; + }); + }; + const handleSaveNote = (newNote) => { + if (!newNote.title && !newNote.description) { + dispatch(noteRemove(newNote.id)); + onChangePickedNote({}); + handleClose(); + return; + } + if (needToEdit) { + dispatch(noteEdit({ ...newNote, id: children.id })); + } else if (needToAdd) { + dispatch(noteAdd(newNote)); + } + onChangePickedNote(newNote); handleClose(); }; return ( @@ -53,7 +75,12 @@ const NoteDescription = ({ children, onPin, onDelete, needToAdd, onCloseAddMode )} {(needToAdd || needToEdit) && ( - + handleSaveNote(showNote)} + /> @@ -62,10 +89,14 @@ const NoteDescription = ({ children, onPin, onDelete, needToAdd, onCloseAddMode {needToAdd || needToEdit ? ( - + ) : ( - {showNote.title || (showNote.id ? `UntitledNote #${showNote.id}` : "")} + {showNote.title || (showNote.id ? `UntitledNote #${showNote.id.substr(0, 5)}` : "")} )} @@ -74,6 +105,7 @@ const NoteDescription = ({ children, onPin, onDelete, needToAdd, onCloseAddMode ) : ( { - const textCleanFromTags = text?.replace(/<[^>]+>/g, ""); + const textCleanFromTags = text?.replace(/<[^>]+>|-|\[|\]|#/g, ""); return textCleanFromTags?.length > letters ? `${textCleanFromTags.slice(0, letters)}...` : textCleanFromTags; }; @@ -18,7 +18,7 @@ const NoteItem = ({ id, title, description, pinned, onPick, onPin }) => { const [shortDescr, setShortDescr] = useState(""); useEffect(() => { - setShortTitle(() => (title ? shortText(title, 30) : `UntitledNote #${id}`)); + setShortTitle(() => (title ? shortText(title, 30) : `UntitledNote #${id.substr(0, 5)}`)); setShortDescr(() => (description ? shortText(description, 60) : "undescribedNote")); }, [title, description]); diff --git a/src/components/Dashboard/Notetaker/NoteList.jsx b/src/components/Dashboard/Notetaker/NoteList.jsx index 796a5e0d..c7bff4a8 100644 --- a/src/components/Dashboard/Notetaker/NoteList.jsx +++ b/src/components/Dashboard/Notetaker/NoteList.jsx @@ -1,10 +1,11 @@ import React from "react"; -import { NotesListContainer } from "./NoteElements"; +import { NotesListContainer, NotesListNoFound } from "./NoteElements"; import NoteItem from "./NoteItem"; const NoteList = ({ children, onPick, onPin }) => { return ( + {!children.length && There Are No Notes} {children.map((note) => ( ))} diff --git a/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx b/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx deleted file mode 100644 index c2a5a1a6..00000000 --- a/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx +++ /dev/null @@ -1,42 +0,0 @@ -// import { useState, React } from "react"; -// import './NoteApp.css' - -// const AddNote = ({ handleAddNote }) => { -// const [noteText, setNoteText] = useState(""); -// const [characterLimit, setCharacterLimit] = useState(200); -// const [characterLimitError, setCharacterLimitError] = useState(false); - -// const handleChange = (event) => { -// setNoteText(event.target.value); -// if (noteText.trim().length > characterLimit) { -// setCharacterLimitError(true); -// } else { -// setCharacterLimitError(false); -// } -// }; - -// const handleSaveClick = () => { -// if (noteText.trim().length > 0 && noteText.trim().length < characterLimit) { -// handleAddNote(noteText); -// setNoteText(""); -// } -// }; - -// return ( -//
-// -//
-// {characterLimit - noteText.length} characters remaining. -// -//
-//
-// ); -// }; - -// export default AddNote; diff --git a/src/components/Dashboard/Notetaker/Old/NoteAppOld.css b/src/components/Dashboard/Notetaker/Old/NoteAppOld.css deleted file mode 100644 index 94a59680..00000000 --- a/src/components/Dashboard/Notetaker/Old/NoteAppOld.css +++ /dev/null @@ -1,57 +0,0 @@ -/* .note { - background-color: #242526; - color: #828282; - border: 1px solid #828282; - border-radius: 8px; - padding: 10px; - margin-bottom: 20px; - max-height: 300px; - width: 300px; - overflow-x: none; - overflow-wrap: break-word; - word-break: break-all; -} - -.note textarea { - width: 100%; - height: 100%; - border: none; - background-color: #242526; - color: #828282; - font-size: 16px; - resize: none; - outline: none; - overflow-x: none; -} - -.note button { - background-color: #828282; - color: #090909; - border: none; - padding: 8px 16px; - font-size: 16px; - border-radius: 4px; - margin-left: 30px; - cursor: pointer; -} - -.note button:hover { - background-color: #090909; - color: #828282; -} - -.note .delete-button { - margin-bottom: -5px; - margin-left: 260px; - cursor: pointer; -} - -.note .character-limit-error { - color: red; -} - -.container { - display: flex; - flex-direction: row; - align-items: flex-start; -} */ diff --git a/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx b/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx deleted file mode 100644 index 554f379d..00000000 --- a/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx +++ /dev/null @@ -1,16 +0,0 @@ -// import React from "react"; -// import Note from "./Note"; -// import AddNote from "./AddNote"; - -// const NoteListOld = ({ id, notes, handleAddNote, handleDeleteNote }) => { -// return ( -//
-// {notes?.map((note) => ( -// -// ))} -// -//
-// ); -// }; - -// export default NoteListOld; diff --git a/src/components/Dashboard/Notetaker/Old/NoteOld.jsx b/src/components/Dashboard/Notetaker/Old/NoteOld.jsx deleted file mode 100644 index 091b0576..00000000 --- a/src/components/Dashboard/Notetaker/Old/NoteOld.jsx +++ /dev/null @@ -1,13 +0,0 @@ -// import React from "react"; -// import { GoTrash } from 'react-icons/go'; - -// const Note = ({ id, text, handleDeleteNote }) => { -// return ( -//
-//

{text}

-// handleDeleteNote(id)}>Delete Note -//
-// ); -// }; - -// export default Note; diff --git a/src/components/Feeds/Feeds.jsx b/src/components/Feeds/Feeds.jsx index 5814268f..7af85b6f 100644 --- a/src/components/Feeds/Feeds.jsx +++ b/src/components/Feeds/Feeds.jsx @@ -1,4 +1,4 @@ -import React, { useState,useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { FeedsContainer, MiddleSection } from "./FeedsElements"; import { Wrapper } from "../Dashboard/Profile/ProfileElements"; import AddFeed from "./PostForm/AddFeed"; @@ -10,12 +10,8 @@ import LoadingSpinner from "../Other/MixComponents/Spinner/LoadingSpinner"; import UnderMaintenance from "../Other/UnderMaintenance/UnderMaintenance"; import apiStatus from "../../features/apiStatus"; import FeedTags from "./FeedTags/FeedTags"; -import { - LeftContainer, - SearchContainer, -} from "../Explore/ExploreElements"; -import { SearchBox, SearchIcon, SearchInput } from "../CaptureTheFlag/CTFElements"; - +import { LeftContainer, SearchContainer } from "../Explore/ExploreElements"; +import { SearchBox, SearchIcon, SearchInput } from "../Common/SearchInputBox/SearchInputBoxElements"; const Feeds = () => { const dispatch = useDispatch(); @@ -24,7 +20,7 @@ const Feeds = () => { const { userDetails, isUserDetailLoading, isUserDetailError, userDetailMessage } = useSelector( (state) => state.userDetail, ); - + useEffect(() => { if (isFeedError) console.log(feedMessage); if (isUserDetailError) console.log(userDetailMessage); @@ -63,7 +59,7 @@ const Feeds = () => { - + diff --git a/src/features/notes/notesService.js b/src/features/notes/notesService.js new file mode 100644 index 00000000..5fc87a40 --- /dev/null +++ b/src/features/notes/notesService.js @@ -0,0 +1 @@ +// we will add here the services after finising with the back-end diff --git a/src/features/notes/notesSlice.js b/src/features/notes/notesSlice.js new file mode 100644 index 00000000..9d10765e --- /dev/null +++ b/src/features/notes/notesSlice.js @@ -0,0 +1,46 @@ +import { createSlice, nanoid } from "@reduxjs/toolkit"; + +const initialState = { + notes: [], +}; + +export const notesSlice = createSlice({ + name: "notes", + initialState, + reducers: { + noteAdd: { + reducer: (state, action) => { + state.notes = [...state.notes, action.payload]; + }, + prepare: (note) => { + const id = nanoid(); + return { payload: { ...note, id } }; + }, + }, + noteRemove: (state, action) => { + state.notes = state.notes.filter((note) => note.id !== action.payload); + }, + noteEdit: (state, action) => { + const editedNote = action.payload; + const indexOfEditedNote = state.notes.findIndex((note) => note.id === editedNote.id); + if (indexOfEditedNote < 0) state.notes = [...state.notes, editedNote]; + state.notes[indexOfEditedNote] = { ...state.notes[indexOfEditedNote], ...editedNote }; + }, + notePin: (state, action) => { + const pinnedNoteIndex = state.notes.findIndex((note) => note.id === action.payload); + if (pinnedNoteIndex > -1) { + state.notes[pinnedNoteIndex].pinned = !state.notes[pinnedNoteIndex].pinned; + } + state.notes.sort((a, b) => { + if (a.pinned !== b.pinned) { + if (a.pinned === true) return -1; + return 1; + } + return 0; + }); + }, + }, +}); + +export const { notesReset, noteAdd, noteRemove, noteEdit, notePin } = notesSlice.actions; +export default notesSlice.reducer;