mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 00:47:53 +00:00
Feature/load scene (#285)
This PR removes the dummy data for Load Scene and uses real data instead. Co-authored-by: Quicksticks-oss <chongobongotoday@gmail.com>
This commit is contained in:
1 parent
79b11ff0e8
commit
2c460e4e63
12 files changed
+252
-197
No files matched your search
@@ -1,17 +1,20 @@
|
||||
import MakeRequest from "../MakeRequest";
|
||||
import { MediaFile, Pagination } from "~/pages/PageEnigma/models";
|
||||
import { MediaInfo } from "~/pages/PageEnigma/models";
|
||||
|
||||
export interface GetMediaRequest {}
|
||||
|
||||
export interface GetMediaListResponse {
|
||||
pagination: Pagination;
|
||||
success: boolean;
|
||||
results: MediaFile[];
|
||||
results: MediaInfo[];
|
||||
}
|
||||
|
||||
export interface Pagination {}
|
||||
|
||||
export interface GetMediaParams {
|
||||
page_index: number;
|
||||
filter_media_type: string;
|
||||
page_index?: number;
|
||||
filter_media_type?: string;
|
||||
filter_engine_categories?: string;
|
||||
}
|
||||
|
||||
export const GetMediaByUser = MakeRequest<
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface LoadingSpinnerProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const LoadingSpinner = ({ className }: LoadingSpinnerProps) => {
|
||||
return (
|
||||
<svg
|
||||
className={twMerge("h-6 w-6 animate-spin", className)}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -86,33 +86,31 @@ export const TransitionDialogue = ({
|
||||
<Dialog.Title
|
||||
as="div"
|
||||
className="mb-5 flex justify-between p-5 pb-0 text-xl font-bold text-white">
|
||||
{titleIcon && (
|
||||
<>
|
||||
{onTitleIconClick ? (
|
||||
<button
|
||||
className="flex items-center gap-3"
|
||||
onClick={onTitleIconClick}>
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
{onTitleIconClick ? (
|
||||
<button
|
||||
className="flex items-center gap-3"
|
||||
onClick={onTitleIconClick}>
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
{titleIcon && (
|
||||
<FontAwesomeIcon
|
||||
icon={titleIcon}
|
||||
className={titleIconClassName}
|
||||
/>
|
||||
)}
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
{showClose && (
|
||||
<Tooltip position="top" content="Close">
|
||||
<button
|
||||
|
||||
@@ -23,6 +23,7 @@ import { Pill } from "./Pill";
|
||||
import { WaveformPlayer } from "./WaveformPlayer";
|
||||
import UploadModal from "./UploadModal";
|
||||
import { ErrorDialog } from "./ErrorDialog";
|
||||
import { LoadingSpinner } from "./LoadingSpinner";
|
||||
|
||||
export {
|
||||
Button,
|
||||
@@ -52,6 +53,7 @@ export {
|
||||
LoadingDots,
|
||||
LoadingDotsBricks,
|
||||
LoadingDotsTyping,
|
||||
LoadingSpinner,
|
||||
Pagination,
|
||||
Pill,
|
||||
WaveformPlayer,
|
||||
|
||||
@@ -9,12 +9,17 @@ import {
|
||||
ErrorResponse,
|
||||
GetJobStatusResponse,
|
||||
JobState,
|
||||
MediaFileType,
|
||||
} from "~/pages/PageEnigma/models";
|
||||
import { ToasterContext, ToastTypes } from "~/contexts/ToasterContext";
|
||||
import { activeJobs, movies } from "~/pages/PageEnigma/store";
|
||||
import { listMediaByUser } from "~/api";
|
||||
import { AuthenticationContext } from "~/contexts/Authentication";
|
||||
import { STORAGE_KEYS } from "~/contexts/Authentication/types";
|
||||
import {
|
||||
GetMediaByUser,
|
||||
GetMediaListResponse,
|
||||
} from "~/api/media_files/GetMediaByUser";
|
||||
|
||||
export async function GetInferenceJobStatus(
|
||||
jobToken: string,
|
||||
@@ -75,22 +80,14 @@ export function GetCompletedMovies(username?: string) {
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
const sessionToken = localStorage.getItem(STORAGE_KEYS.SESSION_TOKEN) || "";
|
||||
fetch(
|
||||
listMediaByUser(username) +
|
||||
"?" +
|
||||
new URLSearchParams({ filter_media_type: "video" }),
|
||||
GetMediaByUser(
|
||||
username,
|
||||
{},
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
session: sessionToken,
|
||||
},
|
||||
// credentials: 'include'
|
||||
filter_media_type: MediaFileType.Video,
|
||||
},
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
.then((res: GetMediaListResponse) => {
|
||||
if (res.success && res.results) {
|
||||
if (
|
||||
JSON.stringify(movies.value) !==
|
||||
|
||||
@@ -95,7 +95,7 @@ export const ControlsTopButtons = () => {
|
||||
label: "Cancel",
|
||||
},
|
||||
showClose: true,
|
||||
className: "max-w-7xl w-auto",
|
||||
className: "max-w-5xl",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -113,7 +113,7 @@ export const ControlsTopButtons = () => {
|
||||
label: "Cancel",
|
||||
},
|
||||
showClose: true,
|
||||
className: "max-w-7xl w-auto",
|
||||
className: "max-w-5xl",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,99 +1,64 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
useLayoutEffect,
|
||||
} from "react";
|
||||
import { ScenePicker, SceneTypes } from "../ScenePicker";
|
||||
import { Label } from "~/components";
|
||||
import { Label, LoadingSpinner } from "~/components";
|
||||
import {
|
||||
GetMediaByUser,
|
||||
GetMediaListResponse,
|
||||
} from "~/api/media_files/GetMediaByUser";
|
||||
import { MediaFileType } from "~/pages/PageEnigma/models";
|
||||
import { AuthenticationContext } from "~/contexts/Authentication";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
interface LoadSceneProps {
|
||||
onSceneSelect: (token: string) => void;
|
||||
}
|
||||
|
||||
export const LoadScene = ({ onSceneSelect }: LoadSceneProps) => {
|
||||
// Dummy Data - replace with API data
|
||||
const dummyScenes: SceneTypes[] = [
|
||||
{
|
||||
token: "111",
|
||||
name: "Dragon",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "222",
|
||||
name: "Dancing Girl",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "333",
|
||||
name: "Dragon",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "444",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "555",
|
||||
name: "Dragon",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "666",
|
||||
name: "Dancing Girl",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "777",
|
||||
name: "Dragon",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "888",
|
||||
name: "Dancing Girl",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "999",
|
||||
name: "Dragon",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "000",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "0001",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "0002",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "0003",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
{
|
||||
token: "0004",
|
||||
name: "Home Office",
|
||||
updated_at: "Apr 03, 2024 14:24:48",
|
||||
thumbnail: "/resources/placeholders/scene_placeholder.png",
|
||||
},
|
||||
];
|
||||
const [scenes, setScenes] = useState<SceneTypes[]>([]);
|
||||
const { authState } = useContext(AuthenticationContext);
|
||||
const sceneLoading = useRef(false);
|
||||
const [isSceneLoading, setIsSceneLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!authState.userInfo || scenes.length || sceneLoading.current) {
|
||||
return;
|
||||
}
|
||||
sceneLoading.current = true;
|
||||
GetMediaByUser(
|
||||
authState.userInfo.username,
|
||||
{},
|
||||
{
|
||||
filter_engine_categories: MediaFileType.Scene,
|
||||
},
|
||||
)
|
||||
.then((res: GetMediaListResponse) => {
|
||||
if (res.success && res.results) {
|
||||
setScenes(
|
||||
res.results.map((scene) => ({
|
||||
token: scene.token,
|
||||
name: scene.maybe_title ?? "Untitled",
|
||||
updated_at: dayjs(scene.updated_at).format(
|
||||
"MMM D, YYYY HH:mm:ss",
|
||||
),
|
||||
thumbnail: scene.public_bucket_path,
|
||||
})),
|
||||
);
|
||||
setIsSceneLoading(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
success: false,
|
||||
error_reason: "Unknown error",
|
||||
};
|
||||
});
|
||||
}, [scenes, authState.userInfo]);
|
||||
|
||||
const handleSceneSelect = (selectedScene: SceneTypes) => {
|
||||
onSceneSelect(selectedScene.token);
|
||||
@@ -105,14 +70,14 @@ export const LoadScene = ({ onSceneSelect }: LoadSceneProps) => {
|
||||
const element = scrollContainerRef.current;
|
||||
if (element) {
|
||||
const atBottom =
|
||||
element.scrollHeight - element.scrollTop === element.clientHeight;
|
||||
element.scrollHeight - element.scrollTop <= element.clientHeight + 1;
|
||||
const hasOverflow = element.scrollHeight > element.clientHeight;
|
||||
|
||||
setBottomGradientOpacity(hasOverflow && !atBottom ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
const element = scrollContainerRef.current;
|
||||
if (element) {
|
||||
handleScroll();
|
||||
@@ -122,21 +87,38 @@ export const LoadScene = ({ onSceneSelect }: LoadSceneProps) => {
|
||||
element.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
}, [isSceneLoading]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<Label>My Scenes</Label>
|
||||
<div className="relative flex max-h-[500px] flex-col">
|
||||
<div
|
||||
className="overflow-y-auto overflow-x-hidden"
|
||||
ref={scrollContainerRef}>
|
||||
<ScenePicker
|
||||
scenes={dummyScenes}
|
||||
onSceneSelect={handleSceneSelect}
|
||||
showDate={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative flex max-h-[500px] min-h-[140px] flex-col">
|
||||
{isSceneLoading ? (
|
||||
<div className="flex items-center justify-center gap-3 py-12">
|
||||
<LoadingSpinner />
|
||||
<span className="font-medium opacity-70">Loading scenes...</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{scenes.length !== 0 ? (
|
||||
<div
|
||||
className="overflow-y-auto overflow-x-hidden"
|
||||
ref={scrollContainerRef}>
|
||||
<ScenePicker
|
||||
scenes={scenes}
|
||||
onSceneSelect={handleSceneSelect}
|
||||
showDate={true}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center gap-3 py-12">
|
||||
<span className="font-medium opacity-50">
|
||||
You have no saved scenes yet.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
className="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-10 bg-gradient-to-t from-ui-panel to-transparent transition-opacity duration-200"
|
||||
style={{ opacity: bottomGradientOpacity }}
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useLayoutEffect, useRef, useState } from "react";
|
||||
import { ScenePicker, SceneTypes } from "../ScenePicker";
|
||||
|
||||
interface NewSceneFromTemplateProps {
|
||||
@@ -8,7 +8,7 @@ interface NewSceneFromTemplateProps {
|
||||
export const NewSceneFromTemplate = ({
|
||||
onSceneSelect,
|
||||
}: NewSceneFromTemplateProps) => {
|
||||
// Dummy Data - replace with API data
|
||||
// Hard coded scenes
|
||||
const dummyScenes: SceneTypes[] = [
|
||||
{
|
||||
token: "m_9pf7a7v0138zx58f4x6ejsehjcvfq6",
|
||||
@@ -32,14 +32,14 @@ export const NewSceneFromTemplate = ({
|
||||
const element = scrollContainerRef.current;
|
||||
if (element) {
|
||||
const atBottom =
|
||||
element.scrollHeight - element.scrollTop === element.clientHeight;
|
||||
element.scrollHeight - element.scrollTop <= element.clientHeight + 1;
|
||||
const hasOverflow = element.scrollHeight > element.clientHeight;
|
||||
|
||||
setBottomGradientOpacity(hasOverflow && !atBottom ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
useLayoutEffect(() => {
|
||||
const element = scrollContainerRef.current;
|
||||
if (element) {
|
||||
handleScroll();
|
||||
@@ -53,7 +53,7 @@ export const NewSceneFromTemplate = ({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<div className="relative flex max-h-[500px] flex-col">
|
||||
<div className="relative flex max-h-[500px] min-h-[140px] flex-col">
|
||||
<div
|
||||
className="overflow-y-auto overflow-x-hidden"
|
||||
ref={scrollContainerRef}>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export type SceneTypes = {
|
||||
token: string;
|
||||
name: string;
|
||||
updated_at?: string;
|
||||
thumbnail: string;
|
||||
};
|
||||
|
||||
interface SceneCardProps {
|
||||
scene: SceneTypes;
|
||||
onSceneSelect: (selectedScene: SceneTypes) => void;
|
||||
selectedSceneId: string | null;
|
||||
showDate?: boolean;
|
||||
}
|
||||
|
||||
export const SceneCard: React.FC<SceneCardProps> = ({
|
||||
scene,
|
||||
onSceneSelect,
|
||||
selectedSceneId,
|
||||
showDate,
|
||||
}) => {
|
||||
const handleSelected = (scene: SceneTypes) => {
|
||||
onSceneSelect(scene);
|
||||
};
|
||||
|
||||
// const bucketConfig = useRef(new BucketConfig());
|
||||
// const imageUrl = bucketConfig.current.getCdnUrl(
|
||||
// scene.thumbnail + "-thumb.gif",
|
||||
// 360,
|
||||
// 20,
|
||||
// );
|
||||
// thumbnail will be replaced with 3d scene screenshots
|
||||
const tempThumbnail = "/resources/placeholders/scene_placeholder.png";
|
||||
|
||||
return (
|
||||
<button
|
||||
key={scene.token}
|
||||
className={twMerge(
|
||||
"relative w-full cursor-pointer overflow-hidden rounded-lg border-2 transition-colors ease-in-out ",
|
||||
selectedSceneId === scene.token
|
||||
? "border-brand-primary"
|
||||
: "border-[#4B4B5C] hover:border-ui-controls-button",
|
||||
)}
|
||||
onClick={() => handleSelected(scene)}>
|
||||
<img
|
||||
className="aspect-video object-cover"
|
||||
src={tempThumbnail}
|
||||
alt={scene.name}
|
||||
/>
|
||||
<div className="absolute left-0 top-0 h-full w-full bg-gradient-to-t from-ui-panel to-transparent" />
|
||||
<div className="absolute bottom-[8px] left-[10px] text-start text-sm drop-shadow-md">
|
||||
<div className="flex flex-col">
|
||||
<span className="w-60 truncate text-sm font-medium">
|
||||
{scene.name}
|
||||
</span>
|
||||
{showDate && (
|
||||
<span className="text-xs opacity-70">{scene.updated_at}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className={`absolute right-1.5 top-1.5 h-[22px] w-[22px] transition-opacity duration-200 ease-in-out ${
|
||||
selectedSceneId === scene.token ? "opacity-100" : "opacity-0"
|
||||
}`}>
|
||||
<path
|
||||
opacity="1"
|
||||
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c-9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"
|
||||
fill="#FC6B68"
|
||||
/>
|
||||
<path
|
||||
d="M369 175c-9.4 9.4-9.4 24.6 0 33.9L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c-9.4-9.4 24.6-9.4 33.9 0z"
|
||||
fill="#FFFFFF"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { SceneCard } from "~/pages/PageEnigma/comps/ControlsTopButtons/ScenePicker/SceneCard";
|
||||
|
||||
export type SceneTypes = {
|
||||
token: string;
|
||||
@@ -36,48 +36,13 @@ export const ScenePicker: React.FC<ScenePickerProps> = ({
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{scenes.map((scene) => (
|
||||
<button
|
||||
<SceneCard
|
||||
key={scene.token}
|
||||
className={twMerge(
|
||||
"relative w-[260px] cursor-pointer overflow-hidden rounded-lg border-2 transition-colors ease-in-out ",
|
||||
selectedSceneId === scene.token
|
||||
? "border-brand-primary"
|
||||
: "border-[#4B4B5C] hover:border-ui-controls-button",
|
||||
)}
|
||||
onClick={() => handleSelected(scene)}>
|
||||
<img
|
||||
className="aspect-video object-cover"
|
||||
src={scene.thumbnail}
|
||||
alt={scene.name}
|
||||
/>
|
||||
<div className="absolute left-0 top-0 h-full w-full bg-gradient-to-t from-ui-panel to-transparent" />
|
||||
<div className="absolute bottom-[8px] left-[10px] text-start text-sm drop-shadow-md">
|
||||
<div className="flex flex-col">
|
||||
<span className="w-60 truncate text-sm font-medium">
|
||||
{scene.name}
|
||||
</span>
|
||||
{showDate && (
|
||||
<span className="text-xs opacity-70">{scene.updated_at}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className={`absolute right-1.5 top-1.5 h-[22px] w-[22px] transition-opacity duration-200 ease-in-out ${
|
||||
selectedSceneId === scene.token ? "opacity-100" : "opacity-0"
|
||||
}`}>
|
||||
<path
|
||||
opacity="1"
|
||||
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c-9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"
|
||||
fill="#FC6B68"
|
||||
/>
|
||||
<path
|
||||
d="M369 175c-9.4 9.4-9.4 24.6 0 33.9L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c-9.4-9.4 24.6-9.4 33.9 0z"
|
||||
fill="#FFFFFF"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
scene={scene}
|
||||
onSceneSelect={() => handleSelected(scene)}
|
||||
selectedSceneId={selectedSceneId}
|
||||
showDate={showDate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ export enum MediaFileType {
|
||||
|
||||
// Full Storyteller Engine scenes (RON = Rusty Object Notation)
|
||||
// This is a temporary format that will eventually go away.
|
||||
SceneRon = "scene_ron",
|
||||
Scene = "scene",
|
||||
|
||||
None = "none",
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { timelineScrollX } from "~/pages/PageEnigma/store/timing";
|
||||
|
||||
// timeline
|
||||
export const scale = signal(1);
|
||||
export const filmLength = signal(12);
|
||||
export const filmLength = signal(7);
|
||||
export const timelineHeight = signal(0);
|
||||
|
||||
export const cameraMinimized = signal(false);
|
||||
|
||||
Reference in new issue
Block a user