mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2025-11-28 18:28:02 +08:00
refactor: extract subscription plan logic into reusable hook
This commit is contained in:
68
src/components/app/hooks/useSubscriptionPlan.ts
Normal file
68
src/components/app/hooks/useSubscriptionPlan.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Subscription, SubscriptionPlan } from '@/application/types';
|
||||||
|
import { isOfficialHost } from '@/utils/subscription';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to manage subscription plan loading and Pro feature detection
|
||||||
|
* Only loads subscription for official hosts (self-hosted instances have Pro features enabled by default)
|
||||||
|
*
|
||||||
|
* @param getSubscriptions - Function to fetch subscriptions (can be undefined)
|
||||||
|
* @returns Object containing activeSubscriptionPlan and isPro flag
|
||||||
|
*/
|
||||||
|
export function useSubscriptionPlan(
|
||||||
|
getSubscriptions?: () => Promise<Subscription[] | undefined>
|
||||||
|
): {
|
||||||
|
activeSubscriptionPlan: SubscriptionPlan | null;
|
||||||
|
isPro: boolean;
|
||||||
|
} {
|
||||||
|
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
||||||
|
// Pro features are enabled by default on self-hosted instances
|
||||||
|
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
||||||
|
|
||||||
|
const loadSubscription = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
if (!getSubscriptions) {
|
||||||
|
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptions = await getSubscriptions();
|
||||||
|
|
||||||
|
if (!subscriptions || subscriptions.length === 0) {
|
||||||
|
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscription = subscriptions[0];
|
||||||
|
|
||||||
|
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
||||||
|
} catch (e: any) {
|
||||||
|
// Silently handle expected errors (API not initialized, no response data, etc.)
|
||||||
|
// These are normal scenarios when the service isn't available or there's no subscription data
|
||||||
|
const isExpectedError =
|
||||||
|
e?.code === -1 &&
|
||||||
|
(e?.message === 'No response data received' ||
|
||||||
|
e?.message === 'No response received from server' ||
|
||||||
|
e?.message === 'API service not initialized');
|
||||||
|
|
||||||
|
if (!isExpectedError) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
||||||
|
}
|
||||||
|
}, [getSubscriptions]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Only load subscription for official host (self-hosted instances have Pro features enabled by default)
|
||||||
|
if (isOfficialHost()) {
|
||||||
|
void loadSubscription();
|
||||||
|
}
|
||||||
|
}, [loadSubscription]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeSubscriptionPlan,
|
||||||
|
isPro,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,24 +1,23 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { SelectOption, SelectOptionColor, useDatabaseContext } from '@/application/database-yjs';
|
import { SelectOption, SelectOptionColor, useDatabaseContext } from '@/application/database-yjs';
|
||||||
import { useDeleteSelectOption, useUpdateSelectOption } from '@/application/database-yjs/dispatch';
|
import { useDeleteSelectOption, useUpdateSelectOption } from '@/application/database-yjs/dispatch';
|
||||||
import { SubscriptionPlan } from '@/application/types';
|
|
||||||
import { ReactComponent as DeleteIcon } from '@/assets/icons/delete.svg';
|
import { ReactComponent as DeleteIcon } from '@/assets/icons/delete.svg';
|
||||||
import { ColorTile } from '@/components/_shared/color-picker';
|
import { ColorTile } from '@/components/_shared/color-picker';
|
||||||
|
import { useSubscriptionPlan } from '@/components/app/hooks/useSubscriptionPlan';
|
||||||
import { SelectOptionColorMap } from '@/components/database/components/cell/cell.const';
|
import { SelectOptionColorMap } from '@/components/database/components/cell/cell.const';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
DropdownMenuGroup,
|
DropdownMenuGroup,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
DropdownMenuContent,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { isOfficialHost } from '@/utils/subscription';
|
|
||||||
|
|
||||||
function OptionMenu({
|
function OptionMenu({
|
||||||
open,
|
open,
|
||||||
@@ -37,31 +36,7 @@ function OptionMenu({
|
|||||||
const onDelete = useDeleteSelectOption(fieldId);
|
const onDelete = useDeleteSelectOption(fieldId);
|
||||||
const onUpdate = useUpdateSelectOption(fieldId);
|
const onUpdate = useUpdateSelectOption(fieldId);
|
||||||
|
|
||||||
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
const { isPro } = useSubscriptionPlan(getSubscriptions);
|
||||||
// Pro features are enabled by default on self-hosted instances
|
|
||||||
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const subscriptions = await getSubscriptions?.();
|
|
||||||
|
|
||||||
if (!subscriptions || subscriptions.length === 0) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = subscriptions[0];
|
|
||||||
|
|
||||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
|
||||||
} catch (e) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}, [getSubscriptions]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadSubscription();
|
|
||||||
}, [loadSubscription]);
|
|
||||||
|
|
||||||
const colors = useMemo(() => {
|
const colors = useMemo(() => {
|
||||||
const baseColors = [
|
const baseColors = [
|
||||||
|
|||||||
@@ -4,16 +4,15 @@ import { useSlateStatic } from 'slate-react';
|
|||||||
|
|
||||||
import { YjsEditor } from '@/application/slate-yjs';
|
import { YjsEditor } from '@/application/slate-yjs';
|
||||||
import { CustomEditor } from '@/application/slate-yjs/command';
|
import { CustomEditor } from '@/application/slate-yjs/command';
|
||||||
import { SubscriptionPlan } from '@/application/types';
|
|
||||||
import { ReactComponent as ChevronRightIcon } from '@/assets/icons/alt_arrow_right.svg';
|
import { ReactComponent as ChevronRightIcon } from '@/assets/icons/alt_arrow_right.svg';
|
||||||
import { ColorTile } from '@/components/_shared/color-picker';
|
import { ColorTile } from '@/components/_shared/color-picker';
|
||||||
import { Origins, Popover } from '@/components/_shared/popover';
|
import { Origins, Popover } from '@/components/_shared/popover';
|
||||||
|
import { useSubscriptionPlan } from '@/components/app/hooks/useSubscriptionPlan';
|
||||||
import { CalloutNode } from '@/components/editor/editor.type';
|
import { CalloutNode } from '@/components/editor/editor.type';
|
||||||
import { useEditorContext } from '@/components/editor/EditorContext';
|
import { useEditorContext } from '@/components/editor/EditorContext';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { ColorEnum, renderColor, toBlockColor, toTint } from '@/utils/color';
|
import { ColorEnum, renderColor, toBlockColor, toTint } from '@/utils/color';
|
||||||
import { isOfficialHost } from '@/utils/subscription';
|
|
||||||
|
|
||||||
const origins: Origins = {
|
const origins: Origins = {
|
||||||
anchorOrigin: {
|
anchorOrigin: {
|
||||||
@@ -46,31 +45,7 @@ function CalloutTextColor({ node, onSelectColor }: { node: CalloutNode; onSelect
|
|||||||
const [originalColor, setOriginalColor] = useState<string>(node.data?.textColor || '');
|
const [originalColor, setOriginalColor] = useState<string>(node.data?.textColor || '');
|
||||||
const selectedColor = originalColor || ColorEnum.BlockTextColor10;
|
const selectedColor = originalColor || ColorEnum.BlockTextColor10;
|
||||||
|
|
||||||
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
const { isPro } = useSubscriptionPlan(getSubscriptions);
|
||||||
// Pro features are enabled by default on self-hosted instances
|
|
||||||
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const subscriptions = await getSubscriptions?.();
|
|
||||||
|
|
||||||
if (!subscriptions || subscriptions.length === 0) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = subscriptions[0];
|
|
||||||
|
|
||||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
|
||||||
} catch (e) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}, [getSubscriptions]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadSubscription();
|
|
||||||
}, [loadSubscription]);
|
|
||||||
|
|
||||||
const builtinColors = useMemo(() => {
|
const builtinColors = useMemo(() => {
|
||||||
const proPalette = [
|
const proPalette = [
|
||||||
|
|||||||
@@ -5,17 +5,16 @@ import { useSlateStatic } from 'slate-react';
|
|||||||
import { YjsEditor } from '@/application/slate-yjs';
|
import { YjsEditor } from '@/application/slate-yjs';
|
||||||
import { CustomEditor } from '@/application/slate-yjs/command';
|
import { CustomEditor } from '@/application/slate-yjs/command';
|
||||||
import { EditorMarkFormat } from '@/application/slate-yjs/types';
|
import { EditorMarkFormat } from '@/application/slate-yjs/types';
|
||||||
import { SubscriptionPlan } from '@/application/types';
|
|
||||||
import { ReactComponent as ColorSvg } from '@/assets/icons/text_highlight.svg';
|
import { ReactComponent as ColorSvg } from '@/assets/icons/text_highlight.svg';
|
||||||
import { ColorTile } from '@/components/_shared/color-picker';
|
import { ColorTile } from '@/components/_shared/color-picker';
|
||||||
import { CustomColorPicker } from '@/components/_shared/color-picker/CustomColorPicker';
|
import { CustomColorPicker } from '@/components/_shared/color-picker/CustomColorPicker';
|
||||||
|
import { useSubscriptionPlan } from '@/components/app/hooks/useSubscriptionPlan';
|
||||||
import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks';
|
import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks';
|
||||||
import { useEditorContext } from '@/components/editor/EditorContext';
|
import { useEditorContext } from '@/components/editor/EditorContext';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { renderColor } from '@/utils/color';
|
import { renderColor } from '@/utils/color';
|
||||||
import { isOfficialHost } from '@/utils/subscription';
|
|
||||||
|
|
||||||
import ActionButton from './ActionButton';
|
import ActionButton from './ActionButton';
|
||||||
import { CreateCustomColorTile } from './TextColor';
|
import { CreateCustomColorTile } from './TextColor';
|
||||||
@@ -44,29 +43,9 @@ function BgColor({
|
|||||||
const recentColorToSave = useRef<string | null>(null);
|
const recentColorToSave = useRef<string | null>(null);
|
||||||
const initialColor = useRef<string | null>(null);
|
const initialColor = useRef<string | null>(null);
|
||||||
|
|
||||||
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
const { isPro } = useSubscriptionPlan(getSubscriptions);
|
||||||
// Pro features are enabled by default on self-hosted instances
|
|
||||||
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
|
||||||
const maxCustomColors = isPro ? 9 : 4;
|
const maxCustomColors = isPro ? 9 : 4;
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const subscriptions = await getSubscriptions?.();
|
|
||||||
|
|
||||||
if (!subscriptions || subscriptions.length === 0) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = subscriptions[0];
|
|
||||||
|
|
||||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
|
||||||
} catch (e) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}, [getSubscriptions]);
|
|
||||||
|
|
||||||
const isCustomColor = useCallback((color: string) => {
|
const isCustomColor = useCallback((color: string) => {
|
||||||
return color.startsWith('#') || color.startsWith('0x');
|
return color.startsWith('#') || color.startsWith('0x');
|
||||||
}, []);
|
}, []);
|
||||||
@@ -103,10 +82,6 @@ function BgColor({
|
|||||||
}
|
}
|
||||||
}, [isOpen, visible]);
|
}, [isOpen, visible]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadSubscription();
|
|
||||||
}, [loadSubscription]);
|
|
||||||
|
|
||||||
const getRawColorValue = useCallback(
|
const getRawColorValue = useCallback(
|
||||||
(color: string) => {
|
(color: string) => {
|
||||||
if (isCustomColor(color)) {
|
if (isCustomColor(color)) {
|
||||||
|
|||||||
@@ -5,18 +5,17 @@ import { useSlateStatic } from 'slate-react';
|
|||||||
import { YjsEditor } from '@/application/slate-yjs';
|
import { YjsEditor } from '@/application/slate-yjs';
|
||||||
import { CustomEditor } from '@/application/slate-yjs/command';
|
import { CustomEditor } from '@/application/slate-yjs/command';
|
||||||
import { EditorMarkFormat } from '@/application/slate-yjs/types';
|
import { EditorMarkFormat } from '@/application/slate-yjs/types';
|
||||||
import { SubscriptionPlan } from '@/application/types';
|
|
||||||
import { ReactComponent as AddIcon } from '@/assets/icons/plus.svg';
|
import { ReactComponent as AddIcon } from '@/assets/icons/plus.svg';
|
||||||
import { ReactComponent as ColorSvg } from '@/assets/icons/text_color.svg';
|
import { ReactComponent as ColorSvg } from '@/assets/icons/text_color.svg';
|
||||||
import { ColorTile } from '@/components/_shared/color-picker';
|
import { ColorTile } from '@/components/_shared/color-picker';
|
||||||
import { CustomColorPicker } from '@/components/_shared/color-picker/CustomColorPicker';
|
import { CustomColorPicker } from '@/components/_shared/color-picker/CustomColorPicker';
|
||||||
|
import { useSubscriptionPlan } from '@/components/app/hooks/useSubscriptionPlan';
|
||||||
import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks';
|
import { useSelectionToolbarContext } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks';
|
||||||
import { useEditorContext } from '@/components/editor/EditorContext';
|
import { useEditorContext } from '@/components/editor/EditorContext';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { renderColor } from '@/utils/color';
|
import { renderColor } from '@/utils/color';
|
||||||
import { isOfficialHost } from '@/utils/subscription';
|
|
||||||
|
|
||||||
import ActionButton from './ActionButton';
|
import ActionButton from './ActionButton';
|
||||||
|
|
||||||
@@ -44,29 +43,9 @@ function TextColor({
|
|||||||
const recentColorToSave = useRef<string | null>(null);
|
const recentColorToSave = useRef<string | null>(null);
|
||||||
const initialColor = useRef<string | null>(null);
|
const initialColor = useRef<string | null>(null);
|
||||||
|
|
||||||
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
const { isPro } = useSubscriptionPlan(getSubscriptions);
|
||||||
// Pro features are enabled by default on self-hosted instances
|
|
||||||
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
|
||||||
const maxCustomColors = isPro ? 9 : 4;
|
const maxCustomColors = isPro ? 9 : 4;
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const subscriptions = await getSubscriptions?.();
|
|
||||||
|
|
||||||
if (!subscriptions || subscriptions.length === 0) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = subscriptions[0];
|
|
||||||
|
|
||||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
|
||||||
} catch (e) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}, [getSubscriptions]);
|
|
||||||
|
|
||||||
const isCustomColor = useCallback((color: string) => {
|
const isCustomColor = useCallback((color: string) => {
|
||||||
return color.startsWith('#') || color.startsWith('0x');
|
return color.startsWith('#') || color.startsWith('0x');
|
||||||
}, []);
|
}, []);
|
||||||
@@ -103,10 +82,6 @@ function TextColor({
|
|||||||
}
|
}
|
||||||
}, [isOpen, visible]);
|
}, [isOpen, visible]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadSubscription();
|
|
||||||
}, [loadSubscription]);
|
|
||||||
|
|
||||||
const getRawColorValue = useCallback(
|
const getRawColorValue = useCallback(
|
||||||
(color: string) => {
|
(color: string) => {
|
||||||
if (isCustomColor(color)) {
|
if (isCustomColor(color)) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { CoverType, SubscriptionPlan, ViewMetaCover } from '@/application/types';
|
import { CoverType, ViewMetaCover } from '@/application/types';
|
||||||
|
import { EmbedLink, TAB_KEY, TabOption, Unsplash, UploadImage, UploadPopover } from '@/components/_shared/image-upload';
|
||||||
import { useAppHandlers, useAppViewId, useOpenModalViewId } from '@/components/app/app.hooks';
|
import { useAppHandlers, useAppViewId, useOpenModalViewId } from '@/components/app/app.hooks';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useSubscriptionPlan } from '@/components/app/hooks/useSubscriptionPlan';
|
||||||
import { EmbedLink, Unsplash, UploadPopover, TabOption, TAB_KEY, UploadImage } from '@/components/_shared/image-upload';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { isOfficialHost } from '@/utils/subscription';
|
|
||||||
import Colors from './CoverColors';
|
|
||||||
import { GradientEnum } from '@/utils/color';
|
import { GradientEnum } from '@/utils/color';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Colors from './CoverColors';
|
||||||
|
|
||||||
function CoverPopover({
|
function CoverPopover({
|
||||||
coverValue,
|
coverValue,
|
||||||
@@ -26,31 +26,7 @@ function CoverPopover({
|
|||||||
const modalViewId = useOpenModalViewId();
|
const modalViewId = useOpenModalViewId();
|
||||||
const viewId = modalViewId || appViewId;
|
const viewId = modalViewId || appViewId;
|
||||||
|
|
||||||
const [activeSubscriptionPlan, setActiveSubscriptionPlan] = useState<SubscriptionPlan | null>(null);
|
const { isPro } = useSubscriptionPlan(getSubscriptions);
|
||||||
// Pro features are enabled by default on self-hosted instances
|
|
||||||
const isPro = activeSubscriptionPlan === SubscriptionPlan.Pro || !isOfficialHost();
|
|
||||||
|
|
||||||
const loadSubscription = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const subscriptions = await getSubscriptions?.();
|
|
||||||
|
|
||||||
if (!subscriptions || subscriptions.length === 0) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscription = subscriptions[0];
|
|
||||||
|
|
||||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
|
||||||
} catch (e) {
|
|
||||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}, [getSubscriptions]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
void loadSubscription();
|
|
||||||
}, [loadSubscription]);
|
|
||||||
|
|
||||||
const tabOptions: TabOption[] = useMemo(() => {
|
const tabOptions: TabOption[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
|||||||
Reference in New Issue
Block a user