From c8bb06384757e5c1351265103d6b6ad724342131 Mon Sep 17 00:00:00 2001 From: alceil <47685349+alceil@users.noreply.github.com> Date: Sat, 16 May 2026 18:58:58 +0530 Subject: [PATCH] Refactor paste components into modular utilities --- src/components/App/App.jsx | 82 +++++------------ src/components/HomePage/HomePage.jsx | 18 ++++ src/components/Modal/Modal.jsx | 68 ++++---------- src/components/ShareActions/ShareActions.jsx | 18 ++++ src/components/ShareContent/ShareContent.jsx | 10 ++ src/components/SharePage/SharePage.jsx | 96 ++++++-------------- src/components/Typing/Typing.jsx | 41 +++++---- src/config/editorConfig.js | 11 +++ src/config/toastConfig.js | 16 ++++ src/services/pasteService.js | 13 +++ src/utils/sanitizePasteContent.js | 23 +++++ src/utils/shareUrl.js | 1 + 12 files changed, 199 insertions(+), 198 deletions(-) create mode 100644 src/components/HomePage/HomePage.jsx create mode 100644 src/components/ShareActions/ShareActions.jsx create mode 100644 src/components/ShareContent/ShareContent.jsx create mode 100644 src/config/editorConfig.js create mode 100644 src/config/toastConfig.js create mode 100644 src/services/pasteService.js create mode 100644 src/utils/sanitizePasteContent.js create mode 100644 src/utils/shareUrl.js diff --git a/src/components/App/App.jsx b/src/components/App/App.jsx index 2b29851..f136fbf 100644 --- a/src/components/App/App.jsx +++ b/src/components/App/App.jsx @@ -1,84 +1,52 @@ -import React, { useState } from "react"; -import Header from "../Header/Header"; - -import Typing from "../Typing/Typing"; -import axios from "axios"; +import React, { useCallback, useState } from "react"; import { Route, Switch, useHistory, withRouter } from "react-router-dom"; +import { toast } from "react-toastify"; +import useDarkMode from "use-dark-mode"; +import Header from "../Header/Header"; +import HomePage from "../HomePage/HomePage"; import SharePage from "../SharePage/SharePage"; -import style from "./App.module.css"; import ToggleDarkMode from "../ToggleDarkMode/ToggleDarkMode"; -import { ToastContainer, toast } from 'react-toastify'; - -// Dark mode custom -import useDarkMode from 'use-dark-mode'; +import { createPaste } from "../../services/pasteService"; +import { hasPasteContent } from "../../utils/sanitizePasteContent"; +import style from "./App.module.css"; const App = () => { const history = useHistory(); const [shareText, setShareText] = useState(""); - const [created, setCreated] = useState(false); - const darkMode = useDarkMode(false); - // const [routeNav, setRouteNav] = useState(false); - - const handleInputChange = (inputValue) => { + const handleInputChange = useCallback((inputValue) => { setShareText(inputValue); - }; + }, []); - const onSubmit = (inputValue) => { - setCreated(true); - console.log("pressed dude"); - // console.log(shareText); - //http://localhost:4000 - console.log("Text:"+shareText); - if(shareText === "


