152 lines
4.7 KiB
TypeScript
152 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { IconBell, IconBellOff } from "@tabler/icons-react";
|
|
import { Button } from "@kandev/ui/button";
|
|
import {
|
|
getIssueSubscription,
|
|
getMRSubscription,
|
|
setIssueSubscription,
|
|
setMRSubscription,
|
|
} from "@/lib/api/domains/gitlab-api";
|
|
import { useToast } from "@/components/toast-provider";
|
|
import { isCurrentIdentityRequest } from "@/hooks/domains/gitlab/request-identity";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
function requestIsActive(
|
|
generation: number,
|
|
requestIdentity: string,
|
|
currentGeneration: { current: number },
|
|
currentIdentity: { current: string },
|
|
) {
|
|
return isCurrentIdentityRequest(
|
|
generation,
|
|
currentGeneration.current,
|
|
requestIdentity,
|
|
currentIdentity.current,
|
|
);
|
|
}
|
|
|
|
export function subscriptionActionLabel(
|
|
subscribed: boolean,
|
|
t: (key: string, values?: Record<string, unknown>) => string,
|
|
): string {
|
|
return subscribed
|
|
? t("gitlab:unsubscribeFromNotifications")
|
|
: t("gitlab:subscribeToNotifications");
|
|
}
|
|
|
|
type SubscriptionIdentity = {
|
|
kind?: "mr" | "issue";
|
|
workspaceId: string;
|
|
host: string;
|
|
project: string;
|
|
iid: number;
|
|
};
|
|
|
|
function useGitLabSubscription({
|
|
kind = "mr",
|
|
workspaceId,
|
|
host,
|
|
project,
|
|
iid,
|
|
}: SubscriptionIdentity) {
|
|
const { t } = useTranslation();
|
|
const [subscribed, setSubscribed] = useState(false);
|
|
const [loading, setLoading] = useState(true);
|
|
const { toast } = useToast();
|
|
const requestGeneration = useRef(0);
|
|
const identity = `${kind}\0${workspaceId}\0${host}\0${project}\0${iid}`;
|
|
const currentIdentity = useRef(identity);
|
|
currentIdentity.current = identity;
|
|
|
|
useEffect(() => {
|
|
const generation = ++requestGeneration.current;
|
|
const requestIdentity = identity;
|
|
setSubscribed(false);
|
|
setLoading(true);
|
|
const getSubscription = kind === "issue" ? getIssueSubscription : getMRSubscription;
|
|
getSubscription({ workspaceId, host, project, iid })
|
|
.then((state) => {
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
setSubscribed(state.subscribed);
|
|
}
|
|
})
|
|
.catch((error: unknown) => {
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
toast({
|
|
title: t("gitlab:notificationStatusUnavailable"),
|
|
description:
|
|
error instanceof Error ? error.message : t("gitlab:gitlabRejectedTheRequest"),
|
|
variant: "error",
|
|
});
|
|
}
|
|
})
|
|
.finally(() => {
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
setLoading(false);
|
|
}
|
|
});
|
|
return () => {
|
|
if (requestGeneration.current === generation) requestGeneration.current += 1;
|
|
};
|
|
}, [kind, workspaceId, host, project, iid, identity, toast]);
|
|
|
|
const toggle = async () => {
|
|
const generation = ++requestGeneration.current;
|
|
const requestIdentity = identity;
|
|
const next = !subscribed;
|
|
setLoading(true);
|
|
try {
|
|
const setSubscription = kind === "issue" ? setIssueSubscription : setMRSubscription;
|
|
const state = await setSubscription({ workspaceId, host, project, iid, subscribed: next });
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
setSubscribed(state.subscribed);
|
|
toast({
|
|
description: state.subscribed
|
|
? t("gitlab:subscribedToGitlabNotifications")
|
|
: t("gitlab:unsubscribedFromGitlabNotifications"),
|
|
variant: "success",
|
|
});
|
|
}
|
|
} catch (error) {
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
toast({
|
|
title: t("gitlab:notificationUpdateFailed"),
|
|
description: error instanceof Error ? error.message : t("gitlab:gitlabRejectedTheAction"),
|
|
variant: "error",
|
|
});
|
|
}
|
|
} finally {
|
|
if (requestIsActive(generation, requestIdentity, requestGeneration, currentIdentity)) {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
return { subscribed, loading, toggle };
|
|
}
|
|
|
|
export function SubscriptionToggle(identity: SubscriptionIdentity) {
|
|
const { t } = useTranslation();
|
|
const { subscribed, loading, toggle } = useGitLabSubscription(identity);
|
|
|
|
const label = subscriptionActionLabel(subscribed, t);
|
|
const Icon = subscribed ? IconBellOff : IconBell;
|
|
return (
|
|
<Button
|
|
variant="outline"
|
|
className="cursor-pointer gap-1.5"
|
|
disabled={loading}
|
|
aria-label={label}
|
|
title={label}
|
|
onClick={() => void toggle()}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
<span className="hidden lg:inline">
|
|
{subscribed ? t("gitlab:unsubscribe") : t("gitlab:subscribe")}
|
|
</span>
|
|
</Button>
|
|
);
|
|
}
|