consistent feature locking with lock icon

This commit is contained in:
bflatastic committed 2024-09-03 16:01:43 +07:00
1 parent f8bf7c0710
commit 0f0874b20e
4 files changed
+174 -99

No files matched your search

@@ -5,27 +5,26 @@
background-color: $panel-inner;
pointer-events: none;
overflow: hidden;
box-shadow: none !important;
border-radius: 0.5rem;
padding: 1rem;
.overlay {
position: absolute;
z-index: 5;
z-index: 10;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba($panel, 0.8);
background-color: rgba($panel-inner, 0.6);
display: flex;
justify-content: center;
align-items: center;
color: rgba($white, 0.8);
font-size: 16px;
color: rgba($white, 0.9);
font-size: 15px;
font-weight: 500;
pointer-events: all;
border: 1px solid rgba($primary, 0.4);
border: 2px solid rgba($primary, 0.4);
border-radius: 0.5rem;
span {
text-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
}
padding-left: 1rem;
}
}
@@ -13,6 +13,8 @@ interface PremiumLockProps {
session?: any;
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
showCtaButton?: boolean;
lockPosition?: "top" | "center";
plural?: boolean;
}
export default function PremiumLock({
@@ -21,6 +23,8 @@ export default function PremiumLock({
sessionSubscriptionsWrapper,
showCtaButton = false,
large = false,
lockPosition = "center",
plural = false,
}: PremiumLockProps) {
const hasAccess = () => {
switch (requiredPlan) {
@@ -50,40 +54,92 @@ export default function PremiumLock({
children
) : (
<Panel className="fy-premium-lock rounded px-3 py-4">
{children}
<div className="overlay">
<div className="d-flex flex-column align-items-center gap-2 text-center">
<FontAwesomeIcon
icon={faLock}
className={`me-2 ${large ? "fs-4" : "fs-5"}`}
/>
{requiredPlan === "any" ? (
<span className={`${large ? "lead fw-medium" : ""}`}>
This feature requires a{" "}
<Link
to="/pricing"
className={`${large ? "lead fw-medium" : ""}`}
>
subscription plan.
</Link>
</span>
) : (
<span>
This feature requires a{" "}
<Link to="/pricing">{requiredPlan} subscription</Link>
</span>
)}
{showCtaButton && (
<Button
variant="primary"
label="Upgrade your account"
icon={faUp}
to="/pricing"
className="mt-2"
{lockPosition === "center" && children}
{lockPosition === "top" && (
<div className="mt-5 mb-0">{children}</div>
)}
<div
className={`overlay ${
lockPosition === "top"
? "align-items-start justify-content-start pt-3"
: ""
}`}
>
{lockPosition === "center" ? (
<div className="d-flex flex-column align-items-center gap-2 text-center">
<FontAwesomeIcon
icon={faLock}
className={`me-2 ${large ? "fs-4" : "fs-5"}`}
/>
)}
</div>
{requiredPlan === "any" ? (
<span className={`${large ? "lead fw-medium" : ""}`}>
{plural
? "These features require a"
: "This feature requires a"}{" "}
<Link
to="/pricing"
className={`${large ? "lead fw-medium" : ""}`}
>
subscription plan
</Link>
</span>
) : (
<span>
{plural
? "These features require a"
: "This feature requires a"}{" "}
<Link to="/pricing">{requiredPlan} plan</Link>
</span>
)}
{showCtaButton && (
<Button
variant="primary"
label="Upgrade your account"
icon={faUp}
to="/pricing"
className="mt-2"
/>
)}
</div>
) : (
<div className="d-flex align-items-center gap-2 text-start justify-content-center">
<FontAwesomeIcon
icon={faLock}
className={`me-1 ${large ? "fs-4" : "fs-7"}`}
/>
{requiredPlan === "any" ? (
<span className={`${large ? "lead fw-medium" : ""}`}>
{plural
? "These features require a"
: "This feature requires a"}{" "}
<Link
to="/pricing"
className={`${large ? "lead fw-medium" : ""}`}
>
subscription plan
</Link>
</span>
) : (
<span>
<Link className="fw-medium" to="/pricing">
Upgrade to {requiredPlan}
</Link>{" "}
{plural ? "to use these features" : "to use this feature"}
</span>
)}
{showCtaButton && (
<Button
variant="primary"
label="Upgrade your account"
icon={faUp}
to="/pricing"
className="mt-2"
/>
)}
</div>
)}
</div>
</Panel>
)}
@@ -22,7 +22,6 @@ import {
faLock,
faPlus,
faSparkles,
faStars,
} from "@fortawesome/pro-solid-svg-icons";
import "./LivePortrait.scss";
import ThumbnailMediaPicker from "./ThumbnailMediaPicker";
@@ -53,6 +52,7 @@ import MotionEntityInput from "./MotionEntityInput";
import OutputThumbnailImage from "./OutputThumbnailImage";
import { useHistory } from "react-router-dom";
import { JobState } from "@storyteller/components/src/jobs/JobStates";
import PremiumLock from "components/PremiumLock";
interface LivePortraitProps {
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
@@ -110,7 +110,6 @@ export default function LivePortrait({
x: 0,
y: 0,
});
const hasPremium = sessionSubscriptionsWrapper.hasPaidFeatures();
const [generatedVideoSrc, setGeneratedVideoSrc] = useState("");
const [sourceTokens, setSourceTokens] = useState<string[]>([
...PRECOMPUTED_SOURCE_TOKENS,
@@ -992,31 +991,40 @@ export default function LivePortrait({
</div>
<div className="d-flex flex-column gap-2">
<div className="d-flex gap-3">
<Checkbox
disabled={!hasPremium}
label={"Make Private"}
onChange={() => {
setVisibility(prevVisibility =>
prevVisibility === "private" ? "public" : "private"
);
}}
checked={visibility === "private"}
/>
<PremiumLock
sessionSubscriptionsWrapper={sessionSubscriptionsWrapper}
lockPosition="top"
requiredPlan="pro"
plural={true}
>
<div className="d-flex gap-3">
<Checkbox
label={"Make Private"}
onChange={() => {
setVisibility(prevVisibility =>
prevVisibility === "private"
? "public"
: "private"
);
}}
checked={visibility === "private"}
className="mb-0"
/>
<Checkbox
disabled={!hasPremium}
label={"Remove Watermark"}
onChange={() => {
setRemoveWatermark(
prevRemoveWatermark => !prevRemoveWatermark
);
}}
checked={removeWatermark}
/>
</div>
<Checkbox
label={"Remove Watermark"}
onChange={() => {
setRemoveWatermark(
prevRemoveWatermark => !prevRemoveWatermark
);
}}
checked={removeWatermark}
className="mb-0"
/>
</div>
</PremiumLock>
{!hasPremium && (
{/* {!hasPremium && (
<div className="d-flex">
<Button
variant="link"
@@ -1025,7 +1033,7 @@ export default function LivePortrait({
to="/pricing"
/>
</div>
)}
)} */}
</div>
</div>
</div>
@@ -20,7 +20,7 @@ import {
EnqueueVSTResponse,
} from "@storyteller/components/src/api/workflows/EnqueueVST";
import { Prompt } from "@storyteller/components/src/api/prompts/GetPrompts";
import { useHistory, useParams } from "react-router-dom";
import { Link, useHistory, useParams } from "react-router-dom";
import { STYLE_OPTIONS, STYLES_BY_KEY } from "common/StyleOptions";
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
import { StyleSelectionButton } from "./StyleSelection/StyleSelectionButton";
@@ -30,7 +30,7 @@ import { isMobile } from "react-device-detect";
import { AITools } from "components/marketing";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faLock } from "@fortawesome/pro-solid-svg-icons";
import { Link } from "react-router-dom";
import PremiumLock from "components/PremiumLock";
export default function StyleVideo() {
const { mediaToken: pageMediaToken } = useParams<{ mediaToken: string }>();
@@ -52,9 +52,6 @@ export default function StyleVideo() {
const { open, modalOpen } = useModal();
const history = useHistory();
const [enableSignUpBlock] = useState(false);
const proOrElite =
sessionSubscriptions?.hasActiveProSubscription() ||
sessionSubscriptions?.hasActiveEliteSubscription();
const openStyleSelection = () =>
open({
@@ -105,9 +102,9 @@ export default function StyleVideo() {
} else {
// @ts-ignore
window.dataLayer.push({
"event": "enqueue_failure",
"page": "/style-video",
"user_id": "$user_id"
event: "enqueue_failure",
page: "/style-video",
user_id: "$user_id",
});
console.log("Failed to enqueue job", res);
}
@@ -120,10 +117,14 @@ export default function StyleVideo() {
const lengthOptions = [
{ label: "3 seconds", value: 3000 },
{ disabled: !proOrElite, label: "5 seconds", value: 5000 },
{ disabled: !proOrElite, label: "7 seconds", value: 7000 },
{ label: "5 seconds", value: 5000 },
{ label: "7 seconds", value: 7000 },
];
const proOrElite =
sessionSubscriptions?.hasActiveProSubscription() ||
sessionSubscriptions?.hasActiveEliteSubscription();
const onPromptUpdate = (prompt: Prompt | null) => {
promptSet(prompt?.maybe_positive_prompt || "");
negativePromptSet(prompt?.maybe_negative_prompt || "");
@@ -487,26 +488,37 @@ export default function StyleVideo() {
</div>
<div className="mt-3">
<SegmentButtons
{...{
className: "fy-style-video-length mb-1",
label: "Video Duration",
onChange: ({ target }: { target: any }) => {
lengthSet(target.value);
},
options: lengthOptions,
value: length,
highlight: true,
}}
/>
<Link
{...{
className: "fs-7 lh-1",
to: "/pricing",
}}
>
Subscribe to Pro or Elite for 5 or 7 second videos
</Link>
{sessionSubscriptions && (
<PremiumLock
sessionSubscriptionsWrapper={sessionSubscriptions}
requiredPlan="pro"
lockPosition="top"
>
<SegmentButtons
{...{
className: "fy-style-video-length",
label: "Video Duration",
onChange: ({ target }: { target: any }) => {
lengthSet(target.value);
},
options: lengthOptions,
value: length,
highlight: true,
}}
/>
</PremiumLock>
)}
{!proOrElite && (
<Link
{...{
className: "d-flex fs-7 lh-1 pt-3",
to: "/pricing",
}}
>
Subscribe to Pro or Elite for 5 or 7 second videos
</Link>
)}
</div>
</div>
</Panel>