From 4dcbc57f0cc8e798b89a7f43bfc3c3a7d88d0402 Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Tue, 14 Mar 2023 11:28:27 -0600 Subject: [PATCH 01/16] stashing slider work --- pages/_app.tsx | 2 + src/components/core/SliderManager.tsx | 63 +++++++++++++++++++ src/components/request/RequestListForm.tsx | 71 ++++++++-------------- src/hooks/stores/useRequestStore.ts | 15 +++++ src/hooks/stores/useSliderManagerStore.ts | 41 +++++++++++++ 5 files changed, 148 insertions(+), 44 deletions(-) create mode 100644 src/components/core/SliderManager.tsx create mode 100644 src/hooks/stores/useRequestStore.ts create mode 100644 src/hooks/stores/useSliderManagerStore.ts diff --git a/pages/_app.tsx b/pages/_app.tsx index 6f852943..fc87672c 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -15,6 +15,7 @@ import trackerInit, { initializeUser, trackEvent } from "lib/utils/amplitude" import { useRouter } from "next/router" import Script from "next/script" import AppLayout from "../src/components/core/AppLayout" +import SliderManager from "../src/components/core/SliderManager" import { useIsRouterLoading } from "../src/hooks/useIsRouterLoading" const queryClient = new QueryClient({ @@ -182,6 +183,7 @@ function App({ Component, pageProps }: AppProps) { ) : ( + ))} diff --git a/src/components/core/SliderManager.tsx b/src/components/core/SliderManager.tsx new file mode 100644 index 00000000..8735858d --- /dev/null +++ b/src/components/core/SliderManager.tsx @@ -0,0 +1,63 @@ +import RightSlider from "@ui/RightSlider" +import { useRouter } from "next/router" +import { useEffect } from "react" +import { useRequestStore } from "../../hooks/stores/useRequestStore" +import { + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" +import { + addQueryParam, + removeQueryParam, +} from "../../lib/utils/updateQueryParam" +import RequestDetailsContent from "../pages/requestDetails/components/RequestDetailsContent" + +const SliderManager = () => { + const router = useRouter() + const activeSlider = useSliderManagerStore((state) => state.activeSlider) + const openSlider = useSliderManagerStore((state) => state.openSlider) + const closeSlider = useSliderManagerStore((state) => state.closeSlider) + const sliderOpen = useSliderManagerStore((state) => state.sliderOpen) + const selectedRequest = useRequestStore((state) => state.selectedRequest) + + const sliderContent = () => { + switch (activeSlider?.key) { + case Sliders.REQUEST_DETAILS: + if (selectedRequest) { + return ( + {}} + /> + ) + } + + default: + return <> + } + } + + useEffect(() => { + if (sliderOpen) { + addQueryParam(router, activeSlider?.queryParam as string, "true") + } else { + removeQueryParam(router, activeSlider?.queryParam as string) + } + }, [sliderOpen]) + + // defaults for if the query param is already loaded up + // but I don't think this will work because the requestId won't be loaded up? + useEffect(() => { + if ("requestDetails" in router.query) { + openSlider() + } + }, [router.query]) + + return ( + closeSlider()}> + {sliderContent()} + + ) +} + +export default SliderManager diff --git a/src/components/request/RequestListForm.tsx b/src/components/request/RequestListForm.tsx index 36854c2b..4df59df3 100644 --- a/src/components/request/RequestListForm.tsx +++ b/src/components/request/RequestListForm.tsx @@ -6,15 +6,16 @@ import { EmptyState } from "components/emptyStates/EmptyState" import { NuxEmptyState } from "components/emptyStates/NuxEmptyState" import dynamic from "next/dynamic" import { useRouter } from "next/router" -import React, { useEffect, useReducer, useState } from "react" +import React, { useReducer, useState } from "react" import { KeyedMutator } from "swr" import { usePermissionsStore } from "../../hooks/stores/usePermissionsStore" +import { useRequestStore } from "../../hooks/stores/useRequestStore" +import { + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" import useStore from "../../hooks/stores/useStore" import { listIntersection } from "../../lib/utils/listIntersection" -import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" import { Action } from "../../models/action/types" import { RequestFrob } from "../../models/request/types" import { BatchStatusBar } from "../core/BatchStatusBar" @@ -115,12 +116,12 @@ const RequestListForm = ({ RequestFrob | undefined >(undefined) - const [detailsSliderOpen, setDetailsSliderOpen] = useState(false) - const closeDetailsSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "requestId") - } - } + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) + const setSelectedRequest = useRequestStore( + (state) => state.setSelectedRequest, + ) const [batchState, dispatch] = useReducer(batchReducer, initialBatchState) const [isVotingApproval, setIsVotingApproval] = useState(false) const [isExecutingApproval, setIsExecutingApproval] = useState(false) @@ -212,20 +213,20 @@ const RequestListForm = ({ (state) => state.setShowTabBottomBorder, ) - useEffect(() => { - if (router.query.requestId) { - const selectedRequest = requests.find( - (r) => r.id === router.query.requestId, - ) + // useEffect(() => { + // if (router.query.requestId) { + // const selectedRequest = requests.find( + // (r) => r.id === router.query.requestId, + // ) - if (selectedRequest) { - setRequestForDetails(selectedRequest) - setDetailsSliderOpen(true) - } - } else { - setDetailsSliderOpen(false) - } - }, [router.query]) + // if (selectedRequest) { + // setRequestForDetails(selectedRequest) + // setDetailsSliderOpen(true) + // } + // } else { + // setDetailsSliderOpen(false) + // } + // }, [router.query]) if (requests.length === 0) { setShowTabBottomBorder(false) @@ -286,23 +287,6 @@ const RequestListForm = ({ return ( <> - {requestForDetails && ( - - { - // set state used by Request in slider - setRequestForDetails(args.payload) - mutateRequest(args) - }} - /> - - )} -
{(isMobile: boolean) => @@ -355,9 +339,8 @@ const RequestListForm = ({ request={request} mutateRequest={mutateRequest} triggerDetails={(request) => { - addQueryParam(router, "requestId", request.id) - setRequestForDetails(request) - setDetailsSliderOpen(true) + setSelectedRequest(request) + setActiveSlider(Sliders.REQUEST_DETAILS) }} onCheckboxChange={onCheckboxChange} checked={batchState.selectedRequests.includes(request)} diff --git a/src/hooks/stores/useRequestStore.ts b/src/hooks/stores/useRequestStore.ts new file mode 100644 index 00000000..0d7d0587 --- /dev/null +++ b/src/hooks/stores/useRequestStore.ts @@ -0,0 +1,15 @@ +import { create } from "zustand" +import { RequestFrob } from "../../models/request/types" + +interface RequestState { + selectedRequest: RequestFrob | undefined | null + setSelectedRequest: (request: RequestFrob | undefined | null) => void +} + +export const useRequestStore = create((set) => ({ + selectedRequest: undefined, // undefined on start + setSelectedRequest: (request: RequestFrob | undefined | null) => + set(() => { + return { selectedRequest: request } + }), +})) diff --git a/src/hooks/stores/useSliderManagerStore.ts b/src/hooks/stores/useSliderManagerStore.ts new file mode 100644 index 00000000..7d0c7b3a --- /dev/null +++ b/src/hooks/stores/useSliderManagerStore.ts @@ -0,0 +1,41 @@ +import { create } from "zustand" + +export enum Sliders { + "REQUEST_DETAILS" = "REQUEST_DETAILS", +} + +type SliderConfig = { + key: Sliders + queryParam: string +} +const sliderOptions: Record = { + REQUEST_DETAILS: { + key: Sliders.REQUEST_DETAILS, + queryParam: "requestDetails", + }, +} + +interface SliderManagerState { + sliderOpen: boolean + activeSlider: SliderConfig | undefined | null + setActiveSlider: (slider: Sliders) => void + closeSlider: () => void + openSlider: () => void +} + +export const useSliderManagerStore = create((set) => ({ + sliderOpen: false, + activeSlider: undefined, // undefined on start + setActiveSlider: (slider: Sliders) => + set(() => { + return { sliderOpen: true, activeSlider: sliderOptions[slider] } + }), + closeSlider: () => + set(() => { + return { sliderOpen: false } + }), + openSlider: () => + set(() => { + return { sliderOpen: true } + }), +})) From 8934d00611aba78c59f722436d1fb9cefa66f966 Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Tue, 21 Mar 2023 18:25:26 -0600 Subject: [PATCH 02/16] updating query param util so it doesn't rely on next router --- src/hooks/stores/useSliderManagerStore.ts | 46 ++++++++++++++++++++++- src/lib/utils/updateQueryParam.ts | 16 ++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/hooks/stores/useSliderManagerStore.ts b/src/hooks/stores/useSliderManagerStore.ts index 7d0c7b3a..15735f79 100644 --- a/src/hooks/stores/useSliderManagerStore.ts +++ b/src/hooks/stores/useSliderManagerStore.ts @@ -2,16 +2,56 @@ import { create } from "zustand" export enum Sliders { "REQUEST_DETAILS" = "REQUEST_DETAILS", + "SEND_TOKENS" = "SEND_TOKENS", + "REQUEST_TOKENS" = "REQUEST_TOKENS", + "EDIT_MEMBERS" = "EDIT_MEMBERS", + "CREATE_AUTOMATION" = "CREATE_AUTOMATION", + "AUTOMATION_DETAILS" = "AUTOMATION_DETAILS", +} + +enum QueryValueTypes { + "ID" = "ID", + "BOOLEAN" = "BOOLEAN", } type SliderConfig = { key: Sliders queryParam: string + queryValueType: QueryValueTypes } + +// should these have a "bootup" script that will run on initial page load? +// thinking for cases where the user has a slider open and refreshes the page const sliderOptions: Record = { REQUEST_DETAILS: { key: Sliders.REQUEST_DETAILS, - queryParam: "requestDetails", + queryParam: "requestId", + queryValueType: QueryValueTypes.ID, + }, + SEND_TOKENS: { + key: Sliders.SEND_TOKENS, + queryParam: "sendTokensOpen", + queryValueType: QueryValueTypes.BOOLEAN, + }, + REQUEST_TOKENS: { + key: Sliders.REQUEST_TOKENS, + queryParam: "requestTokensOpen", + queryValueType: QueryValueTypes.BOOLEAN, + }, + EDIT_MEMBERS: { + key: Sliders.EDIT_MEMBERS, + queryParam: "editMembersOpen", + queryValueType: QueryValueTypes.BOOLEAN, + }, + CREATE_AUTOMATION: { + key: Sliders.CREATE_AUTOMATION, + queryParam: "createAutomationOpen", + queryValueType: QueryValueTypes.BOOLEAN, + }, + AUTOMATION_DETAILS: { + key: Sliders.AUTOMATION_DETAILS, + queryParam: "automationId", + queryValueType: QueryValueTypes.ID, }, } @@ -28,6 +68,10 @@ export const useSliderManagerStore = create((set) => ({ activeSlider: undefined, // undefined on start setActiveSlider: (slider: Sliders) => set(() => { + // maybe part of this is setting the query param, and the useEffect hook + // in the slider manager is what actually opens the slider + // not sure if this will work with our existing url params though, since it relies on useRouter hook + // and this is not a component that can consume hooks return { sliderOpen: true, activeSlider: sliderOptions[slider] } }), closeSlider: () => diff --git a/src/lib/utils/updateQueryParam.ts b/src/lib/utils/updateQueryParam.ts index 30e7b58e..50358c0d 100644 --- a/src/lib/utils/updateQueryParam.ts +++ b/src/lib/utils/updateQueryParam.ts @@ -30,3 +30,19 @@ export const removeQueryParam = (router: NextRouter, paramName: string) => { { shallow: true }, ) } + +function setQueryParam(key: string, value: string) { + const url = new URL(window.location.href) + url.searchParams.set(key, value) + + // Update the browser's history without refreshing the page + window.history.replaceState({}, "", url.toString()) +} + +function removeAQueryParam(key: string) { + const url = new URL(window.location.href) + url.searchParams.delete(key) + + // Update the browser's history without refreshing the page + window.history.replaceState({}, "", url.toString()) +} From 0f98183df294e59182c1be141fea109d5c159d2c Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Tue, 21 Mar 2023 20:10:23 -0600 Subject: [PATCH 03/16] progress on a shared slider manager --- src/components/core/SliderManager.tsx | 98 +++++++++++++------ src/components/core/TerminalActionBar.tsx | 79 ++------------- .../pages/requestDetails/Desktop.tsx | 7 +- .../pages/requestDetails/Mobile.tsx | 5 +- .../components/RequestDetailsContent.tsx | 19 +++- .../request/CreateRequestDropdown.tsx | 78 ++------------- .../request/ProfileRequestsList.tsx | 1 - src/components/request/RequestListForm.tsx | 28 +----- src/hooks/stores/useRequestStore.ts | 11 +-- src/hooks/stores/useSliderManagerStore.ts | 39 +++++--- src/lib/utils/updateQueryParam.ts | 14 ++- 11 files changed, 152 insertions(+), 227 deletions(-) diff --git a/src/components/core/SliderManager.tsx b/src/components/core/SliderManager.tsx index 8735858d..e85a9190 100644 --- a/src/components/core/SliderManager.tsx +++ b/src/components/core/SliderManager.tsx @@ -1,36 +1,82 @@ import RightSlider from "@ui/RightSlider" +import { isQueryParamSet } from "lib/utils/updateQueryParam" +import { convertGlobalId } from "models/terminal/utils" +import dynamic from "next/dynamic" import { useRouter } from "next/router" import { useEffect } from "react" -import { useRequestStore } from "../../hooks/stores/useRequestStore" +import { useSWRConfig } from "swr" +import { useToast } from "../../hooks/useToast" + import { Sliders, useSliderManagerStore, } from "../../hooks/stores/useSliderManagerStore" -import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" import RequestDetailsContent from "../pages/requestDetails/components/RequestDetailsContent" +const RequestTokensContent = dynamic(() => + import("../pages/requestTokens/components/RequestTokensContent").then( + (mod) => mod.RequestTokensContent, + ), +) + +const SendTokensContent = dynamic(() => + import("../pages/sendTokens/components/SendTokensContent").then( + (mod) => mod.SendTokensContent, + ), +) + +const EditMembersContent = dynamic(() => + import("../pages/editMembers/components/EditMembersContent").then( + (mod) => mod.EditMembersContent, + ), +) + +const NewAutomationContent = dynamic(() => + import("../pages/newAutomation/components/NewAutomationContent").then( + (mod) => mod.NewAutomationContent, + ), +) + const SliderManager = () => { const router = useRouter() + const { mutate } = useSWRConfig() + const { successToast } = useToast() + const { address, chainId } = convertGlobalId( + router.query.chainNameAndSafeAddress as string, + ) const activeSlider = useSliderManagerStore((state) => state.activeSlider) - const openSlider = useSliderManagerStore((state) => state.openSlider) + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const closeSlider = useSliderManagerStore((state) => state.closeSlider) const sliderOpen = useSliderManagerStore((state) => state.sliderOpen) - const selectedRequest = useRequestStore((state) => state.selectedRequest) const sliderContent = () => { switch (activeSlider?.key) { case Sliders.REQUEST_DETAILS: - if (selectedRequest) { - return ( - {}} - /> - ) - } + return {}} /> + + case Sliders.REQUEST_TOKENS: + return + + case Sliders.EDIT_MEMBERS: + return + + case Sliders.CREATE_AUTOMATION: + return + + case Sliders.SEND_TOKENS: + return ( + { + successToast({ + message: "Created request", + }) + const key = `/api/v1/requests?safeChainId=${chainId}&safeAddress=${address}&tab=all` + mutate(key) + }} + /> + ) default: return <> @@ -38,20 +84,16 @@ const SliderManager = () => { } useEffect(() => { - if (sliderOpen) { - addQueryParam(router, activeSlider?.queryParam as string, "true") - } else { - removeQueryParam(router, activeSlider?.queryParam as string) - } - }, [sliderOpen]) - - // defaults for if the query param is already loaded up - // but I don't think this will work because the requestId won't be loaded up? - useEffect(() => { - if ("requestDetails" in router.query) { - openSlider() + if (isQueryParamSet("requestId")) { + setActiveSlider(Sliders.REQUEST_DETAILS) + } else if (isQueryParamSet("requestTokensOpen")) { + setActiveSlider(Sliders.REQUEST_TOKENS) + } else if (isQueryParamSet("editMembersOpen")) { + setActiveSlider(Sliders.EDIT_MEMBERS) + } else if (isQueryParamSet("createAutomationOpen")) { + setActiveSlider(Sliders.CREATE_AUTOMATION) } - }, [router.query]) + }, []) return ( closeSlider()}> diff --git a/src/components/core/TerminalActionBar.tsx b/src/components/core/TerminalActionBar.tsx index e5d7ae99..85719a00 100644 --- a/src/components/core/TerminalActionBar.tsx +++ b/src/components/core/TerminalActionBar.tsx @@ -1,30 +1,17 @@ import { CogIcon, PlusIcon } from "@heroicons/react/24/solid" import Breakpoint from "@ui/Breakpoint" import QRCode from "components/core/QrCode" -import { addQueryParam, removeQueryParam } from "lib/utils/updateQueryParam" import { convertGlobalId } from "models/terminal/utils" import dynamic from "next/dynamic" import Link from "next/link" import { useRouter } from "next/router" -import React, { useEffect, useState } from "react" -import { useSWRConfig } from "swr" -import { useToast } from "../../hooks/useToast" +import React, { useState } from "react" +import { + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" import { ArrowUpRight } from "../icons" -const RightSlider = dynamic(() => - import("../ui/RightSlider").then((mod) => mod.RightSlider), -) -const SendTokensContent = dynamic(() => - import("../pages/sendTokens/components/SendTokensContent").then( - (mod) => mod.SendTokensContent, - ), -) - -const NewAutomationContent = dynamic(() => - import("../pages/newAutomation/components/NewAutomationContent").then( - (mod) => mod.NewAutomationContent, - ), -) const BottomDrawer = dynamic(() => import("../ui/BottomDrawer").then((mod) => mod.BottomDrawer), ) @@ -34,64 +21,18 @@ const CopyAddressButton = dynamic(() => ) const TerminalActionBar = () => { - const { mutate } = useSWRConfig() const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const { address, chainId } = convertGlobalId( router.query.chainNameAndSafeAddress as string, ) - const { successToast } = useToast() - const [sendTokenSliderOpen, setSendTokenSliderOpen] = useState(false) - - const closeSendTokenSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "sendTokenSliderOpen") - } - } - - const [newAutomationSliderOpen, setNewAutomationSliderOpen] = - useState(false) - - const closeNewAutomationSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "automationSliderOpen") - } - } const [qrCodeOpen, setQrCodeOpen] = useState(false) - useEffect(() => { - if (router.query.sendTokenSliderOpen) { - setSendTokenSliderOpen(true) - setNewAutomationSliderOpen(false) - } else if (router.query.automationSliderOpen) { - setNewAutomationSliderOpen(true) - setSendTokenSliderOpen(false) - } else { - setSendTokenSliderOpen(false) - setNewAutomationSliderOpen(false) - } - }, [router.query]) - return ( <> - - { - successToast({ - message: "Created request", - }) - const key = `/api/v1/requests?safeChainId=${chainId}&safeAddress=${address}&tab=all` - mutate(key) - }} - /> - - - - - {(isMobile) => { if (isMobile) { @@ -159,7 +100,7 @@ const TerminalActionBar = () => {
{ - addQueryParam(router, "sendTokenSliderOpen", "true") + setActiveSlider(Sliders.SEND_TOKENS, { value: true }) }} >
@@ -192,7 +133,7 @@ const TerminalActionBar = () => {
{ - addQueryParam(router, "automationSliderOpen", "true") + setActiveSlider(Sliders.CREATE_AUTOMATION, { value: true }) }} >
diff --git a/src/components/pages/requestDetails/Desktop.tsx b/src/components/pages/requestDetails/Desktop.tsx index cd023f52..93bd02cc 100644 --- a/src/components/pages/requestDetails/Desktop.tsx +++ b/src/components/pages/requestDetails/Desktop.tsx @@ -5,7 +5,7 @@ import RequestDetailsContent from "./components/RequestDetailsContent" const RequestDetailsDesktop = () => { const router = useRouter() - const { request, mutate } = useRequest(router.query.requestId as string) + const { mutate } = useRequest(router.query.requestId as string) const mutateRequest = ({ fn, @@ -23,12 +23,9 @@ const RequestDetailsDesktop = () => { }) } - if (!request) { - return <> - } return (
- +
) } diff --git a/src/components/pages/requestDetails/Mobile.tsx b/src/components/pages/requestDetails/Mobile.tsx index fd9a098b..a4cdc6ba 100644 --- a/src/components/pages/requestDetails/Mobile.tsx +++ b/src/components/pages/requestDetails/Mobile.tsx @@ -45,10 +45,7 @@ const RequestDetailsMobile = () => { {/* empty span to keep number centered */}
- +
) diff --git a/src/components/pages/requestDetails/components/RequestDetailsContent.tsx b/src/components/pages/requestDetails/components/RequestDetailsContent.tsx index 5093c2eb..ef1ed52f 100644 --- a/src/components/pages/requestDetails/components/RequestDetailsContent.tsx +++ b/src/components/pages/requestDetails/components/RequestDetailsContent.tsx @@ -1,23 +1,23 @@ import { RequestVariantType } from "@prisma/client" import { WaitRequestExecution } from "components/request/WaitRequestExecution" import { timeSince } from "lib/utils" +import { getQueryParam } from "lib/utils/updateQueryParam" import { ActivityMetadata } from "models/activity/types" +import { useRouter } from "next/router" import { useAccount } from "wagmi" import { NewCommentForm } from "../../../../components/comment/NewCommentForm" import ActivityItem from "../../../../components/core/ActivityItem" import { AvatarAddress } from "../../../../components/core/AvatarAddress" import { SignerQuorumRequestContent } from "../../../../components/request/SignerQuorumRequestContent" import { TokenTransferRequestContent } from "../../../../components/request/TokenTransferRequestContent" -import { RequestFrob } from "../../../../models/request/types" +import { useRequest } from "../../../../models/request/hooks" import { isExecuted } from "../../../../models/request/utils" import { RequestDetailsActions } from "../../../request/RequestDetailsActions" import { RequestStatusIcon } from "../../../request/RequestStatusIcon" export const RequestDetailsContent = ({ - request, mutateRequest, }: { - request: RequestFrob mutateRequest: ({ fn, requestId, @@ -28,7 +28,20 @@ export const RequestDetailsContent = ({ payload: any }) => void }) => { + const router = useRouter() + let { requestId } = router.query + + // if the query param is set "shallowly" next router doesn't pick up on it + // this happens on desktop, if the user clicks a request from the list + // we can still grab it from the url manually + if (!requestId) { + requestId = getQueryParam("requestId") as string + } + + console.log(requestId) + const { address } = useAccount() + const { request } = useRequest(requestId as string) if (!request) { return <> diff --git a/src/components/request/CreateRequestDropdown.tsx b/src/components/request/CreateRequestDropdown.tsx index 22d85be2..32a1a432 100644 --- a/src/components/request/CreateRequestDropdown.tsx +++ b/src/components/request/CreateRequestDropdown.tsx @@ -6,81 +6,21 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@ui/Dropdown" -import dynamic from "next/dynamic" import Link from "next/link" import { useRouter } from "next/router" -import { useEffect, useState } from "react" import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" - -const RightSlider = dynamic(() => - import("../ui/RightSlider").then((mod) => mod.RightSlider), -) - -const RequestTokensContent = dynamic(() => - import("../pages/requestTokens/components/RequestTokensContent").then( - (mod) => mod.RequestTokensContent, - ), -) -const EditMembersContent = dynamic(() => - import("../pages/editMembers/components/EditMembersContent").then( - (mod) => mod.EditMembersContent, - ), -) + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" export const CreateRequestDropdown = () => { const router = useRouter() - const [requestTokensSliderOpen, setRequestTokensSliderOpen] = - useState(false) - - const closeRequestTokensSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "requestTokenSliderOpen") - } - } - - const [editMembersSliderOpen, setEditMembersSliderOpen] = - useState(false) - - const closeEditMembersSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "editMembersSliderOpen") - } - } - - useEffect(() => { - if (router.query.sendTokenSliderOpen) { - // adding the query param will trigger the send tokens drawer from the terminal action bar - setRequestTokensSliderOpen(false) - setEditMembersSliderOpen(false) - } else if (router.query.requestTokenSliderOpen) { - setRequestTokensSliderOpen(true) - setEditMembersSliderOpen(false) - } else if (router.query.editMembersSliderOpen) { - setEditMembersSliderOpen(true) - setRequestTokensSliderOpen(false) - } else { - setEditMembersSliderOpen(false) - setRequestTokensSliderOpen(false) - } - }, [router.query]) + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) return ( <> - - - - - - {/* Copy same styles as primary, small button because annoying console.error if we use Button component */} @@ -112,7 +52,7 @@ export const CreateRequestDropdown = () => { return ( { - addQueryParam(router, "sendTokenSliderOpen", "true") + setActiveSlider(Sliders.SEND_TOKENS, { value: true }) }} > Send tokens @@ -136,7 +76,7 @@ export const CreateRequestDropdown = () => { return ( { - addQueryParam(router, "requestTokenSliderOpen", "true") + setActiveSlider(Sliders.REQUEST_TOKENS, { value: true }) }} > Request tokens @@ -160,7 +100,7 @@ export const CreateRequestDropdown = () => { return ( { - addQueryParam(router, "editMembersSliderOpen", "true") + setActiveSlider(Sliders.EDIT_MEMBERS, { value: true }) }} > Edit Members diff --git a/src/components/request/ProfileRequestsList.tsx b/src/components/request/ProfileRequestsList.tsx index fc40d54c..42d50595 100644 --- a/src/components/request/ProfileRequestsList.tsx +++ b/src/components/request/ProfileRequestsList.tsx @@ -91,7 +91,6 @@ export const ProfileRequestsList = ({ address }: { address: string }) => { useInnerPadding={false} > { // set state used by Request in slider setRequestForDetails(args.payload) diff --git a/src/components/request/RequestListForm.tsx b/src/components/request/RequestListForm.tsx index 4df59df3..70753b52 100644 --- a/src/components/request/RequestListForm.tsx +++ b/src/components/request/RequestListForm.tsx @@ -9,7 +9,6 @@ import { useRouter } from "next/router" import React, { useReducer, useState } from "react" import { KeyedMutator } from "swr" import { usePermissionsStore } from "../../hooks/stores/usePermissionsStore" -import { useRequestStore } from "../../hooks/stores/useRequestStore" import { Sliders, useSliderManagerStore, @@ -112,16 +111,9 @@ const RequestListForm = ({ }) } - const [requestForDetails, setRequestForDetails] = useState< - RequestFrob | undefined - >(undefined) - const setActiveSlider = useSliderManagerStore( (state) => state.setActiveSlider, ) - const setSelectedRequest = useRequestStore( - (state) => state.setSelectedRequest, - ) const [batchState, dispatch] = useReducer(batchReducer, initialBatchState) const [isVotingApproval, setIsVotingApproval] = useState(false) const [isExecutingApproval, setIsExecutingApproval] = useState(false) @@ -213,21 +205,6 @@ const RequestListForm = ({ (state) => state.setShowTabBottomBorder, ) - // useEffect(() => { - // if (router.query.requestId) { - // const selectedRequest = requests.find( - // (r) => r.id === router.query.requestId, - // ) - - // if (selectedRequest) { - // setRequestForDetails(selectedRequest) - // setDetailsSliderOpen(true) - // } - // } else { - // setDetailsSliderOpen(false) - // } - // }, [router.query]) - if (requests.length === 0) { setShowTabBottomBorder(false) @@ -339,8 +316,9 @@ const RequestListForm = ({ request={request} mutateRequest={mutateRequest} triggerDetails={(request) => { - setSelectedRequest(request) - setActiveSlider(Sliders.REQUEST_DETAILS) + setActiveSlider(Sliders.REQUEST_DETAILS, { + id: request.id, + }) }} onCheckboxChange={onCheckboxChange} checked={batchState.selectedRequests.includes(request)} diff --git a/src/hooks/stores/useRequestStore.ts b/src/hooks/stores/useRequestStore.ts index 0d7d0587..ea11223a 100644 --- a/src/hooks/stores/useRequestStore.ts +++ b/src/hooks/stores/useRequestStore.ts @@ -1,15 +1,14 @@ import { create } from "zustand" -import { RequestFrob } from "../../models/request/types" interface RequestState { - selectedRequest: RequestFrob | undefined | null - setSelectedRequest: (request: RequestFrob | undefined | null) => void + selectedRequestId: string | undefined | null + setSelectedRequestId: (request: string | undefined | null) => void } export const useRequestStore = create((set) => ({ - selectedRequest: undefined, // undefined on start - setSelectedRequest: (request: RequestFrob | undefined | null) => + selectedRequestId: undefined, // undefined on start + setSelectedRequestId: (request: string | undefined | null) => set(() => { - return { selectedRequest: request } + return { selectedRequestId: request } }), })) diff --git a/src/hooks/stores/useSliderManagerStore.ts b/src/hooks/stores/useSliderManagerStore.ts index 15735f79..31595f8b 100644 --- a/src/hooks/stores/useSliderManagerStore.ts +++ b/src/hooks/stores/useSliderManagerStore.ts @@ -1,3 +1,4 @@ +import { deleteQueryParam, setQueryParam } from "lib/utils/updateQueryParam" import { create } from "zustand" export enum Sliders { @@ -17,7 +18,6 @@ enum QueryValueTypes { type SliderConfig = { key: Sliders queryParam: string - queryValueType: QueryValueTypes } // should these have a "bootup" script that will run on initial page load? @@ -26,56 +26,65 @@ const sliderOptions: Record = { REQUEST_DETAILS: { key: Sliders.REQUEST_DETAILS, queryParam: "requestId", - queryValueType: QueryValueTypes.ID, }, SEND_TOKENS: { key: Sliders.SEND_TOKENS, queryParam: "sendTokensOpen", - queryValueType: QueryValueTypes.BOOLEAN, }, REQUEST_TOKENS: { key: Sliders.REQUEST_TOKENS, queryParam: "requestTokensOpen", - queryValueType: QueryValueTypes.BOOLEAN, }, EDIT_MEMBERS: { key: Sliders.EDIT_MEMBERS, queryParam: "editMembersOpen", - queryValueType: QueryValueTypes.BOOLEAN, }, CREATE_AUTOMATION: { key: Sliders.CREATE_AUTOMATION, queryParam: "createAutomationOpen", - queryValueType: QueryValueTypes.BOOLEAN, }, AUTOMATION_DETAILS: { key: Sliders.AUTOMATION_DETAILS, queryParam: "automationId", - queryValueType: QueryValueTypes.ID, }, } interface SliderManagerState { sliderOpen: boolean activeSlider: SliderConfig | undefined | null - setActiveSlider: (slider: Sliders) => void + setActiveSlider: (slider: Sliders, opts?: any) => void closeSlider: () => void openSlider: () => void } -export const useSliderManagerStore = create((set) => ({ +export const useSliderManagerStore = create((set, get) => ({ sliderOpen: false, activeSlider: undefined, // undefined on start - setActiveSlider: (slider: Sliders) => + // maybe all this does is set query params + // then the manager picks up on the param change + // and calls open which sets the active open slider? + setActiveSlider: (slider: Sliders, opts?: any) => set(() => { - // maybe part of this is setting the query param, and the useEffect hook - // in the slider manager is what actually opens the slider - // not sure if this will work with our existing url params though, since it relies on useRouter hook - // and this is not a component that can consume hooks - return { sliderOpen: true, activeSlider: sliderOptions[slider] } + const activeSlider = sliderOptions[slider] + // maybe it could be cool to split up the "setup" and "open" functions + // "setup" would set query params and "open" would just set the sliderOpen state + // this would allow us to simply open the slider without setting query params + // for example, when the page is already loaded with query params set + if (opts) { + if (opts.id) { + setQueryParam(activeSlider.queryParam, opts.id) + } else { + setQueryParam(activeSlider.queryParam, "true") + } + } + return { sliderOpen: true, activeSlider } }), closeSlider: () => set(() => { + const activeSlider = get().activeSlider + if (activeSlider) { + deleteQueryParam(activeSlider.queryParam) + } return { sliderOpen: false } }), openSlider: () => diff --git a/src/lib/utils/updateQueryParam.ts b/src/lib/utils/updateQueryParam.ts index 50358c0d..eb06248f 100644 --- a/src/lib/utils/updateQueryParam.ts +++ b/src/lib/utils/updateQueryParam.ts @@ -31,7 +31,7 @@ export const removeQueryParam = (router: NextRouter, paramName: string) => { ) } -function setQueryParam(key: string, value: string) { +export function setQueryParam(key: string, value: string) { const url = new URL(window.location.href) url.searchParams.set(key, value) @@ -39,10 +39,20 @@ function setQueryParam(key: string, value: string) { window.history.replaceState({}, "", url.toString()) } -function removeAQueryParam(key: string) { +export function deleteQueryParam(key: string) { const url = new URL(window.location.href) url.searchParams.delete(key) // Update the browser's history without refreshing the page window.history.replaceState({}, "", url.toString()) } + +export function getQueryParam(key: string) { + const url = new URL(window.location.href) + return url.searchParams.get(key) +} + +export function isQueryParamSet(key: string) { + const url = new URL(window.location.href) + return url.searchParams.has(key) +} From ac517d0999d2c9e56d2c2b36d103e7dcf7b00a5f Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Sat, 25 Mar 2023 20:34:01 -0600 Subject: [PATCH 04/16] moving all sliders to the slider manager --- .../automation/CreateAutomationDropdown.tsx | 126 ++++----- src/components/claim/ProfileReadyToClaim.tsx | 74 ++---- src/components/claim/TerminalReadyToClaim.tsx | 69 ++--- .../core/AccountNavBar/AccountDropdown.tsx | 58 +--- src/components/core/SliderManager.tsx | 48 +++- .../components/AutomationsPageContent.tsx | 51 +--- .../members/components/MembersPageContent.tsx | 251 ++++++++---------- .../pages/terminalDetails/Desktop.tsx | 40 +-- .../request/ProfileRequestsList.tsx | 188 +++++-------- src/hooks/stores/useSliderManagerStore.ts | 25 +- 10 files changed, 372 insertions(+), 558 deletions(-) diff --git a/src/components/automation/CreateAutomationDropdown.tsx b/src/components/automation/CreateAutomationDropdown.tsx index c01499e9..2bdd7008 100644 --- a/src/components/automation/CreateAutomationDropdown.tsx +++ b/src/components/automation/CreateAutomationDropdown.tsx @@ -6,97 +6,61 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@ui/Dropdown" -import { addQueryParam, removeQueryParam } from "lib/utils/updateQueryParam" -import dynamic from "next/dynamic" import Link from "next/link" import { useRouter } from "next/router" -import { useEffect, useState } from "react" - -const RightSlider = dynamic(() => - import("../../../src/components/ui/RightSlider").then( - (mod) => mod.RightSlider, - ), -) -const NewAutomationContent = dynamic(() => - import("../pages/newAutomation/components/NewAutomationContent").then( - (mod) => mod.NewAutomationContent, - ), -) +import { + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" export const CreateAutomationDropdown = () => { const router = useRouter() - - const [createAutomationSliderOpen, setCreateAutomationSliderOpen] = - useState(false) - const closeCreateAutomationSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "createAutomationSliderOpen") - } - } - - useEffect(() => { - if (router.query.createAutomationSliderOpen) { - setCreateAutomationSliderOpen(true) - } else { - setCreateAutomationSliderOpen(false) - } - }, [router.query]) + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) return ( - <> - - - - - - {/* Copy same styles as primary, base button because annoying console.error if we use Button component */} - - + Create - - - - - - {(isMobile) => { - if (isMobile) { - return ( - - Revenue share - - ) - } + + + {/* Copy same styles as primary, base button because annoying console.error if we use Button component */} + + + Create + + + + + + {(isMobile) => { + if (isMobile) { return ( - { - addQueryParam( - router, - "createAutomationSliderOpen", - "true", - ) - setCreateAutomationSliderOpen(true) - }} + Revenue share - + ) - }} - - - - - + } + return ( + { + setActiveSlider(Sliders.CREATE_AUTOMATION) + }} + > + Revenue share + + ) + }} + + + + ) } diff --git a/src/components/claim/ProfileReadyToClaim.tsx b/src/components/claim/ProfileReadyToClaim.tsx index 0cf4f652..3a868131 100644 --- a/src/components/claim/ProfileReadyToClaim.tsx +++ b/src/components/claim/ProfileReadyToClaim.tsx @@ -1,65 +1,41 @@ import Breakpoint from "@ui/Breakpoint" -import ClaimListView from "components/claim/ClaimListView" -import dynamic from "next/dynamic" import Link from "next/link" import { useRouter } from "next/router" -import React, { useEffect, useState } from "react" +import React from "react" import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" import { ReadyToClaim } from "./ReadyToClaim" -const RightSlider = dynamic(() => - import("../../components/ui/RightSlider").then((mod) => mod.RightSlider), -) - export const ProfileReadyToClaim = () => { const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const accountAddress = router.query.address as string - const [claimSliderOpen, setClaimSliderOpen] = useState(false) - const closeClaimSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "claimSliderOpen") - } - } - - useEffect(() => { - if (router.query.claimSliderOpen) { - setClaimSliderOpen(true) - } else { - setClaimSliderOpen(false) - } - }, [router.query]) - return ( - <> - - - - - {(isMobile) => { - if (isMobile) { - return ( - - - - ) - } + + {(isMobile) => { + if (isMobile) { return ( - { - setClaimSliderOpen(true) - addQueryParam(router, "claimSliderOpen", "true") - }} - > + - + ) - }} - - + } + return ( + { + setActiveSlider(Sliders.CLAIM_TOKENS) + }} + > + + + ) + }} + ) } diff --git a/src/components/claim/TerminalReadyToClaim.tsx b/src/components/claim/TerminalReadyToClaim.tsx index c13924e7..a6a27e4a 100644 --- a/src/components/claim/TerminalReadyToClaim.tsx +++ b/src/components/claim/TerminalReadyToClaim.tsx @@ -1,64 +1,43 @@ import Breakpoint from "@ui/Breakpoint" -import RightSlider from "@ui/RightSlider" -import ClaimListView from "components/claim/ClaimListView" import { convertGlobalId } from "models/terminal/utils" import Link from "next/link" import { useRouter } from "next/router" -import { useEffect, useState } from "react" import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" import { ReadyToClaim } from "./ReadyToClaim" export const TerminalReadyToClaim = () => { const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const { address } = convertGlobalId( router.query.chainNameAndSafeAddress as string, ) - const [claimSliderOpen, setClaimSliderOpen] = useState(false) - const closeClaimSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "claimSliderOpen") - } - } - - useEffect(() => { - if (router.query.claimSliderOpen) { - setClaimSliderOpen(true) - } else { - setClaimSliderOpen(false) - } - }, [router.query]) - return ( - <> - - - - - {(isMobile) => { - if (isMobile) { - return ( - - - - ) - } + + {(isMobile) => { + if (isMobile) { return ( - { - setClaimSliderOpen(true) - addQueryParam(router, "claimSliderOpen", "true") - }} - > + - + ) - }} - - + } + return ( + { + setActiveSlider(Sliders.CLAIM_TOKENS) + }} + > + + + ) + }} + ) } diff --git a/src/components/core/AccountNavBar/AccountDropdown.tsx b/src/components/core/AccountNavBar/AccountDropdown.tsx index e9efdc84..25221935 100644 --- a/src/components/core/AccountNavBar/AccountDropdown.tsx +++ b/src/components/core/AccountNavBar/AccountDropdown.tsx @@ -15,33 +15,24 @@ import { trackClick } from "lib/utils/amplitude" import dynamic from "next/dynamic" import Link from "next/link" import { useRouter } from "next/router" -import React, { useEffect, useState } from "react" +import React, { useState } from "react" +import { + Sliders, + useSliderManagerStore, +} from "../../../hooks/stores/useSliderManagerStore" import useStore from "../../../hooks/stores/useStore" import { useToast } from "../../../hooks/useToast" -import { - addQueryParam, - removeQueryParam, -} from "../../../lib/utils/updateQueryParam" import EmailNotificationForm from "../../email/EmailNotificationForm" -import CreateTerminalContent from "../../pages/createTerminal/components/CreateTerminalContent" import { AvatarAddress } from "../AvatarAddress" import NetworkDropdown from "../NetworkDropdown" const { LOCATION, EVENT_NAME } = TRACKING -const RightSlider = dynamic(() => - import("../../ui/RightSlider").then((mod) => mod.RightSlider), -) - export const AccountNavBar = () => { const router = useRouter() - const [createTerminalSliderOpen, setCreateTerminalSliderOpen] = - useState(false) - const closeCreateTerminalSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "createTerminalSliderOpen") - } - } + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const [notificationOpen, setNotificationOpen] = useState(false) const setActiveUser = useStore((state) => state.setActiveUser) @@ -54,24 +45,10 @@ export const AccountNavBar = () => { user, } = useDynamicContext() - useEffect(() => { - if (router.query.createTerminalSliderOpen) { - setCreateTerminalSliderOpen(true) - } else { - setCreateTerminalSliderOpen(false) - } - }, [router.query]) - const { successToast } = useToast() return ( <> - - - {(isMobile) => { if (isMobile) { @@ -91,18 +68,6 @@ export const AccountNavBar = () => { ) } - return ( - - { - setNotificationOpen(false) - successToast({ - message: "Email notification settings updated", - }) - }} - /> - - ) }} @@ -140,12 +105,7 @@ export const AccountNavBar = () => { accountAddress: primaryWallet?.address, userId: user?.userId, }) - addQueryParam( - router, - "createTerminalSliderOpen", - "true", - ) - setCreateTerminalSliderOpen(true) + setActiveSlider(Sliders.CREATE_TERMINAL) }} > + New Project diff --git a/src/components/core/SliderManager.tsx b/src/components/core/SliderManager.tsx index e85a9190..61b08854 100644 --- a/src/components/core/SliderManager.tsx +++ b/src/components/core/SliderManager.tsx @@ -11,8 +11,12 @@ import { Sliders, useSliderManagerStore, } from "../../hooks/stores/useSliderManagerStore" -import RequestDetailsContent from "../pages/requestDetails/components/RequestDetailsContent" +const RequestDetailsContent = dynamic(() => + import("../pages/requestDetails/components/RequestDetailsContent").then( + (mod) => mod.RequestDetailsContent, + ), +) const RequestTokensContent = dynamic(() => import("../pages/requestTokens/components/RequestTokensContent").then( (mod) => mod.RequestTokensContent, @@ -37,6 +41,25 @@ const NewAutomationContent = dynamic(() => ), ) +const EditTerminalContent = dynamic( + () => import("../pages/editTerminalDetails/components/EditTerminalContent"), +) + +const ClaimListView = dynamic(() => import("components/claim/ClaimListView")) + +const CreateTerminalContent = dynamic( + () => import("../pages/createTerminal/components/CreateTerminalContent"), +) +const EmailNotificationForm = dynamic( + () => import("../email/EmailNotificationForm"), +) + +const AutomationDetailsContent = dynamic(() => + import( + "components/pages/automationDetails/components/AutomationDetailsContent" + ).then((mod) => mod.AutomationDetailsContent), +) + const SliderManager = () => { const router = useRouter() const { mutate } = useSWRConfig() @@ -62,9 +85,32 @@ const SliderManager = () => { case Sliders.EDIT_MEMBERS: return + case Sliders.EDIT_TERMINAL_DETAILS: + return + case Sliders.CREATE_AUTOMATION: return + case Sliders.CREATE_TERMINAL: + return + + case Sliders.AUTOMATION_DETAILS: + return + + case Sliders.CLAIM_TOKENS: + return + + case Sliders.EMAIL_NOTIFICATIONS: + return ( + { + successToast({ + message: "Email notification settings updated", + }) + }} + /> + ) + case Sliders.SEND_TOKENS: return ( const Modal = dynamic(() => import("../../../ui/Modal").then((mod) => mod.Modal), ) -const RightSlider = dynamic(() => - import("../../../ui/RightSlider").then((mod) => mod.RightSlider), -) -const AutomationDetailsContent = dynamic(() => - import( - "components/pages/automationDetails/components/AutomationDetailsContent" - ).then((mod) => mod.AutomationDetailsContent), -) + const EmptyState = dynamic(() => import("components/emptyStates/EmptyState").then((mod) => mod.EmptyState), ) @@ -39,6 +35,9 @@ const CreateAutomationDropdown = dynamic(() => const AutomationsPageContent = () => { const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const { chainId, address } = parseGlobalId( router.query.chainNameAndSafeAddress as string, ) @@ -49,22 +48,6 @@ const AutomationsPageContent = () => { const { isMobile } = useBreakpoint() - const [automationDetailsOpen, setAutomationDetailsOpen] = - useState(false) - const closeAutomationDetailsSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "automationId") - } - } - - useEffect(() => { - if (router.query.automationId) { - setAutomationDetailsOpen(true) - } else { - setAutomationDetailsOpen(false) - } - }, [automations, router.query.automationId]) - const emptyStateTitle = isSigner ? "Set up Automations" : "No Automations" const emptyStateSubtitle = isSigner ? "Automate your collective operations — sharing on-chain revenue from NFT sales and project sponsorship" @@ -83,13 +66,6 @@ const AutomationsPageContent = () => { {revSharePrompt} - - - )}
@@ -118,11 +94,7 @@ const AutomationsPageContent = () => { `/${router.query.chainNameAndSafeAddress}/automations/new`, ) } else { - addQueryParam( - router, - "createAutomationSliderOpen", - "true", - ) + setActiveSlider(Sliders.CREATE_AUTOMATION) } }} > @@ -154,8 +126,7 @@ const AutomationsPageContent = () => { automation={automation} key={`automation-${automation.id}`} onClick={() => { - addQueryParam(router, "automationId", automation.id) - setAutomationDetailsOpen(true) + setActiveSlider(Sliders.AUTOMATION_DETAILS) }} /> ))} diff --git a/src/components/pages/members/components/MembersPageContent.tsx b/src/components/pages/members/components/MembersPageContent.tsx index 7fc6256e..b7ed8e68 100644 --- a/src/components/pages/members/components/MembersPageContent.tsx +++ b/src/components/pages/members/components/MembersPageContent.tsx @@ -1,16 +1,16 @@ import { PencilIcon } from "@heroicons/react/24/solid" import Breakpoint from "@ui/Breakpoint" import { Button } from "@ui/Button" -import RightSlider from "@ui/RightSlider" -import { addQueryParam, removeQueryParam } from "lib/utils/updateQueryParam" import { useRouter } from "next/router" -import { useEffect, useState } from "react" import { useGetSignerQuorumRequestChanges } from "../../../..//hooks/useGetSignerQuorumRequestChanges" import { convertGlobalId } from "../../../..//models/terminal/utils" import { useSafeMetadata } from "../../../../hooks/safe/useSafeMetadata" import { usePermissionsStore } from "../../../../hooks/stores/usePermissionsStore" +import { + Sliders, + useSliderManagerStore, +} from "../../../../hooks/stores/useSliderManagerStore" import { AvatarAddress } from "../../../core/AvatarAddress" -import EditMembersContent from "../../editMembers/components/EditMembersContent" const EditButton = ({ onClick, @@ -31,6 +31,9 @@ const EditButton = ({ const MembersPageContent = () => { const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const { chainNameAndSafeAddress } = router.query const { chainId, address } = convertGlobalId( chainNameAndSafeAddress as string, @@ -48,175 +51,147 @@ const MembersPageContent = () => { currentQuorum: safeMetadata?.quorum as number, }) - const [editMembersOpen, setEditMembersOpen] = useState(false) - const closeEditMembersSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "editMembers") - } - } - - useEffect(() => { - if (router.query.editMembers && isSigner) { - setEditMembersOpen(true) - } else { - setEditMembersOpen(false) - } - }, [router.query, isSigner]) - return ( - <> - - - -
-
-

Members

- {isSigner && ( -
- - {(isMobile) => { - if (isMobile) { - return ( - - ) - } +
+
+

Members

+ {isSigner && ( +
+ + {(isMobile) => { + if (isMobile) { return ( ) - }} - + } + return ( + + ) + }} + - - {(isMobile) => { - if (isMobile) { - return ( - - router.push( - `/${chainNameAndSafeAddress}/members/edit`, - ) - } - className="ml-2 rounded border border-gray-80" - /> - ) - } + + {(isMobile) => { + if (isMobile) { return ( - addQueryParam(router, "editMembers", "true") + router.push(`/${chainNameAndSafeAddress}/members/edit`) } className="ml-2 rounded border border-gray-80" /> ) - }} - + } + return ( + setActiveSlider(Sliders.EDIT_MEMBERS)} + className="ml-2 rounded border border-gray-80" + /> + ) + }} + +
+ )} +
+
+ {safeMetadata?.signers?.map((signerAddress) => { + const activeRequest = + data?.modifiedChangesToRequests?.modifiedAddresses?.[signerAddress] + return ( +
+ + + {activeRequest?.length ? ( +

+ Pending removal ·  + + View proposal + +

+ ) : null}
- )} -
-
- {safeMetadata?.signers?.map((signerAddress) => { - const activeRequest = - data?.modifiedChangesToRequests?.modifiedAddresses?.[ - signerAddress - ] - return ( -
+ ) + })} + {/* Pending new members */} + {data?.modifiedChangesToRequests?.modifiedAddresses && + Object.entries( + data?.modifiedChangesToRequests?.modifiedAddresses, + ).map(([key, requestIds]) => { + return !safeMetadata?.signers?.includes(key) ? ( +
- {activeRequest?.length ? ( + {(requestIds as string[])?.length ? (

- Pending removal ·  + Pending entry ·  View proposal

) : null}
- ) + ) : null })} - {/* Pending new members */} - {data?.modifiedChangesToRequests?.modifiedAddresses && - Object.entries( - data?.modifiedChangesToRequests?.modifiedAddresses, - ).map(([key, requestIds]) => { - return !safeMetadata?.signers?.includes(key) ? ( -
- - - {(requestIds as string[])?.length ? ( -

- Pending entry ·  - - View proposal - -

- ) : null} -
- ) : null - })} -
-
-

Quorum

-
-

- {safeMetadata?.quorum}/{safeMetadata?.signers?.length} +

+
+

Quorum

+
+

+ {safeMetadata?.quorum}/{safeMetadata?.signers?.length} +

+ {data?.modifiedChangesToRequests?.modifiedQuorum?.length ? ( +

+ Pending update ·  + + View proposal +

- {data?.modifiedChangesToRequests?.modifiedQuorum?.length ? ( -

- Pending update ·  - - View proposal - -

- ) : null} -
+ ) : null}
- +
) } diff --git a/src/components/pages/terminalDetails/Desktop.tsx b/src/components/pages/terminalDetails/Desktop.tsx index 1fb27545..8a0987af 100644 --- a/src/components/pages/terminalDetails/Desktop.tsx +++ b/src/components/pages/terminalDetails/Desktop.tsx @@ -1,53 +1,27 @@ import { EditButton } from "components/core/EditButton" -import dynamic from "next/dynamic" -import { useRouter } from "next/router" -import { useEffect, useState } from "react" import { Terminal } from "../../../../src/models/terminal/types" import { usePermissionsStore } from "../../../hooks/stores/usePermissionsStore" import { - addQueryParam, - removeQueryParam, -} from "../../../lib/utils/updateQueryParam" + Sliders, + useSliderManagerStore, +} from "../../../hooks/stores/useSliderManagerStore" import DesktopTerminalLayout from "../../terminal/DesktopTerminalLayout" -import EditTerminalContent from "../editTerminalDetails/components/EditTerminalContent" import TerminalDetailsPageContent from "./components/TerminalDetailsPageContent" -const RightSlider = dynamic(() => - import("../../ui/RightSlider").then((mod) => mod.RightSlider), -) - const Desktop = ({ terminal }: { terminal: Terminal }) => { - const router = useRouter() - const [editDetailsSliderOpen, setEditSliderOpen] = useState(false) const isSigner = usePermissionsStore((state) => state.isSigner) - const toggleDetailsSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "editDetails") - } - } - - useEffect(() => { - if (isSigner && "editDetails" in router.query) { - setEditSliderOpen(true) - } else { - setEditSliderOpen(false) - } - }, [router.query]) + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) return ( <> {isSigner && ( <> - - -
addQueryParam(router, "editDetails", "true")} + onClick={() => setActiveSlider(Sliders.EDIT_TERMINAL_DETAILS)} className="rounded border border-gray-80" />
diff --git a/src/components/request/ProfileRequestsList.tsx b/src/components/request/ProfileRequestsList.tsx index 42d50595..9be7a168 100644 --- a/src/components/request/ProfileRequestsList.tsx +++ b/src/components/request/ProfileRequestsList.tsx @@ -1,37 +1,22 @@ import Breakpoint from "@ui/Breakpoint" import { TabsContent } from "@ui/Tabs" import { EmptyState } from "components/emptyStates/EmptyState" -import dynamic from "next/dynamic" -import { useRouter } from "next/router" -import { useEffect, useState } from "react" import { - addQueryParam, - removeQueryParam, -} from "../../lib/utils/updateQueryParam" + Sliders, + useSliderManagerStore, +} from "../../hooks/stores/useSliderManagerStore" import { useRequestsCreatedByAccount } from "../../models/request/hooks" import { RequestFrob } from "../../models/request/types" import ProfileRequestTableRow from "../core/ProfileRequestTableRow" import RequestCard from "../core/RequestCard" import RequestTerminalLink from "../core/RequestTerminalLink" import { ProfileTab } from "../core/TabBars/ProfileTabBar" -import RequestDetailsContent from "../pages/requestDetails/components/RequestDetailsContent" - -const RightSlider = dynamic(() => - import("../ui/RightSlider").then((mod) => mod.RightSlider), -) export const ProfileRequestsList = ({ address }: { address: string }) => { - const router = useRouter() + const setActiveSlider = useSliderManagerStore( + (state) => state.setActiveSlider, + ) const { isLoading, requests, mutate } = useRequestsCreatedByAccount(address) - const [requestForDetails, setRequestForDetails] = useState< - RequestFrob | undefined - >(undefined) - const [detailsSliderOpen, setDetailsSliderOpen] = useState(false) - const closeDetailsSlider = (isOpen: boolean) => { - if (!isOpen) { - removeQueryParam(router, "requestId") - } - } const groupedRequests = requests?.reduce( (acc: Record, request) => { @@ -74,105 +59,74 @@ export const ProfileRequestsList = ({ address }: { address: string }) => { }) } - useEffect(() => { - if (router.query.requestId) { - setDetailsSliderOpen(true) - } else { - setDetailsSliderOpen(false) - } - }, [router.query]) - return ( - <> - {requestForDetails && ( - - { - // set state used by Request in slider - setRequestForDetails(args.payload) - mutateRequest(args) - }} + + {isLoading ? ( + <> + ) : requests?.length === 0 ? ( +
+ - - )} - - {isLoading ? ( - <> - ) : requests?.length === 0 ? ( -
- -
- ) : ( - - {(isMobile) => { - if (isMobile) { - return ( -
    - {requests?.map((request, idx) => { - return ( - - ) - })} -
- ) - } +
+ ) : ( + + {(isMobile) => { + if (isMobile) { return ( -
- {groupedRequests && - Object.values(groupedRequests!)?.map( - (terminalBundle, idx) => { - return ( - <> -
- -
- {terminalBundle.requests.map( - (request: RequestFrob, idx: number) => { - return ( - { - addQueryParam( - router, - "requestId", - request.id, - ) - setRequestForDetails(request) - setDetailsSliderOpen(true) - }} - /> - ) - }, - )} - - ) - }, - )} -
+
    + {requests?.map((request, idx) => { + return ( + + ) + })} +
) - }} -
- )} -
- + } + return ( +
+ {groupedRequests && + Object.values(groupedRequests!)?.map( + (terminalBundle, idx) => { + return ( + <> +
+ +
+ {terminalBundle.requests.map( + (request: RequestFrob, idx: number) => { + return ( + { + setActiveSlider(Sliders.REQUEST_DETAILS) + }} + /> + ) + }, + )} + + ) + }, + )} +
+ ) + }} + + )} + ) } diff --git a/src/hooks/stores/useSliderManagerStore.ts b/src/hooks/stores/useSliderManagerStore.ts index 31595f8b..e9670d46 100644 --- a/src/hooks/stores/useSliderManagerStore.ts +++ b/src/hooks/stores/useSliderManagerStore.ts @@ -8,11 +8,10 @@ export enum Sliders { "EDIT_MEMBERS" = "EDIT_MEMBERS", "CREATE_AUTOMATION" = "CREATE_AUTOMATION", "AUTOMATION_DETAILS" = "AUTOMATION_DETAILS", -} - -enum QueryValueTypes { - "ID" = "ID", - "BOOLEAN" = "BOOLEAN", + "EDIT_TERMINAL_DETAILS" = "EDIT_TERMINAL_DETAILS", + "CREATE_TERMINAL" = "CREATE_TERMINAL", + "EMAIL_NOTIFICATIONS" = "EMAIL_NOTIFICATIONS", + "CLAIM_TOKENS" = "CLAIM_TOKENS", } type SliderConfig = { @@ -47,6 +46,22 @@ const sliderOptions: Record = { key: Sliders.AUTOMATION_DETAILS, queryParam: "automationId", }, + CREATE_TERMINAL: { + key: Sliders.CREATE_TERMINAL, + queryParam: "createTerminalOpen", + }, + EDIT_TERMINAL_DETAILS: { + key: Sliders.EDIT_TERMINAL_DETAILS, + queryParam: "editTerminalId", + }, + EMAIL_NOTIFICATIONS: { + key: Sliders.EMAIL_NOTIFICATIONS, + queryParam: "emailNotificationsOpen", + }, + CLAIM_TOKENS: { + key: Sliders.CLAIM_TOKENS, + queryParam: "claimTokensOpen", + }, } interface SliderManagerState { From 0dc86c84cd005aa78141076a75e0aac649422c36 Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Sat, 25 Mar 2023 20:35:20 -0600 Subject: [PATCH 05/16] update useEffect --- src/components/core/SliderManager.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/core/SliderManager.tsx b/src/components/core/SliderManager.tsx index 61b08854..13ca592e 100644 --- a/src/components/core/SliderManager.tsx +++ b/src/components/core/SliderManager.tsx @@ -138,6 +138,18 @@ const SliderManager = () => { setActiveSlider(Sliders.EDIT_MEMBERS) } else if (isQueryParamSet("createAutomationOpen")) { setActiveSlider(Sliders.CREATE_AUTOMATION) + } else if (isQueryParamSet("createTerminalOpen")) { + setActiveSlider(Sliders.CREATE_TERMINAL) + } else if (isQueryParamSet("automationDetailsOpen")) { + setActiveSlider(Sliders.AUTOMATION_DETAILS) + } else if (isQueryParamSet("claimTokensOpen")) { + setActiveSlider(Sliders.CLAIM_TOKENS) + } else if (isQueryParamSet("emailNotificationsOpen")) { + setActiveSlider(Sliders.EMAIL_NOTIFICATIONS) + } else if (isQueryParamSet("sendTokensOpen")) { + setActiveSlider(Sliders.SEND_TOKENS) + } else if (isQueryParamSet("editTerminalDetailsOpen")) { + setActiveSlider(Sliders.EDIT_TERMINAL_DETAILS) } }, []) From ab2fdd986ee682da11a77cf224def2c716d4dd8f Mon Sep 17 00:00:00 2001 From: Michael Gingras Date: Sun, 26 Mar 2023 17:33:22 -0600 Subject: [PATCH 06/16] fixing up quite a few of the sliders --- .../automation/CreateAutomationDropdown.tsx | 2 +- src/components/claim/ClaimListView.tsx | 11 ++++++++++- src/components/claim/ProfileReadyToClaim.tsx | 2 +- src/components/claim/TerminalReadyToClaim.tsx | 2 +- .../core/AccountNavBar/AccountDropdown.tsx | 14 +++++++++++--- src/components/core/SliderManager.tsx | 2 +- .../components/AutomationDetailsContent.tsx | 13 +++++++++++-- .../components/AutomationsPageContent.tsx | 8 ++++++-- .../members/components/MembersPageContent.tsx | 6 ++++-- .../components/RequestDetailsContent.tsx | 2 -- src/components/pages/terminalDetails/Desktop.tsx | 6 +++++- src/components/request/ProfileRequestsList.tsx | 4 +++- src/components/terminal/ProfileTerminalsList.tsx | 10 ++++++++-- 13 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/components/automation/CreateAutomationDropdown.tsx b/src/components/automation/CreateAutomationDropdown.tsx index 2bdd7008..bb0511e4 100644 --- a/src/components/automation/CreateAutomationDropdown.tsx +++ b/src/components/automation/CreateAutomationDropdown.tsx @@ -51,7 +51,7 @@ export const CreateAutomationDropdown = () => { return ( { - setActiveSlider(Sliders.CREATE_AUTOMATION) + setActiveSlider(Sliders.CREATE_AUTOMATION, { value: true }) }} > Revenue share diff --git a/src/components/claim/ClaimListView.tsx b/src/components/claim/ClaimListView.tsx index 98210fae..e3fd4b3d 100644 --- a/src/components/claim/ClaimListView.tsx +++ b/src/components/claim/ClaimListView.tsx @@ -8,6 +8,7 @@ import { addressesAreEqual } from "lib/utils" import { useAccountItemsToClaim } from "models/account/hooks" import { RevShareWithdraw } from "models/automation/types" import { RequestFrob } from "models/request/types" +import { useRouter } from "next/router" import { useReducer, useState } from "react" enum BatchEvent { @@ -93,7 +94,15 @@ const batchReducer = ( } const ClaimListView = ({ recipientAddress }: { recipientAddress: string }) => { - const { isLoading, items, mutate } = useAccountItemsToClaim(recipientAddress) + const router = useRouter() + // check if profile page vs terminal page + let recipientAddressParam = router.query.address as string + const { isLoading, items, mutate, error } = useAccountItemsToClaim( + recipientAddressParam, + ) + console.log("error", error) + console.log("items", items) + const [claimDrawerItemPending, setClaimDrawerItemPending] = useState(false) const [claimDrawerOpen, setClaimDrawerOpen] = useState(false) diff --git a/src/components/claim/ProfileReadyToClaim.tsx b/src/components/claim/ProfileReadyToClaim.tsx index 3a868131..58a0419e 100644 --- a/src/components/claim/ProfileReadyToClaim.tsx +++ b/src/components/claim/ProfileReadyToClaim.tsx @@ -29,7 +29,7 @@ export const ProfileReadyToClaim = () => { { - setActiveSlider(Sliders.CLAIM_TOKENS) + setActiveSlider(Sliders.CLAIM_TOKENS, { value: true }) }} > diff --git a/src/components/claim/TerminalReadyToClaim.tsx b/src/components/claim/TerminalReadyToClaim.tsx index a6a27e4a..fb58a0c0 100644 --- a/src/components/claim/TerminalReadyToClaim.tsx +++ b/src/components/claim/TerminalReadyToClaim.tsx @@ -31,7 +31,7 @@ export const TerminalReadyToClaim = () => { { - setActiveSlider(Sliders.CLAIM_TOKENS) + setActiveSlider(Sliders.CLAIM_TOKENS, { value: true }) }} > diff --git a/src/components/core/AccountNavBar/AccountDropdown.tsx b/src/components/core/AccountNavBar/AccountDropdown.tsx index 25221935..922b066f 100644 --- a/src/components/core/AccountNavBar/AccountDropdown.tsx +++ b/src/components/core/AccountNavBar/AccountDropdown.tsx @@ -105,7 +105,10 @@ export const AccountNavBar = () => { accountAddress: primaryWallet?.address, userId: user?.userId, }) - setActiveSlider(Sliders.CREATE_TERMINAL) + + setActiveSlider(Sliders.CREATE_TERMINAL, { + value: true, + }) }} > + New Project @@ -117,8 +120,13 @@ export const AccountNavBar = () => {
setNotificationOpen(true)} + className="h-8 w-8 cursor-pointer rounded bg-gray-90 p-1" + onClick={() => { + setNotificationOpen(true) + setActiveSlider(Sliders.EMAIL_NOTIFICATIONS, { + value: true, + }) + }} >
diff --git a/src/components/core/SliderManager.tsx b/src/components/core/SliderManager.tsx index 13ca592e..4b731665 100644 --- a/src/components/core/SliderManager.tsx +++ b/src/components/core/SliderManager.tsx @@ -140,7 +140,7 @@ const SliderManager = () => { setActiveSlider(Sliders.CREATE_AUTOMATION) } else if (isQueryParamSet("createTerminalOpen")) { setActiveSlider(Sliders.CREATE_TERMINAL) - } else if (isQueryParamSet("automationDetailsOpen")) { + } else if (isQueryParamSet("automationId")) { setActiveSlider(Sliders.AUTOMATION_DETAILS) } else if (isQueryParamSet("claimTokensOpen")) { setActiveSlider(Sliders.CLAIM_TOKENS) diff --git a/src/components/pages/automationDetails/components/AutomationDetailsContent.tsx b/src/components/pages/automationDetails/components/AutomationDetailsContent.tsx index 120648b7..2507d787 100644 --- a/src/components/pages/automationDetails/components/AutomationDetailsContent.tsx +++ b/src/components/pages/automationDetails/components/AutomationDetailsContent.tsx @@ -1,5 +1,6 @@ import { PencilIcon } from "@heroicons/react/24/solid" import { ArrowLeft } from "@icons" +import { getQueryParam } from "lib/utils/updateQueryParam" import { useAutomation } from "models/automation/hooks" import { RevShareFrob } from "models/automation/types" import Link from "next/link" @@ -11,12 +12,20 @@ import { AutomationTabBar } from "../../../automation/AutomationTabBar" export const AutomationDetailsContent = () => { const router = useRouter() - const { automation } = useAutomation(router.query.automationId as string) + let { automationId } = router.query + // if the query param is set "shallowly" next router doesn't pick up on it + // this happens on desktop, if the user clicks a request from the list + // we can still grab it from the url manually + if (!automationId) { + automationId = getQueryParam("automationId") as string + } + + const { automation } = useAutomation(automationId as string) const [revShare, setRevShare] = useState() // prevents sliders from making content invisible as they disappear when query param is unset useEffect(() => { - if (automation && router.query.automationId) { + if (automation && automationId) { setRevShare(automation) } }, [automation, router.query.automationId]) diff --git a/src/components/pages/automations/components/AutomationsPageContent.tsx b/src/components/pages/automations/components/AutomationsPageContent.tsx index 8808abe6..22ebe0ed 100644 --- a/src/components/pages/automations/components/AutomationsPageContent.tsx +++ b/src/components/pages/automations/components/AutomationsPageContent.tsx @@ -94,7 +94,9 @@ const AutomationsPageContent = () => { `/${router.query.chainNameAndSafeAddress}/automations/new`, ) } else { - setActiveSlider(Sliders.CREATE_AUTOMATION) + setActiveSlider(Sliders.CREATE_AUTOMATION, { + value: true, + }) } }} > @@ -126,7 +128,9 @@ const AutomationsPageContent = () => { automation={automation} key={`automation-${automation.id}`} onClick={() => { - setActiveSlider(Sliders.AUTOMATION_DETAILS) + setActiveSlider(Sliders.AUTOMATION_DETAILS, { + id: automation.id, + }) }} /> ))} diff --git a/src/components/pages/members/components/MembersPageContent.tsx b/src/components/pages/members/components/MembersPageContent.tsx index b7ed8e68..fc6096a5 100644 --- a/src/components/pages/members/components/MembersPageContent.tsx +++ b/src/components/pages/members/components/MembersPageContent.tsx @@ -75,7 +75,7 @@ const MembersPageContent = () => {
) } diff --git a/src/components/claim/ClaimListView.tsx b/src/components/claim/ClaimListView.tsx index 45ebe662..7074c7e1 100644 --- a/src/components/claim/ClaimListView.tsx +++ b/src/components/claim/ClaimListView.tsx @@ -93,9 +93,8 @@ const batchReducer = ( } } -const ClaimListView = ({ recipientAddress }: { recipientAddress: string }) => { +const ClaimListView = () => { const router = useRouter() - console.log(router) // check if profile page vs terminal page let recipientAddressParam const { address, chainNameAndSafeAddress } = router.query as { @@ -221,7 +220,7 @@ const ClaimListView = ({ recipientAddress }: { recipientAddress: string }) => { { { const { address: accountAddress } = useAccount() return (