mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
Merge pull request #561 from storytold/feature/storyteller-prelaunch-landing
storyteller prelaunch landing
This commit is contained in:
5 files changed
+376
-48
No files matched your search
@@ -188,6 +188,22 @@ export default function TopNav({
|
||||
// { id: 4, name: "Text to Image", link: "/text-to-image" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const pageContentWrapper = document.getElementById("page-content-wrapper");
|
||||
|
||||
if (pageContentWrapper) {
|
||||
if (domain.titlePart === "Storyteller AI" && isOnLandingPage) {
|
||||
pageContentWrapper.style.padding = "0px";
|
||||
} else {
|
||||
pageContentWrapper.style.padding = "";
|
||||
}
|
||||
}
|
||||
}, [domain.titlePart, isOnLandingPage]);
|
||||
|
||||
if (domain.titlePart === "Storyteller AI" && isOnLandingPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id="topbar-wrapper"
|
||||
|
||||
@@ -384,8 +384,7 @@ class PageContainer extends React.Component<
|
||||
</Route>
|
||||
|
||||
<Route path="/media/rename/:media_file_token">
|
||||
<MediaRenamePage
|
||||
/>
|
||||
<MediaRenamePage />
|
||||
</Route>
|
||||
|
||||
<Route path="/media/:token">
|
||||
@@ -681,7 +680,9 @@ class PageContainer extends React.Component<
|
||||
</Route>
|
||||
|
||||
<Route path="/moderation">
|
||||
<ModerationPage sessionWrapper={this.props.sessionWrapper} />
|
||||
<ModerationPage
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route exact={true} path="/clone">
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Container } from "components/common";
|
||||
import FakeYouLandingHeader from "./fakeyou/FakeYouLandingHeader";
|
||||
import Dashboard from "./Dashboard";
|
||||
import { useDomainConfig } from "context/DomainConfigContext";
|
||||
import LandingVideoReel from "./components/LandingVideoReel/LandingVideoReel";
|
||||
// import LandingVideoReel from "./components/LandingVideoReel/LandingVideoReel";
|
||||
import {
|
||||
FrontendInferenceJobType,
|
||||
InferenceJob,
|
||||
@@ -17,9 +17,13 @@ import "./LandingPage.scss";
|
||||
// import VstSectionV1 from "./components/VstSectionV1";
|
||||
import VstSectionV2 from "./components/VstSectionV2";
|
||||
import FakeYouLandingBody from "./fakeyou/FakeYouLandingBody";
|
||||
import { WebsiteConfig, Website } from "@storyteller/components/src/env/GetWebsite";
|
||||
import {
|
||||
WebsiteConfig,
|
||||
Website,
|
||||
} from "@storyteller/components/src/env/GetWebsite";
|
||||
import OnboardingSelection from "./storyteller/OnboardingSelection";
|
||||
import TtsDemoSection from "./components/TtsDemoSection/TtsDemoSection";
|
||||
// import TtsDemoSection from "./components/TtsDemoSection/TtsDemoSection";
|
||||
import PrelaunchLanding from "./storyteller/PrelaunchLanding/PrelaunchLanding";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -62,56 +66,58 @@ function LandingPage(props: Props) {
|
||||
return (
|
||||
<>
|
||||
{domain.website === Website.StorytellerAi && (
|
||||
<LandingVideoReel sessionWrapper={props.sessionWrapper} />
|
||||
// <LandingVideoReel sessionWrapper={props.sessionWrapper} />
|
||||
<PrelaunchLanding />
|
||||
)}
|
||||
<Container type="panel">
|
||||
{domain.website === Website.FakeYou ? (
|
||||
<>
|
||||
{/* FAKEYOU.COM */}
|
||||
{!isLoggedIn && (
|
||||
<>
|
||||
<FakeYouLandingHeader
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
props.sessionSubscriptionsWrapper
|
||||
}
|
||||
inferenceJobs={props.inferenceJobs}
|
||||
ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={props.enqueueTtsJob}
|
||||
/>
|
||||
{/* <VstSectionV1 /> */}
|
||||
<VstSectionV2 />
|
||||
</>
|
||||
)}
|
||||
{
|
||||
domain.website === Website.FakeYou ? (
|
||||
<>
|
||||
{/* FAKEYOU.COM */}
|
||||
{!isLoggedIn && (
|
||||
<>
|
||||
<FakeYouLandingHeader
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
props.sessionSubscriptionsWrapper
|
||||
}
|
||||
inferenceJobs={props.inferenceJobs}
|
||||
ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={props.enqueueTtsJob}
|
||||
/>
|
||||
{/* <VstSectionV1 /> */}
|
||||
<VstSectionV2 />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
|
||||
{!isLoggedIn && <FakeYouLandingBody />}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* STORYTELLER,AI */}
|
||||
{!isLoggedIn && <FakeYouLandingBody />}
|
||||
</>
|
||||
) : null
|
||||
// <>
|
||||
// {/* STORYTELLER,AI */}
|
||||
|
||||
{protectedStudioOnboarding}
|
||||
// {protectedStudioOnboarding}
|
||||
|
||||
<VstSectionV2 />
|
||||
<TtsDemoSection
|
||||
sessionWrapper={props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
|
||||
inferenceJobs={props.inferenceJobs}
|
||||
ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
enqueueTtsJob={props.enqueueTtsJob}
|
||||
/>
|
||||
// <VstSectionV2 />
|
||||
// <TtsDemoSection
|
||||
// sessionWrapper={props.sessionWrapper}
|
||||
// sessionSubscriptionsWrapper={props.sessionSubscriptionsWrapper}
|
||||
// inferenceJobs={props.inferenceJobs}
|
||||
// ttsInferenceJobs={props.ttsInferenceJobs}
|
||||
// enqueueInferenceJob={props.enqueueInferenceJob}
|
||||
// inferenceJobsByCategory={props.inferenceJobsByCategory}
|
||||
// enqueueTtsJob={props.enqueueTtsJob}
|
||||
// />
|
||||
|
||||
<Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
// <Dashboard sessionWrapper={props.sessionWrapper} />
|
||||
|
||||
<FakeYouLandingBody />
|
||||
</>
|
||||
)}
|
||||
// <FakeYouLandingBody />
|
||||
// </>
|
||||
}
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
grid-template-rows: repeat(7, calc(100vw / 7 * (9 / 16)));
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 992px) {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-rows: repeat(5, calc(100vw / 5 * (9 / 16)));
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: repeat(3, calc(100vw / 3 * (9 / 16)));
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
position: relative;
|
||||
background-color: rgba($white, 0.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s;
|
||||
pointer-events: all;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&.double-grid {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
&.triple-grid {
|
||||
grid-column: span 3;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
&.grid-item-custom {
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin-left: 20%;
|
||||
margin-top: 10%;
|
||||
width: 270%;
|
||||
cursor: default;
|
||||
|
||||
@media (max-width: 992px) {
|
||||
margin-left: 22%;
|
||||
margin-top: 15%;
|
||||
}
|
||||
}
|
||||
|
||||
.waitlist-button {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin-left: 10%;
|
||||
margin-top: 4%;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background-color: $primary;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
width: 84%;
|
||||
padding: 2.5%;
|
||||
border: 4px solid lighten($primary, 12%);
|
||||
box-shadow: 0 0 15px rgba(255, 255, 255, 0.7); // Soft white glow
|
||||
animation: glow 1.5s infinite alternate;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($primary, 5%);
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
margin-left: 8%;
|
||||
margin-top: 2%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
from {
|
||||
box-shadow: 0 0 50px rgba(255, 170, 168, 0.25);
|
||||
}
|
||||
to {
|
||||
box-shadow: 0 0 25px rgba(255, 170, 168, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grid-item.blank {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./PrelaunchLanding.scss";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
|
||||
interface MediaItem {
|
||||
token: string;
|
||||
public_bucket_path: string;
|
||||
maybe_creator: {
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
const PrelaunchLanding: React.FC = () => {
|
||||
const totalGrids = 63;
|
||||
const [mediaItems, setMediaItems] = useState<MediaItem[]>([]);
|
||||
const [blankGrids, setBlankGrids] = useState<number[]>([15, 16, 17, 22, 23]);
|
||||
const [screenSize, setScreenSize] = useState("lg");
|
||||
|
||||
useEffect(() => {
|
||||
fetchMediaItems();
|
||||
updateGridsForScreenSize();
|
||||
window.addEventListener("resize", updateGridsForScreenSize);
|
||||
return () => window.removeEventListener("resize", updateGridsForScreenSize);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const updateGridsForScreenSize = () => {
|
||||
const screenWidth = window.innerWidth;
|
||||
if (screenWidth < 768) {
|
||||
setBlankGrids([7, 8, 9, 10, 11, 12]);
|
||||
setScreenSize("sm");
|
||||
} else if (screenWidth < 992) {
|
||||
setBlankGrids([12, 13, 14, 17, 18, 19]);
|
||||
setScreenSize("md");
|
||||
} else {
|
||||
setBlankGrids([15, 16, 17, 22, 23]);
|
||||
setScreenSize("lg");
|
||||
}
|
||||
};
|
||||
|
||||
const getCustomGridItemIndex = () => {
|
||||
switch (screenSize) {
|
||||
case "lg":
|
||||
return 15;
|
||||
case "md":
|
||||
return 12;
|
||||
case "sm":
|
||||
return 7;
|
||||
default:
|
||||
return 15;
|
||||
}
|
||||
};
|
||||
|
||||
const fetchMediaItems = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://api.storyteller.ai/v1/media_files/list_featured?page_size=63&filter_media_classes=video"
|
||||
);
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setMediaItems(shuffleArray(data.results));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching media items:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const shuffleArray = (array: any[]) => {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
const renderGridItems = () => {
|
||||
const gridItems = [];
|
||||
const customGridItemIndex = getCustomGridItemIndex();
|
||||
|
||||
for (let i = 1; i <= totalGrids; i++) {
|
||||
const isBlank = blankGrids.includes(i);
|
||||
if (screenSize === "sm") {
|
||||
if (i === 10) {
|
||||
gridItems.push(
|
||||
<div
|
||||
key="10-12"
|
||||
className={`grid-item triple-grid grid-item-custom ${
|
||||
isBlank ? "blank" : ""
|
||||
}`}
|
||||
>
|
||||
<button className="waitlist-button">
|
||||
Join the Waitlist
|
||||
<FontAwesomeIcon icon={faArrowRight} className="ms-2 fs-6" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
} else if (screenSize === "md") {
|
||||
if (i === 17) {
|
||||
gridItems.push(
|
||||
<div
|
||||
key="17-19"
|
||||
className={`grid-item triple-grid grid-item-custom ${
|
||||
isBlank ? "blank" : ""
|
||||
}`}
|
||||
>
|
||||
<button className="waitlist-button">
|
||||
Join the Waitlist
|
||||
<FontAwesomeIcon icon={faArrowRight} className="ms-2 fs-6" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (i === 22) {
|
||||
gridItems.push(
|
||||
<div
|
||||
key="22-23"
|
||||
className={`grid-item double-grid grid-item-custom ${
|
||||
isBlank ? "blank" : ""
|
||||
}`}
|
||||
>
|
||||
<button className="waitlist-button">
|
||||
Join the Waitlist
|
||||
<FontAwesomeIcon icon={faArrowRight} className="ms-2 fs-6" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const mediaItem = mediaItems[i - 1];
|
||||
const bucketConfig = new BucketConfig();
|
||||
const gifUrl = mediaItem
|
||||
? bucketConfig.getCdnUrl(
|
||||
mediaItem.public_bucket_path + "-thumb.gif",
|
||||
360,
|
||||
20
|
||||
)
|
||||
: "";
|
||||
|
||||
gridItems.push(
|
||||
<div
|
||||
key={i}
|
||||
className={`grid-item ${isBlank ? "blank" : ""} ${
|
||||
i === customGridItemIndex ? "grid-item-custom" : ""
|
||||
}`.trim()}
|
||||
>
|
||||
{isBlank ? (
|
||||
i === customGridItemIndex ? (
|
||||
<img
|
||||
className="logo"
|
||||
src="/fakeyou/Storyteller-Logo-1.png"
|
||||
alt="Storyteller Logo"
|
||||
/>
|
||||
) : null
|
||||
) : mediaItem ? (
|
||||
<img
|
||||
onClick={() =>
|
||||
window.open(`https://storyteller.ai/media/${mediaItem.token}`)
|
||||
}
|
||||
src={gifUrl}
|
||||
className="w-100 h-100 object-fit-cover"
|
||||
alt={`Video by ${mediaItem.maybe_creator.username}`}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return gridItems;
|
||||
};
|
||||
|
||||
return <div className="grid-container">{renderGridItems()}</div>;
|
||||
};
|
||||
|
||||
export default PrelaunchLanding;
|
||||
Reference in new issue
Block a user