From b269a5e6b32c545451b0bbade54ea5029f2c353a Mon Sep 17 00:00:00 2001 From: Antonio Date: Sat, 4 Mar 2023 21:28:44 +0100 Subject: [PATCH] feat: improve modal control --- components/Form.tsx | 12 ++++-- components/Input.tsx | 3 ++ components/Modal.tsx | 4 +- components/layout/Sidebar.tsx | 19 ++------- components/layout/SidebarItem.tsx | 16 +++++-- components/layout/SidebarTweetButton.tsx | 6 ++- components/modals/LoginModal.tsx | 52 ++++++++++++++++------- components/modals/RegisterModal.tsx | 53 +++++++++++++++++------- components/posts/PostItem.tsx | 9 ++-- components/profiles/ProfileBio.tsx | 17 +++----- hooks/useLoginModal.ts | 16 +++++++ hooks/useRegisterModal.ts | 16 +++++++ package-lock.json | 48 ++++++++++++++++++++- package.json | 3 +- pages/_app.tsx | 12 ++++-- pages/search.tsx | 11 +++++ 16 files changed, 224 insertions(+), 73 deletions(-) create mode 100644 hooks/useLoginModal.ts create mode 100644 hooks/useRegisterModal.ts create mode 100644 pages/search.tsx diff --git a/components/Form.tsx b/components/Form.tsx index 7e2ea38..f5f8e51 100644 --- a/components/Form.tsx +++ b/components/Form.tsx @@ -2,10 +2,11 @@ import { BiImage } from 'react-icons/bi'; import { HiOutlineGif } from 'react-icons/hi2'; import { IoLocationSharp } from 'react-icons/io5'; +import useLoginModal from '@/hooks/useLoginModal'; +import useRegisterModal from '@/hooks/useRegisterModal'; + import Avatar from './Avatar'; import Button from './Button'; -import LoginModal from './modals/LoginModal'; -import RegisterModal from './modals/RegisterModal'; const isLoggedIn = false; @@ -14,6 +15,9 @@ interface FormProps { } const Form: React.FC = ({ placeholder }) => { + const registerModal = useRegisterModal(); + const loginModal = useLoginModal(); + return (
{isLoggedIn ? ( @@ -67,8 +71,8 @@ const Form: React.FC = ({ placeholder }) => {

Welcome to Twitter

- - +
)} diff --git a/components/Input.tsx b/components/Input.tsx index be65421..60c3861 100644 --- a/components/Input.tsx +++ b/components/Input.tsx @@ -19,6 +19,9 @@ const Input: React.FC = ({ placeholder, value, type = "text" }) => { rounded-md outline-none text-white + focus:border-sky-500 + focus:border-2 + transition " /> ); diff --git a/components/Modal.tsx b/components/Modal.tsx index 1f7d734..2c7b1ff 100644 --- a/components/Modal.tsx +++ b/components/Modal.tsx @@ -8,10 +8,11 @@ interface ModalProps { onSubmit: () => void; title?: string; body?: React.ReactElement; + footer?: React.ReactElement; actionLabel: string; } -const Modal: React.FC = ({ isOpen, onClose, onSubmit, title, body, actionLabel }) => { +const Modal: React.FC = ({ isOpen, onClose, onSubmit, title, body, actionLabel, footer }) => { const handleClose = useCallback(() => { onClose(); }, [onClose]); @@ -92,6 +93,7 @@ const Modal: React.FC = ({ isOpen, onClose, onSubmit, title, body, a {/*footer*/}
diff --git a/components/layout/Sidebar.tsx b/components/layout/Sidebar.tsx index 80db545..76e22e2 100644 --- a/components/layout/Sidebar.tsx +++ b/components/layout/Sidebar.tsx @@ -34,32 +34,21 @@ const items = [ ] const Sidebar = () => { - const filteredItems = useMemo(() => { - return items.filter((item) => { - if (!isLoggedIn && item.auth) { - return null; - } - - return item; - }) - }, []); - return (
- {filteredItems.map((item) => ( - ( + ))} - {isLoggedIn && ( - - )} +
diff --git a/components/layout/SidebarItem.tsx b/components/layout/SidebarItem.tsx index e2d9160..9eb2536 100644 --- a/components/layout/SidebarItem.tsx +++ b/components/layout/SidebarItem.tsx @@ -1,19 +1,27 @@ -import { useRouter } from 'next/router'; import React, { useCallback } from 'react'; import { IconType } from "react-icons"; +import { useRouter } from 'next/router'; + +import useLoginModal from '@/hooks/useLoginModal'; interface SidebarItemProps { label: string; icon: IconType; href: string; + auth?: boolean; } -const SidebarItem: React.FC = ({ label, icon: Icon, href }) => { +const SidebarItem: React.FC = ({ label, icon: Icon, href, auth }) => { const router = useRouter(); + const loginModal = useLoginModal(); const onClick = useCallback(() => { - router.push(href); - }, [router, href]); + if (auth) { + loginModal.onOpen(); + } else { + router.push(href); + } + }, [router, href, auth, loginModal]); return (
diff --git a/components/layout/SidebarTweetButton.tsx b/components/layout/SidebarTweetButton.tsx index 0f67dfc..aff3895 100644 --- a/components/layout/SidebarTweetButton.tsx +++ b/components/layout/SidebarTweetButton.tsx @@ -1,8 +1,12 @@ import { FaFeather } from "react-icons/fa"; +import useLoginModal from "@/hooks/useLoginModal"; + const SidebarTweetButton = () => { + const loginModal = useLoginModal(); + return ( -
+
{ ) } +const ModalFooter = () => { + const loginModal = useLoginModal(); + const registerModal = useRegisterModal(); + + const onClick = useCallback(() => { + loginModal.onClose(); + registerModal.onOpen(); + }, [loginModal, registerModal]); + + return ( +
+

First time using Twitter? + Create an account +

+
+ ) +} + const LoginModal = () => { - const [showModal, setShowModal] = useState(false); + const loginModal = useLoginModal(); return ( - <> -
diff --git a/hooks/useLoginModal.ts b/hooks/useLoginModal.ts new file mode 100644 index 0000000..a45546c --- /dev/null +++ b/hooks/useLoginModal.ts @@ -0,0 +1,16 @@ +import { create } from 'zustand'; + +interface LoginModalStore { + isOpen: boolean; + onOpen: () => void; + onClose: () => void; +} + +const useLoginModal = create((set) => ({ + isOpen: false, + onOpen: () => set({ isOpen: true }), + onClose: () => set({ isOpen: false }) +})); + + +export default useLoginModal; diff --git a/hooks/useRegisterModal.ts b/hooks/useRegisterModal.ts new file mode 100644 index 0000000..d323968 --- /dev/null +++ b/hooks/useRegisterModal.ts @@ -0,0 +1,16 @@ +import { create } from 'zustand'; + +interface RegisterModalStore { + isOpen: boolean; + onOpen: () => void; + onClose: () => void; +} + +const useRegisterModal = create((set) => ({ + isOpen: false, + onOpen: () => set({ isOpen: true }), + onClose: () => set({ isOpen: false }) +})); + + +export default useRegisterModal; diff --git a/package-lock.json b/package-lock.json index dbb5612..079327a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^4.7.1", - "typescript": "4.9.5" + "typescript": "4.9.5", + "zustand": "^4.3.5" }, "devDependencies": { "autoprefixer": "^10.4.13", @@ -3793,6 +3794,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3907,6 +3916,29 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.5.tgz", + "integrity": "sha512-2iPUzfwx+g3f0PagOMz2vDO9mZzEp2puFpNe7vrAymVPOEIEUjCPkC4/zy84eAscxIWmTU4j9g6upXYkJdzEFQ==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } }, "dependencies": { @@ -6442,6 +6474,12 @@ "punycode": "^2.1.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6523,6 +6561,14 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zustand": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.5.tgz", + "integrity": "sha512-2iPUzfwx+g3f0PagOMz2vDO9mZzEp2puFpNe7vrAymVPOEIEUjCPkC4/zy84eAscxIWmTU4j9g6upXYkJdzEFQ==", + "requires": { + "use-sync-external-store": "1.2.0" + } } } } diff --git a/package.json b/package.json index f5a6a76..e8d5fd5 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^4.7.1", - "typescript": "4.9.5" + "typescript": "4.9.5", + "zustand": "^4.3.5" }, "devDependencies": { "autoprefixer": "^10.4.13", diff --git a/pages/_app.tsx b/pages/_app.tsx index 7a3c801..a5a5425 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,12 +1,18 @@ import type { AppProps } from 'next/app' import Layout from '@/components/Layout' +import LoginModal from '@/components/modals/LoginModal' +import RegisterModal from '@/components/modals/RegisterModal' import '@/styles/globals.css' export default function App({ Component, pageProps }: AppProps) { return ( - - - + <> + + + + + + ) } diff --git a/pages/search.tsx b/pages/search.tsx new file mode 100644 index 0000000..fc62ebc --- /dev/null +++ b/pages/search.tsx @@ -0,0 +1,11 @@ +import Header from "@/components/Header"; + +const Search = () => { + return ( + <> +
+ + ); +} + +export default Search; \ No newline at end of file