" || !shareText ) { - console.log("Empty Text"); + const onSubmit = async () => { + if (!hasPasteContent(shareText)) { toast.error("Error : Empty Text"); - - } else { - axios - .post("https://justpasteitapi.herokuapp.com/add", { content: shareText }) - .then((response) => { - let id = response.data["_id"]; - history.push("/" + id); + return; + } - console.log(id); - }) - .catch((error) => { - console.log(error); - }); + try { + const paste = await createPaste(shareText); + history.push(`/${paste._id}`); + } catch (error) { + toast.error("Error : Unable to save text"); + console.error(error); } - } + }; return (
-
- +
+ - - + -
-
+
); }; diff --git a/src/components/HomePage/HomePage.jsx b/src/components/HomePage/HomePage.jsx new file mode 100644 index 0000000..2b45252 --- /dev/null +++ b/src/components/HomePage/HomePage.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import { ToastContainer } from "react-toastify"; +import Typing from "../Typing/Typing"; +import { defaultToastConfig } from "../../config/toastConfig"; + +const HomePage = ({ handleInputChange, onSubmit }) => ( + <> + + + +); + +export default HomePage; diff --git a/src/components/Modal/Modal.jsx b/src/components/Modal/Modal.jsx index e2e90f0..948e02d 100644 --- a/src/components/Modal/Modal.jsx +++ b/src/components/Modal/Modal.jsx @@ -3,11 +3,12 @@ import "./Modal.css"; import { ToastContainer, toast } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import { CopyToClipboard } from "react-copy-to-clipboard"; -import Button from '../Button/Button'; +import Button from "../Button/Button"; +import { defaultToastConfig, toastStyle } from "../../config/toastConfig"; +import { getShareUrl } from "../../utils/shareUrl"; + const MODAL_STYLES = { position: "fixed", - // display: 'flex', - // flexFlow: 'column', width: "50%", top: "50%", left: "50%", @@ -28,31 +29,18 @@ const OVERLAY_STYLES = { zIndex: 1000, }; -const getUrl = (id) => { - return `${window.location.protocol}//${window.location.host}/${id}`; - -} - -export default function Modal({ id, open, children, onClose }) { +const Modal = ({ id, open, onClose }) => { if (!open) return null; - console.log(id); - var url = getUrl(id); + + const url = getShareUrl(id); const notify = () => toast("Link Copied📋"); + return ( <>
@@ -64,39 +52,15 @@ export default function Modal({ id, open, children, onClose }) {
- - - */}
); - // document.getElementById('portal') -} +}; + +export default Modal; diff --git a/src/components/ShareActions/ShareActions.jsx b/src/components/ShareActions/ShareActions.jsx new file mode 100644 index 0000000..6faecad --- /dev/null +++ b/src/components/ShareActions/ShareActions.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import { ToastContainer } from "react-toastify"; +import Button from "../Button/Button"; +import style from "../SharePage/SharePage.module.css"; +import { defaultToastConfig, toastStyle } from "../../config/toastConfig"; + +const ShareActions = ({ onCopy, onShare }) => ( +
+
+); + +export default ShareActions; diff --git a/src/components/ShareContent/ShareContent.jsx b/src/components/ShareContent/ShareContent.jsx new file mode 100644 index 0000000..7fe4db7 --- /dev/null +++ b/src/components/ShareContent/ShareContent.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import style from "../SharePage/SharePage.module.css"; + +const ShareContent = ({ content }) => ( +
+

+

+); + +export default ShareContent; diff --git a/src/components/SharePage/SharePage.jsx b/src/components/SharePage/SharePage.jsx index 3770edc..7f26523 100644 --- a/src/components/SharePage/SharePage.jsx +++ b/src/components/SharePage/SharePage.jsx @@ -1,87 +1,45 @@ -import React, { useState,useEffect}from "react"; -import {useParams} from 'react-router-dom'; -import Button from "../Button/Button"; -import Modal from '../Modal/Modal' -import axios from "axios"; +import React, { useCallback, useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; import copy from "copy-to-clipboard"; -import style from "./SharePage.module.css"; -import { ToastContainer, toast } from 'react-toastify'; -import xss from 'xss'; -import 'react-toastify/dist/ReactToastify.css'; -import 'highlight.js/styles/atom-one-light.css'; +import { toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import "highlight.js/styles/atom-one-light.css"; +import Modal from "../Modal/Modal"; +import ShareActions from "../ShareActions/ShareActions"; +import ShareContent from "../ShareContent/ShareContent"; +import { getPaste } from "../../services/pasteService"; +import { sanitizePasteContent } from "../../utils/sanitizePasteContent"; const SharePage = () => { - const {id} = useParams(); - const [isOpen, setIsOpen] = useState(false) - const [data, setData] = useState(''); - - const options = { - whiteList: { - pre: ["class"], - span: ["class"], - p: [], - br: [], - strong: [], - em: [], - u: [], - ul: [], - li: [], - ol: [], - a: ["href", "rel", "target"] - } - }; + const { id } = useParams(); + const [isOpen, setIsOpen] = useState(false); + const [data, setData] = useState(""); useEffect(() => { const fetchData = async () => { - const result = await axios( - `https://justpasteitapi.herokuapp.com/add/${id}` - ); - - let text = xss(result.data['content'], options); - setData(text); - console.log(result.data['content']); // Debug log + try { + const paste = await getPaste(id); + setData(sanitizePasteContent(paste.content)); + } catch (error) { + toast.error("Error : Unable to load text"); + console.error(error); + } }; - + fetchData(); - }, [data, id]); + }, [id]); - - const notify = () =>{ + const notify = useCallback(() => { copy(data); toast("Text Copied Successfully 📋"); - } - + }, [data]); return (
-
-

{/*Data was sanitized with the XSS library*/} -
- -
-
- setIsOpen(false)}/> + + setIsOpen(true)} /> + setIsOpen(false)} />
- ); }; diff --git a/src/components/Typing/Typing.jsx b/src/components/Typing/Typing.jsx index c0d0797..5e1662e 100644 --- a/src/components/Typing/Typing.jsx +++ b/src/components/Typing/Typing.jsx @@ -1,40 +1,41 @@ -import React, {useState, useEffect} from "react"; +import React, { useEffect, useState } from "react"; import style from "./Typing.module.css"; -import hljs from 'highlight.js'; -import ReactQuill from 'react-quill'; +import hljs from "highlight.js"; +import ReactQuill from "react-quill"; import Button from "../Button/Button"; -import 'react-quill/dist/quill.snow.css'; -import 'highlight.js/styles/atom-one-light.css'; +import { editorModules } from "../../config/editorConfig"; +import "react-quill/dist/quill.snow.css"; +import "highlight.js/styles/atom-one-light.css"; const Typing = ({ handleInputChange, onSubmit }) => { - const [value, setValue] = useState(); + const [value, setValue] = useState(""); - useEffect(() => { - hljs.configure({useBR: false}); + useEffect(() => { + hljs.configure({ useBR: false }); }, []); - useEffect(() => { // Called on value change + useEffect(() => { handleInputChange(value); - console.log("input debug: " + value); }, [handleInputChange, value]); - return ( -
+ return ( +
- - +
-
+
); }; diff --git a/src/config/editorConfig.js b/src/config/editorConfig.js new file mode 100644 index 0000000..78d235d --- /dev/null +++ b/src/config/editorConfig.js @@ -0,0 +1,11 @@ +export const editorModules = { + syntax: { + setInterval: 10, + }, + toolbar: [ + ["bold", "italic", "underline", "blockquote"], + [{ list: "ordered" }, { list: "bullet" }], + ["link"], + ["code-block"], + ], +}; diff --git a/src/config/toastConfig.js b/src/config/toastConfig.js new file mode 100644 index 0000000..ab73a8f --- /dev/null +++ b/src/config/toastConfig.js @@ -0,0 +1,16 @@ +export const defaultToastConfig = { + position: "bottom-center", + autoClose: 1000, + hideProgressBar: true, + newestOnTop: false, + closeOnClick: true, + rtl: false, + pauseOnFocusLoss: true, + draggable: true, + pauseOnHover: true, +}; + +export const toastStyle = (paddingLeft) => ({ + color: "black", + paddingLeft, +}); diff --git a/src/services/pasteService.js b/src/services/pasteService.js new file mode 100644 index 0000000..ead40f9 --- /dev/null +++ b/src/services/pasteService.js @@ -0,0 +1,13 @@ +import axios from "axios"; + +const API_BASE_URL = "https://justpasteitapi.herokuapp.com"; + +export const createPaste = async (content) => { + const response = await axios.post(`${API_BASE_URL}/add`, { content }); + return response.data; +}; + +export const getPaste = async (id) => { + const response = await axios(`${API_BASE_URL}/add/${id}`); + return response.data; +}; diff --git a/src/utils/sanitizePasteContent.js b/src/utils/sanitizePasteContent.js new file mode 100644 index 0000000..abc87b0 --- /dev/null +++ b/src/utils/sanitizePasteContent.js @@ -0,0 +1,23 @@ +import xss from "xss"; + +export const EMPTY_EDITOR_CONTENT = "


"; + +const sanitizerOptions = { + whiteList: { + pre: ["class"], + span: ["class"], + p: [], + br: [], + strong: [], + em: [], + u: [], + ul: [], + li: [], + ol: [], + a: ["href", "rel", "target"], + }, +}; + +export const hasPasteContent = (content) => Boolean(content && content !== EMPTY_EDITOR_CONTENT); + +export const sanitizePasteContent = (content) => xss(content || "", sanitizerOptions); diff --git a/src/utils/shareUrl.js b/src/utils/shareUrl.js new file mode 100644 index 0000000..80701cd --- /dev/null +++ b/src/utils/shareUrl.js @@ -0,0 +1 @@ +export const getShareUrl = (id) => `${window.location.protocol}//${window.location.host}/${id}`;