Files
calendar/apps/web/modules/feature-opt-in/hooks/useTeamFeatureOptIn.ts
T

129 lines
4.6 KiB
TypeScript

"use client";
import type { NormalizedFeature, UseFeatureOptInResult } from "@calcom/features/feature-opt-in/types";
import type { FeatureState } from "@calcom/features/flags/config";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { showToast } from "@calcom/ui/components/toast";
import type { TFunction } from "i18next";
import { useCallback, useMemo } from "react";
type FeatureBlockingState = { orgState?: FeatureState };
type TeamFeatureData = {
featureId: string;
globalEnabled: boolean;
teamState: FeatureState;
orgState: FeatureState;
};
function useMutationCallbacks(onSuccessCallback: () => void): { onSuccess: () => void; onError: () => void } {
const { t } = useLocale();
return useMemo(
() => ({
onSuccess: (): void => {
onSuccessCallback();
showToast(t("settings_updated_successfully"), "success");
},
onError: (): void => {
showToast(t("error_updating_settings"), "error");
},
}),
[onSuccessCallback, t]
);
}
function normalizeTeamFeatures(data: TeamFeatureData[] | undefined): NormalizedFeature[] {
return (data ?? []).map((feature) => ({
slug: feature.featureId,
globalEnabled: feature.globalEnabled,
currentState: feature.teamState,
}));
}
function buildBlockingStateMap(data: TeamFeatureData[] | undefined): Map<string, FeatureBlockingState> {
const map = new Map<string, FeatureBlockingState>();
for (const feature of data ?? []) {
map.set(feature.featureId, { orgState: feature.orgState });
}
return map;
}
function createTeamBlockedWarningFn(
blockingStateMap: Map<string, FeatureBlockingState>,
t: TFunction
): (feature: NormalizedFeature) => string | null {
return (feature: NormalizedFeature): string | null => {
const blockingState = blockingStateMap.get(feature.slug);
if (blockingState?.orgState === "disabled") {
return t("feature_org_disabled");
}
return null;
};
}
function createTeamIsBlockedByHigherLevelFn(
blockingStateMap: Map<string, FeatureBlockingState>
): (feature: NormalizedFeature) => boolean {
return (feature: NormalizedFeature): boolean => {
const blockingState = blockingStateMap.get(feature.slug);
return blockingState?.orgState === "disabled";
};
}
/**
* Hook for managing feature opt-in at the team level.
*/
export function useTeamFeatureOptIn(teamId: number): UseFeatureOptInResult {
const { t } = useLocale();
const utils = trpc.useUtils();
const featuresQuery = trpc.viewer.featureOptIn.listForTeam.useQuery(
{ teamId },
{ refetchOnWindowFocus: false }
);
const autoOptInQuery = trpc.viewer.featureOptIn.getTeamAutoOptIn.useQuery(
{ teamId },
{ refetchOnWindowFocus: false }
);
const invalidateFeatures = useCallback(
() => utils.viewer.featureOptIn.listForTeam.invalidate({ teamId }),
[utils, teamId]
);
const invalidateFeaturesAndAutoOptIn = useCallback(() => {
utils.viewer.featureOptIn.getTeamAutoOptIn.invalidate({ teamId });
utils.viewer.featureOptIn.listForTeam.invalidate({ teamId });
}, [utils, teamId]);
const setStateMutationCallbacks = useMutationCallbacks(invalidateFeatures);
const setAutoOptInMutationCallbacks = useMutationCallbacks(invalidateFeaturesAndAutoOptIn);
const setStateMutation = trpc.viewer.featureOptIn.setTeamState.useMutation(setStateMutationCallbacks);
const setAutoOptInMutation = trpc.viewer.featureOptIn.setTeamAutoOptIn.useMutation(
setAutoOptInMutationCallbacks
);
const featureBlockingState = useMemo(() => buildBlockingStateMap(featuresQuery.data), [featuresQuery.data]);
const features = normalizeTeamFeatures(featuresQuery.data);
const setFeatureState = (slug: string, state: FeatureState): void =>
setStateMutation.mutate({ teamId, slug, state });
const setAutoOptIn = (checked: boolean): void =>
setAutoOptInMutation.mutate({ teamId, autoOptIn: checked });
const getBlockedWarning = createTeamBlockedWarningFn(featureBlockingState, t);
const isBlockedByHigherLevel = createTeamIsBlockedByHigherLevelFn(featureBlockingState);
return {
features,
autoOptIn: autoOptInQuery.data?.autoOptIn ?? false,
isLoading: featuresQuery.isLoading || autoOptInQuery.isLoading,
setFeatureState,
setAutoOptIn,
isStateMutationPending: setStateMutation.isPending,
isAutoOptInMutationPending: setAutoOptInMutation.isPending,
toggleLabels: { enabled: t("enable"), disabled: t("disable"), inherit: t("let_users_decide") },
autoOptInDescription: t("auto_opt_in_experimental_description_team"),
getBlockedWarning,
isBlockedByHigherLevel,
};
}