/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import { useEffect, useState } from "react"; import { observer } from "mobx-react"; import { Bell, BellOff } from "lucide-react"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { Loader } from "@plane/ui"; import { cn } from "@plane/utils"; import { IssueService } from "@/services/issue/issue.service"; const issueService = new IssueService(); type TSubscriptionIdentity = { workspaceSlug: string; projectId: string; issueId: string; }; type Props = TSubscriptionIdentity & { buttonClassName?: string; }; export const useExternalContourSubscription = ({ workspaceSlug, projectId, issueId }: TSubscriptionIdentity) => { const [isSubscribed, setIsSubscribed] = useState(undefined); const [loading, setLoading] = useState(false); useEffect(() => { let isMounted = true; const fetchSubscription = async () => { try { const response = await issueService.getIssueNotificationSubscriptionStatus(workspaceSlug, projectId, issueId); if (isMounted) setIsSubscribed(response?.subscribed ?? false); } catch { if (isMounted) setIsSubscribed(false); } }; if (workspaceSlug && projectId && issueId) { void fetchSubscription(); } return () => { isMounted = false; }; }, [workspaceSlug, projectId, issueId]); const toggleSubscription = async () => { if (!workspaceSlug || !projectId || !issueId) return; const nextValue = !isSubscribed; setLoading(true); setIsSubscribed(nextValue); try { if (nextValue) { await issueService.subscribeToIssueNotifications(workspaceSlug, projectId, issueId); } else { await issueService.unsubscribeFromIssueNotifications(workspaceSlug, projectId, issueId); } } catch { setIsSubscribed(!nextValue); throw new Error("subscription-toggle-failed"); } finally { setLoading(false); } }; return { isSubscribed, loading, toggleSubscription, }; }; type TExternalContourSubscriptionButtonProps = { isSubscribed: boolean | undefined; loading: boolean; onToggle: () => Promise; buttonClassName?: string; iconOnly?: boolean; }; export const ExternalContourSubscriptionButton = observer(function ExternalContourSubscriptionButton( props: TExternalContourSubscriptionButtonProps ) { const { isSubscribed, loading, onToggle, buttonClassName, iconOnly = false } = props; const { t } = useTranslation(); if (isSubscribed === undefined) { return ( ); } return ( ); }); export const ExternalContourSubscription = observer(function ExternalContourSubscription(props: Props) { const { workspaceSlug, projectId, issueId, buttonClassName } = props; const { t } = useTranslation(); const { isSubscribed, loading, toggleSubscription } = useExternalContourSubscription({ workspaceSlug, projectId, issueId, }); const handleToggle = async () => { try { const nextValue = !isSubscribed; await toggleSubscription(); setToast({ type: TOAST_TYPE.SUCCESS, title: t("toast.success"), message: nextValue ? t("issue.subscription.actions.subscribed") : t("issue.subscription.actions.unsubscribed"), }); } catch { setToast({ type: TOAST_TYPE.ERROR, title: t("toast.error"), message: t("common.error.message"), }); } }; return ( ); });