From 8b1ae309fb052cce5d69bd4bbfa55e04b2636949 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Tue, 24 Dec 2024 15:07:39 +0800 Subject: [PATCH] refactor(ui): useZustandShallowSelector --- internal/certificate/service.go | 1 + ui/package-lock.json | 9 ++++++++ ui/package.json | 1 + ui/src/components/access/AccessSelect.tsx | 4 ++-- .../notification/NotifyChannels.tsx | 4 ++-- ui/src/components/workflow/AddNode.tsx | 10 +++------ ui/src/components/workflow/ApplyForm.tsx | 11 ++++------ ui/src/components/workflow/BranchNode.tsx | 9 +++----- ui/src/components/workflow/ConditionNode.tsx | 10 +++------ .../components/workflow/DeployToAliyunALB.tsx | 11 +++------- .../components/workflow/DeployToAliyunCDN.tsx | 10 +++------ .../components/workflow/DeployToAliyunCLB.tsx | 11 +++------- .../components/workflow/DeployToAliyunNLB.tsx | 11 +++------- .../components/workflow/DeployToAliyunOss.tsx | 12 +++------- .../workflow/DeployToBaiduCloudCDN.tsx | 11 +++------- .../workflow/DeployToByteplusCDN.tsx | 11 +++------- .../workflow/DeployToDogeCloudCDN.tsx | 10 +++------ .../workflow/DeployToHuaweiCloudCDN.tsx | 10 +++------ .../workflow/DeployToHuaweiCloudELB.tsx | 10 +++------ .../workflow/DeployToKubernetesSecret.tsx | 10 +++------ ui/src/components/workflow/DeployToLocal.tsx | 11 +++------- .../components/workflow/DeployToQiniuCDN.tsx | 10 +++------ ui/src/components/workflow/DeployToSSH.tsx | 11 +++------- .../workflow/DeployToTencentCDN.tsx | 11 +++------- .../workflow/DeployToTencentCLB.tsx | 11 +++------- .../workflow/DeployToTencentCOS.tsx | 11 +++------- .../workflow/DeployToTencentTEO.tsx | 11 +++------- .../workflow/DeployToVolcengineCDN.tsx | 11 +++------- .../workflow/DeployToVolcengineLive.tsx | 11 +++------- .../components/workflow/DeployToWebhook.tsx | 12 +++------- ui/src/components/workflow/Node.tsx | 10 +++------ ui/src/components/workflow/NotifyForm.tsx | 9 +++----- ui/src/components/workflow/StartForm.tsx | 9 +++----- .../workflow/WorkflowBaseInfoEditDialog.tsx | 10 +++------ ui/src/components/workflow/WorkflowLog.tsx | 5 ++--- ui/src/hooks/index.ts | 3 ++- ui/src/hooks/useBrowserTheme.ts | 4 ++-- ui/src/hooks/useZustandShallowSelector.ts | 18 +++++++++++++++ ui/src/pages/accesses/AccessList.tsx | 4 ++-- ui/src/pages/certificates/CertificateList.tsx | 2 +- .../pages/settings/SettingsNotification.tsx | 4 ++-- ui/src/pages/workflows/WorkflowDetail.tsx | 22 ++++++------------- ui/src/pages/workflows/WorkflowList.tsx | 4 ++-- ui/src/repository/access.ts | 6 +++++ ui/src/repository/certificate.ts | 6 +++++ ui/src/repository/workflow.ts | 6 ++++- ui/src/repository/workflowRunLog.ts | 4 +++- ui/src/router.tsx | 2 +- ui/src/stores/access/index.ts | 17 +++++++++----- ui/src/stores/contact/index.ts | 15 ++++++++----- ui/src/stores/notify/index.ts | 17 +++++++++----- 51 files changed, 194 insertions(+), 269 deletions(-) create mode 100644 ui/src/hooks/useZustandShallowSelector.ts diff --git a/internal/certificate/service.go b/internal/certificate/service.go index 36acac3b..2b29b9f5 100644 --- a/internal/certificate/service.go +++ b/internal/certificate/service.go @@ -41,6 +41,7 @@ func (s *certificateService) InitSchedule(ctx context.Context) error { return } msg := buildMsg(certs) + // TODO: 空指针 Bug if err := notify.SendToAllChannels(msg.Subject, msg.Message); err != nil { app.GetApp().Logger().Error("failed to send expire soon certificate", "err", err) } diff --git a/ui/package-lock.json b/ui/package-lock.json index 465706d3..cce4813e 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -31,6 +31,7 @@ "lucide-react": "^0.469.0", "nanoid": "^5.0.9", "pocketbase": "^0.21.5", + "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", @@ -6743,6 +6744,14 @@ } ] }, + "node_modules/radash": { + "version": "12.1.0", + "resolved": "https://registry.npmmirror.com/radash/-/radash-12.1.0.tgz", + "integrity": "sha512-b0Zcf09AhqKS83btmUeYBS8tFK7XL2e3RvLmZcm0sTdF1/UUlHSsjXdCcWNxe7yfmAlPve5ym0DmKGtTzP6kVQ==", + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/rc-cascader": { "version": "3.30.0", "resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.30.0.tgz", diff --git a/ui/package.json b/ui/package.json index 107d2be8..2c514768 100644 --- a/ui/package.json +++ b/ui/package.json @@ -33,6 +33,7 @@ "lucide-react": "^0.469.0", "nanoid": "^5.0.9", "pocketbase": "^0.21.5", + "radash": "^12.1.0", "react": "^18.3.1", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.3.1", diff --git a/ui/src/components/access/AccessSelect.tsx b/ui/src/components/access/AccessSelect.tsx index 3cbca057..1e7d9f3f 100644 --- a/ui/src/components/access/AccessSelect.tsx +++ b/ui/src/components/access/AccessSelect.tsx @@ -12,7 +12,7 @@ export type AccessTypeSelectProps = Omit< }; const AccessSelect = ({ filter, ...props }: AccessTypeSelectProps) => { - const { initialized, accesses, fetchAccesses } = useAccessStore(); + const { accesses, loadedAtOnce, fetchAccesses } = useAccessStore(); useEffect(() => { fetchAccesses(); }, [fetchAccesses]); @@ -64,7 +64,7 @@ const AccessSelect = ({ filter, ...props }: AccessTypeSelectProps) => { return {props.placeholder}; }} - loading={!initialized} + loading={!loadedAtOnce} options={options} optionFilterProp="label" optionLabelProp={undefined} diff --git a/ui/src/components/notification/NotifyChannels.tsx b/ui/src/components/notification/NotifyChannels.tsx index 76d09817..6e9c8d23 100644 --- a/ui/src/components/notification/NotifyChannels.tsx +++ b/ui/src/components/notification/NotifyChannels.tsx @@ -70,7 +70,7 @@ export type NotifyChannelsProps = { const NotifyChannels = ({ className, classNames, style, styles }: NotifyChannelsProps) => { const { t, i18n } = useTranslation(); - const { initialized, channels, setChannel, fetchChannels } = useNotifyChannelStore(); + const { channels, loadedAtOnce, setChannel, fetchChannels } = useNotifyChannelStore(); useEffect(() => { fetchChannels(); }, [fetchChannels]); @@ -105,7 +105,7 @@ const NotifyChannels = ({ className, classNames, style, styles }: NotifyChannels return (
- {!initialized ? ( + {!loadedAtOnce ? ( ) : ( diff --git a/ui/src/components/workflow/AddNode.tsx b/ui/src/components/workflow/AddNode.tsx index 95307cac..854a9281 100644 --- a/ui/src/components/workflow/AddNode.tsx +++ b/ui/src/components/workflow/AddNode.tsx @@ -3,8 +3,8 @@ import { Plus } from "lucide-react"; import { BrandNodeProps, NodeProps } from "./types"; import { newWorkflowNode, workflowNodeDropdownList, WorkflowNodeType } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useZustandShallowSelector } from "@/hooks"; +import { useWorkflowStore } from "@/stores/workflow"; import { DropdownMenu, DropdownMenuContent, @@ -21,12 +21,8 @@ import DropdownMenuItemIcon from "./DropdownMenuItemIcon"; import Show from "../Show"; import { useTranslation } from "react-i18next"; -const selectState = (state: WorkflowState) => ({ - addNode: state.addNode, -}); - const AddNode = ({ data }: NodeProps | BrandNodeProps) => { - const { addNode } = useWorkflowStore(useShallow(selectState)); + const { addNode } = useWorkflowStore(useZustandShallowSelector(["addNode"])); const { t } = useTranslation(); const handleTypeSelected = (type: WorkflowNodeType, provider?: string) => { diff --git a/ui/src/components/workflow/ApplyForm.tsx b/ui/src/components/workflow/ApplyForm.tsx index baf18d4c..33217b98 100644 --- a/ui/src/components/workflow/ApplyForm.tsx +++ b/ui/src/components/workflow/ApplyForm.tsx @@ -13,23 +13,20 @@ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrig import AccessEditModal from "@/components/access/AccessEditModal"; import EmailsEdit from "@/components/certimate/EmailsEdit"; import StringList from "@/components/certimate/StringList"; - import { accessProvidersMap } from "@/domain/access"; +import { useZustandShallowSelector } from "@/hooks"; import { useAccessStore } from "@/stores/access"; import { useContactStore } from "@/stores/contact"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; import { usePanel } from "./PanelProvider"; type ApplyFormProps = { data: WorkflowNode; }; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, -}); + const ApplyForm = ({ data }: ApplyFormProps) => { - const { updateNode } = useWorkflowStore(useShallow(selectState)); + const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"])); const { accesses } = useAccessStore(); const { emails, fetchEmails } = useContactStore(); diff --git a/ui/src/components/workflow/BranchNode.tsx b/ui/src/components/workflow/BranchNode.tsx index b09b2fd3..5e1d5ccf 100644 --- a/ui/src/components/workflow/BranchNode.tsx +++ b/ui/src/components/workflow/BranchNode.tsx @@ -4,15 +4,12 @@ import { WorkflowBranchNode, WorkflowNode } from "@/domain/workflow"; import NodeRender from "./NodeRender"; import { memo } from "react"; import { BrandNodeProps } from "./types"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { useTranslation } from "react-i18next"; -const selectState = (state: WorkflowState) => ({ - addBranch: state.addBranch, -}); const BranchNode = memo(({ data }: BrandNodeProps) => { - const { addBranch } = useWorkflowStore(useShallow(selectState)); + const { addBranch } = useWorkflowStore(useZustandShallowSelector(["addBranch"])); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/ConditionNode.tsx b/ui/src/components/workflow/ConditionNode.tsx index d35d80cc..9c29ca7d 100644 --- a/ui/src/components/workflow/ConditionNode.tsx +++ b/ui/src/components/workflow/ConditionNode.tsx @@ -1,16 +1,12 @@ -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; +import { useWorkflowStore } from "@/stores/workflow"; import AddNode from "./AddNode"; import { NodeProps } from "./types"; -import { useShallow } from "zustand/shallow"; +import { useZustandShallowSelector } from "@/hooks"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu"; import { Ellipsis, Trash2 } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - removeBranch: state.removeBranch, -}); const ConditionNode = ({ data, branchId, branchIndex }: NodeProps) => { - const { updateNode, removeBranch } = useWorkflowStore(useShallow(selectState)); + const { updateNode, removeBranch } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeBranch"])); const handleNameBlur = (e: React.FocusEvent) => { updateNode({ ...data, name: e.target.innerText }); }; diff --git a/ui/src/components/workflow/DeployToAliyunALB.tsx b/ui/src/components/workflow/DeployToAliyunALB.tsx index 270a2e74..8c14e8fe 100644 --- a/ui/src/components/workflow/DeployToAliyunALB.tsx +++ b/ui/src/components/workflow/DeployToAliyunALB.tsx @@ -6,8 +6,8 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; import { DeployFormProps } from "./DeployForm"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -18,13 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const DeployToAliyunALB = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToAliyunCDN.tsx b/ui/src/components/workflow/DeployToAliyunCDN.tsx index 5c031833..6bb794b3 100644 --- a/ui/src/components/workflow/DeployToAliyunCDN.tsx +++ b/ui/src/components/workflow/DeployToAliyunCDN.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -19,12 +19,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToAliyunCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToAliyunCLB.tsx b/ui/src/components/workflow/DeployToAliyunCLB.tsx index 3562978f..83a0eb86 100644 --- a/ui/src/components/workflow/DeployToAliyunCLB.tsx +++ b/ui/src/components/workflow/DeployToAliyunCLB.tsx @@ -6,8 +6,8 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; import { DeployFormProps } from "./DeployForm"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -18,13 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const DeployToAliyunCLB = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToAliyunNLB.tsx b/ui/src/components/workflow/DeployToAliyunNLB.tsx index 5f183dae..45204067 100644 --- a/ui/src/components/workflow/DeployToAliyunNLB.tsx +++ b/ui/src/components/workflow/DeployToAliyunNLB.tsx @@ -6,21 +6,16 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; import { DeployFormProps } from "./DeployForm"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"; import { Button } from "../ui/button"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const DeployToAliyunNLB = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToAliyunOss.tsx b/ui/src/components/workflow/DeployToAliyunOss.tsx index 49536ccf..f0cc90df 100644 --- a/ui/src/components/workflow/DeployToAliyunOss.tsx +++ b/ui/src/components/workflow/DeployToAliyunOss.tsx @@ -7,25 +7,19 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; - import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToAliyunOSS = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToBaiduCloudCDN.tsx b/ui/src/components/workflow/DeployToBaiduCloudCDN.tsx index c5449272..ca01bc51 100644 --- a/ui/src/components/workflow/DeployToBaiduCloudCDN.tsx +++ b/ui/src/components/workflow/DeployToBaiduCloudCDN.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToBaiduCloudCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToByteplusCDN.tsx b/ui/src/components/workflow/DeployToByteplusCDN.tsx index c6dcb424..99a57db1 100644 --- a/ui/src/components/workflow/DeployToByteplusCDN.tsx +++ b/ui/src/components/workflow/DeployToByteplusCDN.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToByteplusCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToDogeCloudCDN.tsx b/ui/src/components/workflow/DeployToDogeCloudCDN.tsx index 24aa21cb..f01a0dca 100644 --- a/ui/src/components/workflow/DeployToDogeCloudCDN.tsx +++ b/ui/src/components/workflow/DeployToDogeCloudCDN.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -19,12 +19,8 @@ import AccessSelect from "./AccessSelect"; import { Plus } from "lucide-react"; import AccessEditModal from "../access/AccessEditModal"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToDogeCloudCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToHuaweiCloudCDN.tsx b/ui/src/components/workflow/DeployToHuaweiCloudCDN.tsx index 903bc4a9..0a430422 100644 --- a/ui/src/components/workflow/DeployToHuaweiCloudCDN.tsx +++ b/ui/src/components/workflow/DeployToHuaweiCloudCDN.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -20,12 +20,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToHuaweiCloudCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToHuaweiCloudELB.tsx b/ui/src/components/workflow/DeployToHuaweiCloudELB.tsx index d56b62ba..30541463 100644 --- a/ui/src/components/workflow/DeployToHuaweiCloudELB.tsx +++ b/ui/src/components/workflow/DeployToHuaweiCloudELB.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -19,12 +19,8 @@ import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; import AccessSelect from "./AccessSelect"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToHuaweiCloudELB = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToKubernetesSecret.tsx b/ui/src/components/workflow/DeployToKubernetesSecret.tsx index 9cc418ba..9ee37ff6 100644 --- a/ui/src/components/workflow/DeployToKubernetesSecret.tsx +++ b/ui/src/components/workflow/DeployToKubernetesSecret.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -19,12 +19,8 @@ import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; import AccessSelect from "./AccessSelect"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToKubernetesSecret = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToLocal.tsx b/ui/src/components/workflow/DeployToLocal.tsx index 942f646b..96827afb 100644 --- a/ui/src/components/workflow/DeployToLocal.tsx +++ b/ui/src/components/workflow/DeployToLocal.tsx @@ -7,8 +7,8 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "../ui/select"; import { Button } from "../ui/button"; import { DeployFormProps } from "./DeployForm"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useEffect, useState } from "react"; import i18n from "@/i18n"; @@ -19,11 +19,6 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const t = i18n.t; const formSchema = z @@ -75,7 +70,7 @@ const formSchema = z }); const DeployToLocal = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToQiniuCDN.tsx b/ui/src/components/workflow/DeployToQiniuCDN.tsx index 839b4d7f..ce027aa1 100644 --- a/ui/src/components/workflow/DeployToQiniuCDN.tsx +++ b/ui/src/components/workflow/DeployToQiniuCDN.tsx @@ -7,8 +7,8 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; @@ -19,12 +19,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToQiniuCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToSSH.tsx b/ui/src/components/workflow/DeployToSSH.tsx index f41596ce..78bafc42 100644 --- a/ui/src/components/workflow/DeployToSSH.tsx +++ b/ui/src/components/workflow/DeployToSSH.tsx @@ -7,8 +7,8 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "../ui/select"; import { Button } from "../ui/button"; import { DeployFormProps } from "./DeployForm"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useEffect, useState } from "react"; import i18n from "@/i18n"; @@ -18,11 +18,6 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const t = i18n.t; const formSchema = z @@ -71,7 +66,7 @@ const formSchema = z }); const DeployToSSH = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToTencentCDN.tsx b/ui/src/components/workflow/DeployToTencentCDN.tsx index b7189e51..7d2f723d 100644 --- a/ui/src/components/workflow/DeployToTencentCDN.tsx +++ b/ui/src/components/workflow/DeployToTencentCDN.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToTencentCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToTencentCLB.tsx b/ui/src/components/workflow/DeployToTencentCLB.tsx index e97b95cb..05ad2e35 100644 --- a/ui/src/components/workflow/DeployToTencentCLB.tsx +++ b/ui/src/components/workflow/DeployToTencentCLB.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -21,12 +20,8 @@ import { Plus } from "lucide-react"; type TencentResourceType = "ssl-deploy" | "loadbalancer" | "listener" | "ruledomain"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToTencentCLB = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToTencentCOS.tsx b/ui/src/components/workflow/DeployToTencentCOS.tsx index 3766a52a..7a04c7ac 100644 --- a/ui/src/components/workflow/DeployToTencentCOS.tsx +++ b/ui/src/components/workflow/DeployToTencentCOS.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessEditModal from "../access/AccessEditModal"; import AccessSelect from "./AccessSelect"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToTencentCOS = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToTencentTEO.tsx b/ui/src/components/workflow/DeployToTencentTEO.tsx index faa0e43e..95d27a5c 100644 --- a/ui/src/components/workflow/DeployToTencentTEO.tsx +++ b/ui/src/components/workflow/DeployToTencentTEO.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToTencentTEO = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToVolcengineCDN.tsx b/ui/src/components/workflow/DeployToVolcengineCDN.tsx index 444410fd..ccb05267 100644 --- a/ui/src/components/workflow/DeployToVolcengineCDN.tsx +++ b/ui/src/components/workflow/DeployToVolcengineCDN.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToVolcengineCDN = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToVolcengineLive.tsx b/ui/src/components/workflow/DeployToVolcengineLive.tsx index 06c34dd4..b06309a7 100644 --- a/ui/src/components/workflow/DeployToVolcengineLive.tsx +++ b/ui/src/components/workflow/DeployToVolcengineLive.tsx @@ -7,11 +7,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,12 +18,8 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); const DeployToVolcengineLive = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/DeployToWebhook.tsx b/ui/src/components/workflow/DeployToWebhook.tsx index 7c383056..cdb59ef2 100644 --- a/ui/src/components/workflow/DeployToWebhook.tsx +++ b/ui/src/components/workflow/DeployToWebhook.tsx @@ -6,11 +6,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from " import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { Button } from "../ui/button"; - import { useEffect, useState } from "react"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; import { SelectLabel } from "@radix-ui/react-select"; @@ -19,17 +18,12 @@ import AccessSelect from "./AccessSelect"; import AccessEditModal from "../access/AccessEditModal"; import { Plus } from "lucide-react"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - getWorkflowOuptutBeforeId: state.getWorkflowOuptutBeforeId, -}); - const KVTypeSchema = z.object({ key: z.string(), value: z.string(), }); const DeployToWebhook = ({ data }: DeployFormProps) => { - const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useShallow(selectState)); + const { updateNode, getWorkflowOuptutBeforeId } = useWorkflowStore(useZustandShallowSelector(["updateNode", "getWorkflowOuptutBeforeId"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/Node.tsx b/ui/src/components/workflow/Node.tsx index 0496d8fa..a8ac3762 100644 --- a/ui/src/components/workflow/Node.tsx +++ b/ui/src/components/workflow/Node.tsx @@ -1,7 +1,7 @@ import { WorkflowNode, WorkflowNodeType } from "@/domain/workflow"; import AddNode from "./AddNode"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../ui/dropdown-menu"; import { Ellipsis, Trash2 } from "lucide-react"; import { usePanel } from "./PanelProvider"; @@ -17,12 +17,8 @@ type NodeProps = { const i18nPrefix = "workflow.node"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, - removeNode: state.removeNode, -}); const Node = ({ data }: NodeProps) => { - const { updateNode, removeNode } = useWorkflowStore(useShallow(selectState)); + const { updateNode, removeNode } = useWorkflowStore(useZustandShallowSelector(["updateNode", "removeNode"])); const handleNameBlur = (e: React.FocusEvent) => { updateNode({ ...data, name: e.target.innerText }); }; diff --git a/ui/src/components/workflow/NotifyForm.tsx b/ui/src/components/workflow/NotifyForm.tsx index a632ddb2..75d87643 100644 --- a/ui/src/components/workflow/NotifyForm.tsx +++ b/ui/src/components/workflow/NotifyForm.tsx @@ -5,8 +5,8 @@ import { z } from "zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger } from "../ui/select"; import { Input } from "../ui/input"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { usePanel } from "./PanelProvider"; import { useTranslation } from "react-i18next"; import { Button } from "../ui/button"; @@ -21,9 +21,6 @@ type NotifyFormProps = { data: WorkflowNode; }; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, -}); type ChannelName = { key: string; label: string; @@ -31,7 +28,7 @@ type ChannelName = { const i18nPrefix = "workflow.node.notify.form"; const NotifyForm = ({ data }: NotifyFormProps) => { - const { updateNode } = useWorkflowStore(useShallow(selectState)); + const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); const { channels: supportedChannels, fetchChannels } = useNotifyChannelStore(); diff --git a/ui/src/components/workflow/StartForm.tsx b/ui/src/components/workflow/StartForm.tsx index f87cc510..d8645660 100644 --- a/ui/src/components/workflow/StartForm.tsx +++ b/ui/src/components/workflow/StartForm.tsx @@ -5,12 +5,12 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Radio } from "antd"; import { parseExpression } from "cron-parser"; import { z } from "zod"; -import { useShallow } from "zustand/shallow"; import { Button } from "../ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"; import { Input } from "../ui/input"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; +import { useWorkflowStore } from "@/stores/workflow"; import { WorkflowNode, WorkflowNodeConfig } from "@/domain/workflow"; import { usePanel } from "./PanelProvider"; import { RadioChangeEvent } from "antd/lib"; @@ -41,11 +41,8 @@ type StartFormProps = { const i18nPrefix = "workflow.node.start.form"; -const selectState = (state: WorkflowState) => ({ - updateNode: state.updateNode, -}); const StartForm = ({ data }: StartFormProps) => { - const { updateNode } = useWorkflowStore(useShallow(selectState)); + const { updateNode } = useWorkflowStore(useZustandShallowSelector(["updateNode"])); const { hidePanel } = usePanel(); const { t } = useTranslation(); diff --git a/ui/src/components/workflow/WorkflowBaseInfoEditDialog.tsx b/ui/src/components/workflow/WorkflowBaseInfoEditDialog.tsx index 0ed4162a..770f154a 100644 --- a/ui/src/components/workflow/WorkflowBaseInfoEditDialog.tsx +++ b/ui/src/components/workflow/WorkflowBaseInfoEditDialog.tsx @@ -1,7 +1,7 @@ import { z } from "zod"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; -import { useShallow } from "zustand/shallow"; +import { useWorkflowStore } from "@/stores/workflow"; +import { useZustandShallowSelector } from "@/hooks"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"; @@ -20,12 +20,8 @@ const formSchema = z.object({ description: z.string(), }); -const selectState = (state: WorkflowState) => ({ - setBaseInfo: state.setBaseInfo, - workflow: state.workflow, -}); const WorkflowNameBaseInfoDialog = ({ trigger }: WorkflowNameEditDialogProps) => { - const { setBaseInfo, workflow } = useWorkflowStore(useShallow(selectState)); + const { setBaseInfo, workflow } = useWorkflowStore(useZustandShallowSelector(["setBaseInfo", "workflow"])); const form = useForm>({ resolver: zodResolver(formSchema), diff --git a/ui/src/components/workflow/WorkflowLog.tsx b/ui/src/components/workflow/WorkflowLog.tsx index c2b117d1..d94a3f86 100644 --- a/ui/src/components/workflow/WorkflowLog.tsx +++ b/ui/src/components/workflow/WorkflowLog.tsx @@ -3,7 +3,7 @@ import { list as logs } from "@/repository/workflowRunLog"; import { ColumnDef } from "@tanstack/react-table"; import { useState } from "react"; import { DataTable } from "./DataTable"; -import { useSearchParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { Check, X } from "lucide-react"; import WorkflowLogDetail from "./WorkflowLogDetail"; import { useTranslation } from "react-i18next"; @@ -12,8 +12,7 @@ const WorkflowLog = () => { const [data, setData] = useState([]); const [pageCount, setPageCount] = useState(0); - const [searchParams] = useSearchParams(); - const id = searchParams.get("id"); + const { id } = useParams(); const { t } = useTranslation(); diff --git a/ui/src/hooks/index.ts b/ui/src/hooks/index.ts index e90b87d9..69e1b33c 100644 --- a/ui/src/hooks/index.ts +++ b/ui/src/hooks/index.ts @@ -1,3 +1,4 @@ import useBrowserTheme from "./useBrowserTheme"; +import useZustandShallowSelector from "./useZustandShallowSelector"; -export { useBrowserTheme }; +export { useBrowserTheme, useZustandShallowSelector }; diff --git a/ui/src/hooks/useBrowserTheme.ts b/ui/src/hooks/useBrowserTheme.ts index baa6b2fd..09b83d59 100644 --- a/ui/src/hooks/useBrowserTheme.ts +++ b/ui/src/hooks/useBrowserTheme.ts @@ -1,5 +1,5 @@ import { useTheme } from "ahooks"; -export default () => { +export default function () { return useTheme({ localStorageKey: "certimate-ui-theme" }); -}; +} diff --git a/ui/src/hooks/useZustandShallowSelector.ts b/ui/src/hooks/useZustandShallowSelector.ts new file mode 100644 index 00000000..92bf83ca --- /dev/null +++ b/ui/src/hooks/useZustandShallowSelector.ts @@ -0,0 +1,18 @@ +import { pick, isArray } from "radash"; + +import { useRef } from "react"; +import { shallow } from "zustand/shallow"; + +type MaybeMany = T | readonly T[]; + +export default function (paths: MaybeMany): (state: T) => Pick { + const prev = useRef>({} as Pick); + + return (state: T) => { + if (state) { + const next = pick(state, isArray(paths) ? paths : [paths]); + return shallow(prev.current, next) ? prev.current : (prev.current = next); + } + return prev.current; + }; +} diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx index ab83d2fe..ce1860e5 100644 --- a/ui/src/pages/accesses/AccessList.tsx +++ b/ui/src/pages/accesses/AccessList.tsx @@ -18,7 +18,7 @@ const AccessList = () => { const [modalApi, ModelContextHolder] = Modal.useModal(); const [notificationApi, NotificationContextHolder] = notification.useNotification(); - const { initialized, accesses, fetchAccesses, deleteAccess } = useAccessStore(); + const { accesses, loadedAtOnce, fetchAccesses, deleteAccess } = useAccessStore(); const tableColumns: TableProps["columns"] = [ { @@ -181,7 +181,7 @@ const AccessList = () => { columns={tableColumns} dataSource={tableData} - loading={!initialized || loading} + loading={!loadedAtOnce || loading} locale={{ emptyText: , }} diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 3e68b5b3..de80ddc5 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -120,7 +120,7 @@ const CertificateList = () => { type="secondary" ellipsis onClick={() => { - navigate(`/workflows/detail?id=${workflowId}`); + navigate(`/workflows/${workflowId}`); }} > {record.expand?.workflow?.name ?? ""} diff --git a/ui/src/pages/settings/SettingsNotification.tsx b/ui/src/pages/settings/SettingsNotification.tsx index 555af3a1..7ac8aa31 100644 --- a/ui/src/pages/settings/SettingsNotification.tsx +++ b/ui/src/pages/settings/SettingsNotification.tsx @@ -8,7 +8,7 @@ import { useNotifyChannelStore } from "@/stores/notify"; const SettingsNotification = () => { const { t } = useTranslation(); - const { initialized } = useNotifyChannelStore(); + const { loadedAtOnce } = useNotifyChannelStore(); return (
@@ -20,7 +20,7 @@ const SettingsNotification = () => { - +
diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 5eb5e2f7..a1c7d5e1 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -1,8 +1,7 @@ import { useEffect, useMemo, useState } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Button, message, notification, Switch } from "antd"; -import { useShallow } from "zustand/shallow"; import { ArrowLeft as ArrowLeftIcon } from "lucide-react"; import Show from "@/components/Show"; @@ -12,20 +11,14 @@ import WorkflowBaseInfoEditDialog from "@/components/workflow/WorkflowBaseInfoEd import WorkflowLog from "@/components/workflow/WorkflowLog"; import WorkflowProvider from "@/components/workflow/WorkflowProvider"; import { cn } from "@/components/ui/utils"; +import { useZustandShallowSelector } from "@/hooks"; import { allNodesValidated, WorkflowNode } from "@/domain/workflow"; -import { useWorkflowStore, WorkflowState } from "@/stores/workflow"; +import { useWorkflowStore } from "@/stores/workflow"; import { run as runWorkflow } from "@/api/workflow"; -const selectState = (state: WorkflowState) => ({ - workflow: state.workflow, - init: state.init, - switchEnable: state.switchEnable, - save: state.save, -}); - const WorkflowDetail = () => { const navigate = useNavigate(); - const [searchParams] = useSearchParams(); + const { id } = useParams(); const { t } = useTranslation(); @@ -33,11 +26,10 @@ const WorkflowDetail = () => { const [_, NotificationContextHolder] = notification.useNotification(); // 3. 使用正确的选择器和 shallow 比较 - const { workflow, init, switchEnable, save } = useWorkflowStore(useShallow(selectState)); + const { workflow, init, switchEnable, save } = useWorkflowStore(useZustandShallowSelector(["workflow", "init", "switchEnable", "save"])); // 从 url 中获取 workflowId const [locId, setLocId] = useState(""); - const id = searchParams.get("id"); const [tab, setTab] = useState("workflow"); @@ -78,7 +70,7 @@ const WorkflowDetail = () => { } switchEnable(); if (!locId) { - navigate(`/workflows/detail?id=${workflow.id}`); + navigate(`/workflows/${workflow.id}`); } }; @@ -89,7 +81,7 @@ const WorkflowDetail = () => { } save(); if (!locId) { - navigate(`/workflows/detail?id=${workflow.id}`); + navigate(`/workflows/${workflow.id}`); } }; diff --git a/ui/src/pages/workflows/WorkflowList.tsx b/ui/src/pages/workflows/WorkflowList.tsx index a154c5c2..54c3c068 100644 --- a/ui/src/pages/workflows/WorkflowList.tsx +++ b/ui/src/pages/workflows/WorkflowList.tsx @@ -180,7 +180,7 @@ const WorkflowList = () => { type="link" icon={} onClick={() => { - navigate(`/workflows/detail?id=${record.id}`); + navigate(`/workflows/${record.id}`); }} /> @@ -276,7 +276,7 @@ const WorkflowList = () => { }; const handleCreateClick = () => { - navigate("/workflows/detail"); + navigate("/workflows/"); }; return ( diff --git a/ui/src/repository/access.ts b/ui/src/repository/access.ts index 085c1675..fd53d852 100644 --- a/ui/src/repository/access.ts +++ b/ui/src/repository/access.ts @@ -23,5 +23,11 @@ export const save = async (record: MaybeModelRecord) => { export const remove = async (record: MaybeModelRecordWithId) => { record = { ...record, deleted: dayjs.utc().format("YYYY-MM-DD HH:mm:ss") }; + + // TODO: 仅为兼容旧版本,后续迭代时删除 + if ("configType" in record && record.configType === "httpreq") record.configType = "acmehttpreq"; + if ("configType" in record && record.configType === "tencent") record.configType = "tencentcloud"; + if ("configType" in record && record.configType === "pdns") record.configType = "powerdns"; + await getPocketBase().collection(COLLECTION_NAME).update(record.id!, record); }; diff --git a/ui/src/repository/certificate.ts b/ui/src/repository/certificate.ts index a0418a98..7a3ac1d6 100644 --- a/ui/src/repository/certificate.ts +++ b/ui/src/repository/certificate.ts @@ -36,3 +36,9 @@ export const list = async (request: ListCertificateRequest) => { return pb.collection(COLLECTION_NAME).getList(page, perPage, options); }; + +export const remove = async (record: MaybeModelRecordWithId) => { + record = { ...record, deleted: dayjs.utc().format("YYYY-MM-DD HH:mm:ss") }; + + await getPocketBase().collection(COLLECTION_NAME).update(record.id!, record); +}; diff --git a/ui/src/repository/workflow.ts b/ui/src/repository/workflow.ts index cc8bb302..b4596d17 100644 --- a/ui/src/repository/workflow.ts +++ b/ui/src/repository/workflow.ts @@ -17,7 +17,11 @@ export const list = async (request: ListWorkflowRequest) => { const page = request.page || 1; const perPage = request.perPage || 10; - const options: RecordListOptions = { requestKey: null, sort: "-created" }; + const options: RecordListOptions = { + sort: "-created", + requestKey: null, + }; + if (request.enabled != null) { options.filter = pb.filter("enabled={:enabled}", { enabled: request.enabled }); } diff --git a/ui/src/repository/workflowRunLog.ts b/ui/src/repository/workflowRunLog.ts index 780077a6..b358d12c 100644 --- a/ui/src/repository/workflowRunLog.ts +++ b/ui/src/repository/workflowRunLog.ts @@ -1,6 +1,8 @@ import { type WorkflowRunLog } from "@/domain/workflow"; import { getPocketBase } from "./pocketbase"; +const COLLECTION_NAME = "workflow_run_log"; + export type ListWorkflowLogsRequest = { id: string; page?: number; @@ -12,7 +14,7 @@ export const list = async (request: ListWorkflowLogsRequest) => { const perPage = request.perPage || 10; return await getPocketBase() - .collection("workflow_run_log") + .collection(COLLECTION_NAME) .getList(page, perPage, { filter: getPocketBase().filter("workflow={:workflowId}", { workflowId: request.id }), sort: "-created", diff --git a/ui/src/router.tsx b/ui/src/router.tsx index 357aaf81..787a8def 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -36,7 +36,7 @@ export const router = createHashRouter([ element: , }, { - path: "/workflows/detail", + path: "/workflows/:id", element: , }, { diff --git a/ui/src/stores/access/index.ts b/ui/src/stores/access/index.ts index 290ec27f..d7c67e41 100644 --- a/ui/src/stores/access/index.ts +++ b/ui/src/stores/access/index.ts @@ -5,20 +5,23 @@ import { type AccessModel } from "@/domain/access"; import { list as listAccess, save as saveAccess, remove as removeAccess } from "@/repository/access"; export interface AccessState { - initialized: boolean; accesses: AccessModel[]; - createAccess: (access: MaybeModelRecord) => void; - updateAccess: (access: MaybeModelRecordWithId) => void; - deleteAccess: (access: MaybeModelRecordWithId) => void; + loading: boolean; + loadedAtOnce: boolean; + fetchAccesses: () => Promise; + createAccess: (access: MaybeModelRecord) => Promise; + updateAccess: (access: MaybeModelRecordWithId) => Promise; + deleteAccess: (access: MaybeModelRecordWithId) => Promise; } export const useAccessStore = create((set) => { let fetcher: Promise | null = null; // 防止多次重复请求 return { - initialized: false, accesses: [], + loading: false, + loadedAtOnce: false, createAccess: async (access) => { const record = await saveAccess(access); @@ -57,10 +60,12 @@ export const useAccessStore = create((set) => { fetcher ??= listAccess(); try { + set({ loading: true }); const accesses = await fetcher; - set({ accesses: accesses ?? [], initialized: true }); + set({ accesses: accesses ?? [], loadedAtOnce: true }); } finally { fetcher = null; + set({ loading: false }); } }, }; diff --git a/ui/src/stores/contact/index.ts b/ui/src/stores/contact/index.ts index 00889a65..e973bc38 100644 --- a/ui/src/stores/contact/index.ts +++ b/ui/src/stores/contact/index.ts @@ -5,10 +5,12 @@ import { SETTINGS_NAMES, type EmailsSettingsContent, type SettingsModel } from " import { get as getSettings, save as saveSettings } from "@/repository/settings"; export interface ContactState { - initialized: boolean; emails: string[]; - setEmails: (emails: string[]) => void; + loading: boolean; + loadedAtOnce: boolean; + fetchEmails: () => Promise; + setEmails: (emails: string[]) => Promise; } export const useContactStore = create((set) => { @@ -16,8 +18,9 @@ export const useContactStore = create((set) => { let settings: SettingsModel; // 记录当前设置的其他字段,保存回数据库时用 return { - initialized: false, emails: [], + loading: false, + loadedAtOnce: false, setEmails: async (emails) => { settings ??= await getSettings(SETTINGS_NAMES.EMAILS); @@ -32,7 +35,7 @@ export const useContactStore = create((set) => { set( produce((state: ContactState) => { state.emails = settings.content.emails; - state.initialized = true; + state.loadedAtOnce = true; }) ); }, @@ -41,10 +44,12 @@ export const useContactStore = create((set) => { fetcher ??= getSettings(SETTINGS_NAMES.EMAILS); try { + set({ loading: true }); settings = await fetcher; - set({ emails: settings.content.emails?.sort() ?? [], initialized: true }); + set({ emails: settings.content.emails?.sort() ?? [], loadedAtOnce: true }); } finally { fetcher = null; + set({ loading: false }); } }, }; diff --git a/ui/src/stores/notify/index.ts b/ui/src/stores/notify/index.ts index 86f81fe7..b2d6db58 100644 --- a/ui/src/stores/notify/index.ts +++ b/ui/src/stores/notify/index.ts @@ -5,11 +5,13 @@ import { SETTINGS_NAMES, type NotifyChannelsSettingsContent, type SettingsModel import { get as getSettings, save as saveSettings } from "@/repository/settings"; export interface NotifyChannelState { - initialized: boolean; channels: NotifyChannelsSettingsContent; - setChannel: (channel: keyof NotifyChannelsSettingsContent, config: NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent]) => void; - setChannels: (channels: NotifyChannelsSettingsContent) => void; + loading: boolean; + loadedAtOnce: boolean; + fetchChannels: () => Promise; + setChannel: (channel: keyof NotifyChannelsSettingsContent, config: NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent]) => Promise; + setChannels: (channels: NotifyChannelsSettingsContent) => Promise; } export const useNotifyChannelStore = create((set, get) => { @@ -17,8 +19,9 @@ export const useNotifyChannelStore = create((set, get) => { let settings: SettingsModel; // 记录当前设置的其他字段,保存回数据库时用 return { - initialized: false, channels: {}, + loading: false, + loadedAtOnce: false, setChannel: async (channel, config) => { settings ??= await getSettings(SETTINGS_NAMES.NOTIFY_CHANNELS); @@ -40,7 +43,7 @@ export const useNotifyChannelStore = create((set, get) => { set( produce((state: NotifyChannelState) => { state.channels = settings.content; - state.initialized = true; + state.loadedAtOnce = true; }) ); }, @@ -49,10 +52,12 @@ export const useNotifyChannelStore = create((set, get) => { fetcher ??= getSettings(SETTINGS_NAMES.NOTIFY_CHANNELS); try { + set({ loading: true }); settings = await fetcher; - set({ channels: settings.content ?? {}, initialized: true }); + set({ channels: settings.content ?? {}, loadedAtOnce: true }); } finally { fetcher = null; + set({ loading: false }); } }, };