diff --git a/apps/builder/app/builder/builder.tsx b/apps/builder/app/builder/builder.tsx index 5248544ce35d..686844e104bd 100644 --- a/apps/builder/app/builder/builder.tsx +++ b/apps/builder/app/builder/builder.tsx @@ -407,7 +407,6 @@ export const Builder = ({ { }; export const SectionBackups = () => { - const { hasProPlan } = useStore($userPlanFeatures); + const { hasPaidPlan } = useStore($userPlanFeatures); const { data, load } = trpcClient.project.publishedBuilds.useQuery(); const projectId = $project.get()?.id ?? ""; useEffect(() => { @@ -85,7 +85,7 @@ export const SectionBackups = () => { @@ -120,7 +120,7 @@ export const SectionBackups = () => { - {!hasProPlan && ( + {!hasPaidPlan && ( { - const hasProPlan = useStore($userPlanFeatures).hasProPlan; + const { allowStagingPublish } = useStore($userPlanFeatures); const project = useStore($project); if (project === undefined) { return; } - const tooltipContentForFreeUsers = hasProPlan ? undefined : ( + const tooltipContentForFreeUsers = allowStagingPublish ? undefined : ( Publish to Staging @@ -55,13 +55,13 @@ export const DomainCheckbox = (props: DomainCheckboxProps) => { ); - const defaultChecked = hasProPlan ? props.defaultChecked : true; - const disabled = hasProPlan ? props.disabled : true; + const defaultChecked = allowStagingPublish ? props.defaultChecked : true; + const disabled = allowStagingPublish ? props.disabled : true; const hideDomainCheckbox = project.domainsVirtual.filter( (domain) => domain.status === "ACTIVE" && domain.verified - ).length === 0 && hasProPlan; + ).length === 0 && allowStagingPublish; return (
diff --git a/apps/builder/app/builder/features/topbar/entri.tsx b/apps/builder/app/builder/features/topbar/entri.tsx index 39af866ed889..a272fb5aade5 100644 --- a/apps/builder/app/builder/features/topbar/entri.tsx +++ b/apps/builder/app/builder/features/topbar/entri.tsx @@ -98,7 +98,7 @@ const useEntri = ({ domain, dnsRecords, onClose }: EntriProps) => { export const Entri = ({ domain, dnsRecords, onClose }: EntriProps) => { entriGlobalStyles(); - const { hasProPlan } = useStore($userPlanFeatures); + const { hasPaidPlan } = useStore($userPlanFeatures); const { error, isOpen, showDialog } = useEntri({ domain, dnsRecords, @@ -113,7 +113,7 @@ export const Entri = ({ domain, dnsRecords, onClose }: EntriProps) => { color="primary" type="button" onClick={() => { - if (hasProPlan) { + if (hasPaidPlan) { showDialog(); } else { setRequestUpgrade(true); diff --git a/apps/builder/app/builder/features/topbar/menu/menu.tsx b/apps/builder/app/builder/features/topbar/menu/menu.tsx index ca5957c3c0c8..effaa058bff2 100644 --- a/apps/builder/app/builder/features/topbar/menu/menu.tsx +++ b/apps/builder/app/builder/features/topbar/menu/menu.tsx @@ -58,7 +58,7 @@ const ViewMenuItem = () => { }; export const Menu = () => { - const { hasProPlan } = useStore($userPlanFeatures); + const { hasPaidPlan } = useStore($userPlanFeatures); const authPermit = useStore($authPermit); const authTokenPermission = useStore($authTokenPermissions); const authToken = useStore($authToken); @@ -287,7 +287,7 @@ export const Menu = () => { - {hasProPlan === false && ( + {hasPaidPlan === false && ( <> { +const $restrictedFeatures = computed( + [$pages, $dataSources, $instances, $userPlanFeatures], + (pages, dataSources, instances, userPlanFeatures) => { const features = new Map< string, | undefined @@ -248,46 +244,38 @@ const $usedProFeatures = computed( return features; } // specified emails for default webhook form - if ((pages?.meta?.contactEmail ?? "").trim()) { + if ( + userPlanFeatures.maxContactEmails === 0 && + (pages?.meta?.contactEmail ?? "").trim() + ) { features.set("Custom contact email", undefined); } - // pages with dynamic paths - for (const page of [pages.homePage, ...pages.pages]) { - const awareness = { - pageId: page.id, - instanceSelector: [page.rootInstanceId], - }; - // allow catch all for 404 pages on free plan - if (isPathnamePattern(page.path) && page.path !== "/*") { - features.set("Dynamic path", { awareness, view: "pageSettings" }); - } - if (page.meta.redirect && page.meta.redirect !== `""`) { - features.set("Redirect", { awareness, view: "pageSettings" }); - } - } - // has resource variables - for (const dataSource of dataSources.values()) { - if (dataSource.type === "resource") { - const instanceId = dataSource.scopeInstanceId ?? ""; - features.set("Resource variable", { - awareness: findAwarenessByInstanceId(pages, instances, instanceId), - }); + if (!userPlanFeatures.allowDynamicData) { + // pages with dynamic paths + for (const page of [pages.homePage, ...pages.pages]) { + const awareness = { + pageId: page.id, + instanceSelector: [page.rootInstanceId], + }; + // allow catch all for 404 pages on free plan + if (isPathnamePattern(page.path) && page.path !== "/*") { + features.set("Dynamic path", { awareness, view: "pageSettings" }); + } + if (page.meta.redirect && page.meta.redirect !== `""`) { + features.set("Redirect", { awareness, view: "pageSettings" }); + } } - } - - // Instances with animations. - for (const instance of instances.values()) { - const [namespace] = parseComponentName(instance.component); - if (namespace === "@webstudio-is/sdk-components-animation") { - features.set("Animation component", { - awareness: findAwarenessByInstanceId(pages, instances, instance.id), - }); + // has resource variables + for (const dataSource of dataSources.values()) { + if (dataSource.type === "resource") { + const instanceId = dataSource.scopeInstanceId ?? ""; + features.set("Resource variable", { + awareness: findAwarenessByInstanceId(pages, instances, instanceId), + }); + } } } - - // temporary ignore features checks - // return features; - return new Map() as typeof features; + return features; } ); @@ -309,10 +297,10 @@ const Publish = ({ const [isPublishing, setIsPublishing] = useOptimistic(false); const buttonRef = useRef(null); const [hasSelectedDomains, setHasSelectedDomains] = useState(false); - const hasProPlan = useStore($userPlanFeatures).hasProPlan; + const { hasPaidPlan, allowStagingPublish } = useStore($userPlanFeatures); useEffect(() => { - if (hasProPlan === false) { + if (allowStagingPublish === false) { setHasSelectedDomains(true); return; } @@ -344,13 +332,13 @@ const Publish = ({ return () => { observer.disconnect(); }; - }, [hasProPlan]); + }, [allowStagingPublish]); const handlePublish = async (formData: FormData) => { setPublishError(undefined); setIsPublishing(true); - const domains = hasProPlan + const domains = allowStagingPublish ? formData .getAll(domainToPublishName) .map((domainEntry) => domainEntry.toString()) @@ -436,7 +424,7 @@ const Publish = ({ toast.success( <> The project has been successfully published.{" "} - {hasProPlan === false && ( + {hasPaidPlan === false && (
On the free plan, you have {timesLeft} out of{" "} {maxPublishesAllowedPerUser} daily publications remaining. The @@ -641,30 +629,17 @@ const PublishStatic = ({ const useCanAddDomain = () => { const { load, data } = trpcClient.domain.countTotalDomains.useQuery(); - const { maxDomainsAllowedPerUser, hasProPlan } = useStore($userPlanFeatures); + const { maxDomainsAllowedPerUser } = useStore($userPlanFeatures); const project = useStore($project); - const activeDomainsCount = project?.domainsVirtual.filter( (domain) => domain.status === "ACTIVE" && domain.verified ).length; - useEffect(() => { load(); }, [load, activeDomainsCount]); - - if (hasProPlan) { - return { canAddDomain: true, maxDomainsAllowedPerUser }; - } - - if (data?.success === false) { - return { canAddDomain: false, maxDomainsAllowedPerUser }; - } - - const withinFreeLimit = data + const canAddDomain = data ? data.success && data.data < maxDomainsAllowedPerUser : true; - const canAddDomain = hasProPlan || withinFreeLimit; - return { canAddDomain, maxDomainsAllowedPerUser }; }; @@ -704,7 +679,7 @@ const buttonLinkClass = css({ }).toString(); const UpgradeBanner = () => { - const usedProFeatures = useStore($usedProFeatures); + const restrictedFeatures = useStore($restrictedFeatures); const { canAddDomain } = useCanAddDomain(); const { userPublishCount, maxPublishesAllowedPerUser } = useUserPublishCount(); @@ -729,7 +704,7 @@ const UpgradeBanner = () => { ); } - if (usedProFeatures.size > 0) { + if (restrictedFeatures.size > 0) { return ( { /> Following Pro features are used: - {Array.from(usedProFeatures).map( + {Array.from(restrictedFeatures).map( ([message, { awareness, view, info } = {}], index) => (
  • @@ -813,8 +788,7 @@ const Content = (props: { projectId: Project["id"]; onExportClick: () => void; }) => { - const usedProFeatures = useStore($usedProFeatures); - const { hasProPlan } = useStore($userPlanFeatures); + const restrictedFeatures = useStore($restrictedFeatures); const [newDomains, setNewDomains] = useState(new Set()); const project = useStore($project); @@ -859,13 +833,13 @@ const Content = (props: { }} onExportClick={props.onExportClick} /> - {hasProPlan === false && } + 0 && hasProPlan === false) || + restrictedFeatures.size > 0 || userPublishCount >= maxPublishesAllowedPerUser } /> diff --git a/apps/builder/app/builder/features/topbar/share.tsx b/apps/builder/app/builder/features/topbar/share.tsx index 11ef6a3fc362..d8669d883fae 100644 --- a/apps/builder/app/builder/features/topbar/share.tsx +++ b/apps/builder/app/builder/features/topbar/share.tsx @@ -13,13 +13,7 @@ import { ShareProjectContainer } from "~/shared/share-project"; import { $authPermit } from "~/shared/nano-states"; import { $isShareDialogOpen } from "~/builder/shared/nano-states"; -export const ShareButton = ({ - projectId, - hasProPlan, -}: { - projectId: string; - hasProPlan: boolean; -}) => { +export const ShareButton = ({ projectId }: { projectId: string }) => { const isShareDialogOpen = useStore($isShareDialogOpen); const authPermit = useStore($authPermit); @@ -50,7 +44,7 @@ export const ShareButton = ({ sideOffset={Number.parseFloat(rawTheme.spacing[8])} css={{ marginRight: theme.spacing[3] }} > - + Share diff --git a/apps/builder/app/builder/features/topbar/topbar.tsx b/apps/builder/app/builder/features/topbar/topbar.tsx index 6dd722d1a06f..76177dbb3bbe 100644 --- a/apps/builder/app/builder/features/topbar/topbar.tsx +++ b/apps/builder/app/builder/features/topbar/topbar.tsx @@ -81,12 +81,11 @@ const hideOnMobile: CSS = { type TopbarProps = { project: Project; - hasProPlan: boolean; loading: ReactNode; css: CSS; }; -export const Topbar = ({ project, hasProPlan, css, loading }: TopbarProps) => { +export const Topbar = ({ project, css, loading }: TopbarProps) => { const pages = useStore($pages); return (