Skip to content

Commit

Permalink
Add mutatePrefix and mutateSuffix
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-tey committed Jan 5, 2025
1 parent e933f60 commit f78f10b
Show file tree
Hide file tree
Showing 26 changed files with 135 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function PayoutStats() {
text="Confirm payouts"
className="h-7 w-fit px-2"
onClick={() => setIsOpen(true)}
disabled={confirmButtonDisabled}
disabled={eligiblePayoutsLoading || confirmButtonDisabled}
disabledTooltip={
confirmButtonDisabled ? (
"You have no pending payouts that match the minimum payout requirement for partners that have payouts enabled."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export default function WorkspaceDomainsClient() {
)
) : (
<ul className="grid grid-cols-1 gap-3">
{Array.from({ length: 10 }).map((_, idx) => (
{Array.from({ length: 5 }).map((_, idx) => (
<li key={idx}>
<DomainCardPlaceholder />
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { TagProps } from "@/lib/types";
import TagBadge from "@/ui/links/tag-badge";
Expand All @@ -17,7 +18,6 @@ import { cn, nFormatter, pluralize } from "@dub/utils";
import Link from "next/link";
import { useContext, useState } from "react";
import { toast } from "sonner";
import { mutate } from "swr";
import { TagsListContext } from "./page-client";

export function TagCard({
Expand Down Expand Up @@ -64,16 +64,8 @@ export function TagCard({
.then(async (res) => {
if (res.ok) {
await Promise.all([
mutate(
(key) => typeof key === "string" && key.startsWith("/api/tags"),
undefined,
{ revalidate: true },
),
mutate(
(key) => typeof key === "string" && key.startsWith("/api/links"),
undefined,
{ revalidate: true },
),
mutatePrefix("/api/tags"),
mutatePrefix("/api/links"),
]);
toast.success("Tag deleted");
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { DestinationUrlInput } from "@/ui/links/destination-url-input";
import { ShortLinkInput } from "@/ui/links/short-link-input";
Expand All @@ -12,7 +13,6 @@ import posthog from "posthog-js";
import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { toast } from "sonner";
import { mutate } from "swr";
import { useDebounce } from "use-debounce";
import { LaterButton } from "../../later-button";
import { useOnboardingProgress } from "../../use-onboarding-progress";
Expand Down Expand Up @@ -130,11 +130,7 @@ export function Form() {
throw new Error(error);
}

await mutate(
(key) => typeof key === "string" && key.startsWith("/api/links"),
undefined,
{ revalidate: true },
);
await mutatePrefix("/api/links");
const result = await res.json();
posthog.capture("link_created", result);

Expand Down
27 changes: 27 additions & 0 deletions apps/web/lib/swr/mutate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { mutate } from "swr";

export const mutatePrefix = async (prefix: string | string[]) => {
await mutate(
(key) =>
typeof key === "string" &&
(Array.isArray(prefix)
? prefix.some((p) => key.startsWith(p))
: key.startsWith(prefix)),
undefined,
{ revalidate: true },
);
};

export const mutateSuffix = async (suffix: string | string[]) => {
await mutate(
(key) =>
typeof key === "string" &&
(Array.isArray(suffix)
? suffix.some((s) => key.endsWith(s))
: key.endsWith(suffix)),
undefined,
{
revalidate: true,
},
);
};
9 changes: 7 additions & 2 deletions apps/web/lib/swr/use-workspace.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { ExpandedWorkspaceProps } from "@/lib/types";
import { PRO_PLAN, fetcher, getNextPlan } from "@dub/utils";
import { useParams, useSearchParams } from "next/navigation";
import useSWR from "swr";
import useSWR, { SWRConfiguration } from "swr";

export default function useWorkspace() {
export default function useWorkspace({
swrOpts,
}: {
swrOpts?: SWRConfiguration;
} = {}) {
let { slug } = useParams() as { slug: string | null };
const searchParams = useSearchParams();
if (!slug) {
Expand All @@ -19,6 +23,7 @@ export default function useWorkspace() {
fetcher,
{
dedupingInterval: 60000,
...swrOpts,
},
);

Expand Down
12 changes: 3 additions & 9 deletions apps/web/ui/domains/add-edit-domain-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isValidDomain } from "@/lib/api/domains";
import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { DomainProps } from "@/lib/types";
import { createDomainBodySchema } from "@/lib/zod/schemas/domains";
Expand Down Expand Up @@ -29,7 +30,6 @@ import posthog from "posthog-js";
import { useMemo, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { toast } from "sonner";
import { mutate } from "swr";
import { useDebouncedCallback } from "use-debounce";
import { z } from "zod";
import { QRCode } from "../shared/qr-code";
Expand Down Expand Up @@ -165,14 +165,8 @@ export function AddEditDomainForm({

if (res.ok) {
await Promise.all([
mutate(
(key) => typeof key === "string" && key.startsWith("/api/domains"),
),
mutate(
(key) => typeof key === "string" && key.startsWith("/api/links"),
undefined,
{ revalidate: true },
),
mutatePrefix("/api/domains"),
mutatePrefix("/api/links"),
]);
const data = await res.json();
posthog.capture(props ? "domain_updated" : "domain_created", data);
Expand Down
15 changes: 5 additions & 10 deletions apps/web/ui/domains/register-domain-form.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import {
AnimatedSizeContainer,
Expand All @@ -13,7 +14,6 @@ import { CircleCheck, Star } from "lucide-react";
import Link from "next/link";
import { FormEvent, useEffect, useState } from "react";
import { toast } from "sonner";
import { mutate } from "swr";
import { useDebounce } from "use-debounce";
import { AlertCircleFill, CheckCircleFill } from "../shared/icons";
import { ProBadgeTooltip } from "../shared/pro-badge-tooltip";
Expand Down Expand Up @@ -107,15 +107,10 @@ export function RegisterDomainForm({
toast.success("Domain registered successfully!");

// Mutate workspace, domains, and links
await Promise.all([
mutate(`/api/workspaces/${workspace.slug}`),
mutate(
(key) =>
typeof key === "string" &&
(key.startsWith("/api/domains") || key.startsWith("/api/links")),
undefined,
{ revalidate: true },
),
await mutatePrefix([
`/api/workspaces/${workspace.slug}`,
"/api/domains",
"/api/links",
]);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web/ui/layout/sidebar/usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function UsageInner() {
slug,
paymentFailedAt,
loading,
} = useWorkspace();
} = useWorkspace({ swrOpts: { keepPreviousData: true } });

const [billingEnd] = useMemo(() => {
if (billingCycleStart) {
Expand Down
9 changes: 2 additions & 7 deletions apps/web/ui/links/link-controls.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import { useArchiveLinkModal } from "@/ui/modals/archive-link-modal";
import { useDeleteLinkModal } from "@/ui/modals/delete-link-modal";
import {
Expand All @@ -16,7 +17,6 @@ import { CopyPlus, Delete, FolderInput } from "lucide-react";
import { useParams } from "next/navigation";
import { useContext } from "react";
import { toast } from "sonner";
import { mutate } from "swr";
import { useLinkBuilder } from "../modals/link-builder";
import { useLinkQRModal } from "../modals/link-qr-modal";
import { useTransferLinkModal } from "../modals/transfer-link-modal";
Expand Down Expand Up @@ -90,12 +90,7 @@ export function LinkControls({ link }: { link: ResponseLink }) {
fetch(`/api/admin/links/ban?domain=${link.domain}&key=${link.key}`, {
method: "DELETE",
}).then(async () => {
await mutate(
(key) =>
typeof key === "string" && key.startsWith("/api/admin/links"),
undefined,
{ revalidate: true },
);
await mutatePrefix("/api/admin/links");
}),
{
loading: "Banning link...",
Expand Down
7 changes: 2 additions & 5 deletions apps/web/ui/modals/accept-invite-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { Button, Logo, Modal } from "@dub/ui";
import { useSession } from "next-auth/react";
Expand All @@ -11,7 +12,6 @@ import {
useState,
} from "react";
import { toast } from "sonner";
import { mutate } from "swr";

function AcceptInviteModal({
showAcceptInviteModal,
Expand Down Expand Up @@ -61,10 +61,7 @@ function AcceptInviteModal({
posthog.capture("accepted_workspace_invite", {
workspace: slug,
});
await Promise.all([
mutate("/api/workspaces"),
mutate(`/api/workspaces/${slug}`),
]);
await mutatePrefix("/api/workspaces");
router.replace(`/${slug}`);
setShowAcceptInviteModal(false);
toast.success("You now are a part of this workspace!");
Expand Down
17 changes: 2 additions & 15 deletions apps/web/ui/modals/add-edit-tag-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import useTags from "@/lib/swr/use-tags";
import useWorkspace from "@/lib/swr/use-workspace";
import { TagColorProps, TagProps } from "@/lib/types";
Expand All @@ -23,7 +24,6 @@ import {
useState,
} from "react";
import { toast } from "sonner";
import { mutate } from "swr";
import { COLORS_LIST, randomBadgeColor } from "../links/tag-badge";

function AddEditTagModal({
Expand Down Expand Up @@ -122,20 +122,7 @@ function AddEditTagModal({
tag_name: data.name,
tag_color: data.color,
});
await Promise.all([
mutate(
(key) =>
typeof key === "string" && key.startsWith("/api/tags"),
undefined,
{ revalidate: true },
),
mutate(
(key) =>
typeof key === "string" && key.startsWith("/api/links"),
undefined,
{ revalidate: true },
),
]);
await mutatePrefix(["/api/tags", "/api/links"]);
toast.success(endpoint.successMessage);
setShowAddEditTagModal(false);
} else {
Expand Down
14 changes: 3 additions & 11 deletions apps/web/ui/modals/archive-domain-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { DomainProps } from "@/lib/types";
import { Button, LinkLogo, Modal, useToastWithUndo } from "@dub/ui";
Expand All @@ -10,7 +11,6 @@ import {
useState,
} from "react";
import { toast } from "sonner";
import { mutate } from "swr";

const sendArchiveRequest = ({
domain,
Expand All @@ -31,14 +31,6 @@ const sendArchiveRequest = ({
});
};

const revalidateDomains = () => {
return mutate(
(key) => typeof key === "string" && key.startsWith("/api/domains"),
undefined,
{ revalidate: true },
);
};

function ArchiveDomainModal({
showArchiveDomainModal,
setShowArchiveDomainModal,
Expand Down Expand Up @@ -71,7 +63,7 @@ function ArchiveDomainModal({
return;
}

revalidateDomains();
mutatePrefix("/api/domains");
setShowArchiveDomainModal(false);
toastWithUndo({
id: "domain-archive-undo-toast",
Expand All @@ -92,7 +84,7 @@ function ArchiveDomainModal({
loading: "Undo in progress...",
error: "Failed to roll back changes. An error occurred.",
success: () => {
revalidateDomains();
mutatePrefix("/api/domains");
return "Undo successful! Changes reverted.";
},
},
Expand Down
14 changes: 3 additions & 11 deletions apps/web/ui/modals/archive-link-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mutatePrefix } from "@/lib/swr/mutate";
import useWorkspace from "@/lib/swr/use-workspace";
import { LinkProps } from "@/lib/types";
import { Button, LinkLogo, Modal, useToastWithUndo } from "@dub/ui";
Expand All @@ -11,7 +12,6 @@ import {
useState,
} from "react";
import { toast } from "sonner";
import { mutate } from "swr";

const sendArchiveRequest = ({
linkId,
Expand All @@ -31,14 +31,6 @@ const sendArchiveRequest = ({
});
};

const revalidateLinks = () => {
return mutate(
(key) => typeof key === "string" && key.startsWith("/api/links"),
undefined,
{ revalidate: true },
);
};

type ArchiveLinkModalProps = {
showArchiveLinkModal: boolean;
setShowArchiveLinkModal: Dispatch<SetStateAction<boolean>>;
Expand Down Expand Up @@ -93,7 +85,7 @@ function ArchiveLinkModalInner({
return;
}

revalidateLinks();
mutatePrefix("/api/links");
setShowArchiveLinkModal(false);
toastWithUndo({
id: "link-archive-undo-toast",
Expand All @@ -114,7 +106,7 @@ function ArchiveLinkModalInner({
loading: "Undo in progress...",
error: "Failed to roll back changes. An error occurred.",
success: () => {
revalidateLinks();
mutatePrefix("/api/links");
return "Undo successful! Changes reverted.";
},
},
Expand Down
Loading

0 comments on commit f78f10b

Please sign in to comment.