mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2026-03-13 10:00:26 +08:00
refactor: plan logic (#217)
This commit is contained in:
@@ -71,6 +71,7 @@ import { RepeatedChatMessage } from '@/components/chat';
|
||||
import { database_blob } from '@/proto/database_blob';
|
||||
import { getAppFlowyFileUploadUrl, getAppFlowyFileUrl } from '@/utils/file-storage-url';
|
||||
import { Log } from '@/utils/log';
|
||||
import { hasProAccessFromPlans } from '@/utils/subscription';
|
||||
|
||||
export * from './gotrue';
|
||||
|
||||
@@ -1671,7 +1672,7 @@ export async function uploadFile(
|
||||
if (file.size > 7 * 1024 * 1024) {
|
||||
const plan = await getActiveSubscription(workspaceId);
|
||||
|
||||
if (plan?.length === 0 || plan?.[0] === SubscriptionPlan.Free) {
|
||||
if (!hasProAccessFromPlans(plan)) {
|
||||
notify.error('Your file is over 7 MB limit of the Free plan. Upgrade for unlimited uploads.');
|
||||
|
||||
return Promise.reject({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { Subscription, SubscriptionPlan } from '@/application/types';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { getProAccessPlanFromSubscriptions, isAppFlowyHosted } from '@/utils/subscription';
|
||||
|
||||
/**
|
||||
* Hook to manage subscription plan loading and Pro feature detection
|
||||
@@ -34,9 +34,7 @@ export function useSubscriptionPlan(
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
|
||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
||||
setActiveSubscriptionPlan(getProAccessPlanFromSubscriptions(subscriptions));
|
||||
} catch (e: unknown) {
|
||||
// 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
|
||||
@@ -67,4 +65,3 @@ export function useSubscriptionPlan(
|
||||
isPro,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { NormalModal } from '@/components/_shared/modal';
|
||||
import { useService } from '@/components/main/app.hooks';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { hasProAccessFromPlans, isAppFlowyHosted } from '@/utils/subscription';
|
||||
|
||||
const GuestLimitExceededCode = 1070;
|
||||
const REPEAT_REQUEST_CODE = 1122;
|
||||
@@ -29,7 +29,7 @@ function ApproveRequestPage() {
|
||||
|
||||
const [requestInfo, setRequestInfo] = useState<GetRequestAccessInfoResponse | null>(null);
|
||||
const [currentPlans, setCurrentPlans] = useState<SubscriptionPlan[]>([]);
|
||||
const isPro = useMemo(() => currentPlans.includes(SubscriptionPlan.Pro), [currentPlans]);
|
||||
const isPro = useMemo(() => hasProAccessFromPlans(currentPlans), [currentPlans]);
|
||||
const requestId = searchParams.get('request_id');
|
||||
const service = useService();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -13,7 +13,7 @@ import PublishedPages from '@/components/app/publish-manage/PublishedPages';
|
||||
import PublishPagesSkeleton from '@/components/app/publish-manage/PublishPagesSkeleton';
|
||||
import UpdateNamespace from '@/components/app/publish-manage/UpdateNamespace';
|
||||
import { useCurrentUser, useService } from '@/components/main/app.hooks';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { getProAccessPlanFromSubscriptions, isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { openUrl } from '@/utils/url';
|
||||
|
||||
export function PublishManage({ onClose }: { onClose?: () => void }) {
|
||||
@@ -190,9 +190,7 @@ export function PublishManage({ onClose }: { onClose?: () => void }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
|
||||
setActiveSubscription(subscription?.plan || SubscriptionPlan.Free);
|
||||
setActiveSubscription(getProAccessPlanFromSubscriptions(subscriptions));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { InviteGuest } from '@/components/app/share/InviteGuest';
|
||||
import { PeopleWithAccess } from '@/components/app/share/PeopleWithAccess';
|
||||
import { UpgradeBanner } from '@/components/app/share/UpgradeBanner';
|
||||
import { useCurrentUser, useService } from '@/components/main/app.hooks';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { getProAccessPlanFromSubscriptions, isAppFlowyHosted } from '@/utils/subscription';
|
||||
|
||||
function SharePanel({ viewId }: { viewId: string }) {
|
||||
const currentUser = useCurrentUser();
|
||||
@@ -113,9 +113,7 @@ function SharePanel({ viewId }: { viewId: string }) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
|
||||
setActiveSubscriptionPaln(subscription?.plan || SubscriptionPlan.Free);
|
||||
setActiveSubscriptionPaln(getProAccessPlanFromSubscriptions(subscriptions));
|
||||
} catch (e) {
|
||||
setActiveSubscriptionPaln(SubscriptionPlan.Free);
|
||||
console.error(e);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { getProAccessPlanFromSubscriptions, isAppFlowyHosted } from '@/utils/subscription';
|
||||
|
||||
function InviteMember({
|
||||
workspace,
|
||||
@@ -63,9 +63,7 @@ function InviteMember({
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
|
||||
setActiveSubscriptionPaln(subscription?.plan || SubscriptionPlan.Free);
|
||||
setActiveSubscriptionPaln(getProAccessPlanFromSubscriptions(subscriptions));
|
||||
} catch (e) {
|
||||
setActiveSubscriptionPaln(SubscriptionPlan.Free);
|
||||
console.error(e);
|
||||
|
||||
@@ -51,9 +51,24 @@ function UpgradePlan({ open, onClose, onOpen }: { open: boolean; onClose: () =>
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
const proSubscription = subscriptions.find(
|
||||
(item) => item.plan === SubscriptionPlan.Pro || item.plan === SubscriptionPlan.Team
|
||||
);
|
||||
|
||||
setActiveSubscription(subscription);
|
||||
if (proSubscription) {
|
||||
setActiveSubscription({
|
||||
...proSubscription,
|
||||
plan: SubscriptionPlan.Pro,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveSubscription({
|
||||
plan: SubscriptionPlan.Free,
|
||||
currency: '',
|
||||
recurring_interval: SubscriptionInterval.Month,
|
||||
price_cents: 0,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useEditorContext } from '@/components/editor/EditorContext';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { ColorEnum, renderColor } from '@/utils/color';
|
||||
import { isAppFlowyHosted } from '@/utils/subscription';
|
||||
import { getProAccessPlanFromSubscriptions, isAppFlowyHosted } from '@/utils/subscription';
|
||||
|
||||
const origins: Origins = {
|
||||
anchorOrigin: {
|
||||
@@ -50,9 +50,7 @@ function Color({ node, onSelectColor }: { node: BlockNode; onSelectColor: () =>
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = subscriptions[0];
|
||||
|
||||
setActiveSubscriptionPlan(subscription?.plan || SubscriptionPlan.Free);
|
||||
setActiveSubscriptionPlan(getProAccessPlanFromSubscriptions(subscriptions));
|
||||
} catch (e) {
|
||||
setActiveSubscriptionPlan(SubscriptionPlan.Free);
|
||||
console.error(e);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
* Include localhost:8000 to cover the default dev backend when APPFLOWY_BASE_URL isn't updated.
|
||||
* Self-hosted instances are not official hosts.
|
||||
*/
|
||||
import { Subscription, SubscriptionPlan } from '@/application/types';
|
||||
import { getConfigValue } from '@/utils/runtime-config';
|
||||
|
||||
const OFFICIAL_HOSTNAMES = new Set(['beta.appflowy.cloud', 'test.appflowy.cloud', 'localhost']);
|
||||
const PRO_ACCESS_PLANS = new Set([SubscriptionPlan.Pro, SubscriptionPlan.Team]);
|
||||
|
||||
function getBaseUrlHostname(): string | null {
|
||||
const baseUrl = getConfigValue('APPFLOWY_BASE_URL', '').trim();
|
||||
@@ -60,3 +62,15 @@ export function isAppFlowyHosted(): boolean {
|
||||
|
||||
return isOfficialHostname(resolveHostname());
|
||||
}
|
||||
|
||||
export function hasProAccessFromPlans(plans?: SubscriptionPlan[] | null): boolean {
|
||||
if (!plans || plans.length === 0) return false;
|
||||
return plans.some((plan) => PRO_ACCESS_PLANS.has(plan));
|
||||
}
|
||||
|
||||
export function getProAccessPlanFromSubscriptions(subscriptions?: Subscription[] | null): SubscriptionPlan {
|
||||
if (!subscriptions || subscriptions.length === 0) return SubscriptionPlan.Free;
|
||||
return subscriptions.some((subscription) => PRO_ACCESS_PLANS.has(subscription.plan))
|
||||
? SubscriptionPlan.Pro
|
||||
: SubscriptionPlan.Free;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user