Compare commits

...
Author SHA1 Message Date
bflatastic 4daa98d47e replaced video: had black screen at end 2025-05-06 13:13:04 +07:00
bflatastic 46b70de389 login modal shared 2025-05-06 12:53:38 +07:00
bflatastic 82ed0c5da3 shared login modal component init 2025-05-06 10:26:23 +07:00
22 changed files with 513 additions and 37 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -1,5 +1,4 @@
import React, { useEffect } from "react";
import { invoke } from "@tauri-apps/api/core";
import React, { useEffect, useState } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { LoadingDots, TopBar } from "~/components";
import { Controls3D } from "./comps/Controls3D";
@@ -23,13 +22,13 @@ import { Outliner } from "./comps/Outliner";
import { CameraAspectRatio } from "./enums";
import { PromptBox } from "./comps/PromptBox";
import { OnboardingHelper } from "./comps/OnboardingHelper";
import { IsDesktopApp } from "@storyteller/tauri-utils";
import { FocalLengthDisplay } from "./comps/FocalLengthDisplay/FocalLengthDisplay";
import { DemoModal } from "@storyteller/ui-demo-modal";
import { appTabId, setAppTabId } from "~/signals/appTab";
import { LoginModal } from "@storyteller/ui-login-modal";
export const PageEditor = () => {
useSignals();
const [showLoginModal, setShowLoginModal] = useState(true);
//console.log(api());
//To prevent the click event from propagating to the canvas: TODO: HANDLE THIS BETTER?
const handleOverlayClick = (event: React.MouseEvent<HTMLDivElement>) => {
@@ -158,7 +157,7 @@ export const PageEditor = () => {
<Timeline />
</div> */}
<DemoModal
{/* <DemoModal
title="Welcome to ArtCraft 3D"
subTitle="Your 3D editor for digital art and design"
description="Set up your scene by adding objects and start bringing your ideas to life!"
@@ -171,6 +170,13 @@ export const PageEditor = () => {
console.error("This is not the Desktop app.");
}
}}
/> */}
<LoginModal
onClose={() => {}}
videoSrc2D="/resources/videos/artcraft-canvas-demo.mp4"
videoSrc3D="/resources/videos/artcraft-3d-demo.mp4"
openAiLogo="/resources/images/openai-logo.png"
/>
</div>
);
+9 -6
View File
@@ -54,6 +54,15 @@
{
"path": "../../libs/components/demo-modal"
},
{
"path": "../../libs/soundboard"
},
{
"path": "../../libs/components/tab-selector"
},
{
"path": "../../libs/api"
},
{
"path": "../../libs/components/tooltip"
},
@@ -65,12 +74,6 @@
},
{
"path": "../../libs/tauri-utils"
},
{
"path": "../../libs/api"
},
{
"path": "../../libs/soundboard"
}
]
}
@@ -54,7 +54,6 @@ export function DemoModal({
<video autoPlay muted loop controls={false}>
<source src={videoSrc} type="video/mp4" />
</video>
W
</div>
<Button
className="font-semibold"
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
@@ -0,0 +1,7 @@
# @storyteller/ui-login-modal
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test @storyteller/ui-login-modal` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,17 @@
{
"name": "@storyteller/ui-login-modal",
"version": "0.0.1",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"development": "./src/index.ts",
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
}
}
@@ -0,0 +1,2 @@
export * from './lib/login-modal';
@@ -0,0 +1,7 @@
/*
* Replace this with your own classes
*
* e.g.
* .container {
* }
*/
@@ -0,0 +1,12 @@
import { render } from '@testing-library/react';
import LoginModal from './login-modal';
describe('LoginModal', () => {
it('should render successfully', () => {
const { baseElement } = render(<LoginModal />);
expect(baseElement).toBeTruthy();
});
});
@@ -0,0 +1,271 @@
import { Button } from "@storyteller/ui-button";
import { Transition, TransitionChild } from "@headlessui/react";
import { createPortal } from "react-dom";
import { useState, useEffect } from "react";
import {
faArrowRight,
faRightToBracket,
} from "@fortawesome/pro-solid-svg-icons";
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
import { IsDesktopApp } from "@storyteller/tauri-utils";
import { invoke } from "@tauri-apps/api/core";
interface LoginModalProps {
onClose: () => void;
videoSrc2D: string;
videoSrc3D: string;
openAiLogo: string;
}
export function LoginModal({
onClose,
videoSrc2D,
videoSrc3D,
openAiLogo,
}: LoginModalProps) {
const [step, setStep] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const [hasSession, setHasSession] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const totalSteps = 3;
const checkSession = async () => {
// Bombay: This will be replaced with actual session check
// For testing: return true to simulate having a session, false to simulate no session
const sessionExists = false;
setHasSession(sessionExists);
return sessionExists;
};
// Check session on component mount
useEffect(() => {
const initSession = async () => {
const sessionExists = await checkSession();
if (!sessionExists) {
setIsOpen(true);
setStep(1);
} else {
setIsOpen(false);
}
};
initSession();
}, [hasSession]);
const handleClose = () => {
setIsOpen(false);
onClose();
};
const handleNext = async () => {
if (step === 2) {
setIsLoading(true);
try {
if (IsDesktopApp()) {
await invoke("open_sora_login_command");
const sessionExists = await checkSession();
if (sessionExists) {
setStep(step + 1);
}
} else {
const sessionExists = true;
alert("Please open the desktop app to login");
if (sessionExists) {
setStep(step + 1);
}
}
} catch (error) {
console.error("Login failed:", error);
} finally {
setIsLoading(false);
}
} else if (step < totalSteps) {
setStep(step + 1);
}
};
const handleBack = () => {
if (step > 1) setStep(step - 1);
};
// Progress bar rendering
const renderProgress = () => (
<div className="flex items-center justify-center gap-2 mb-2">
{[...Array(totalSteps)].map((_, idx) => (
<div
key={idx}
className={`h-1.5 rounded transition-all duration-300 w-16 ${
idx < step ? "bg-primary" : "bg-white/30"
}`}
/>
))}
</div>
);
// Step content rendering
const renderStepContent = () => {
switch (step) {
case 1:
return (
<div className="flex flex-col items-center justify-center h-full">
<h2 className="text-3xl font-bold mb-2 text-center">
Welcome to ArtCraft
</h2>
<p className="text-white/70 mb-6 text-center">
Here's what you can do...
</p>
<div className="grid grid-cols-2 gap-6 h-full grow">
<div className="h-full">
<div className="aspect-[4/3] w-full overflow-hidden bg-black/20 rounded-t-lg">
<video
className="object-cover w-full h-full"
autoPlay
muted
loop
controls={false}
>
<source src={videoSrc2D} type="video/mp4" />
</video>
</div>
<p className="text-center px-1.5 py-2 text-white/90 bg-black/20 rounded-b-lg font-medium text-sm">
2D Canvas
</p>
</div>
<div>
<div className="aspect-[4/3] w-full overflow-hidden bg-black/20 rounded-t-lg">
<video
className="object-cover w-full h-full"
autoPlay
muted
loop
controls={false}
>
<source src={videoSrc3D} type="video/mp4" />
</video>
</div>
<p className="text-center px-1.5 py-2 text-white/90 bg-black/20 rounded-b-lg font-medium text-sm">
3D Scene Editor
</p>
</div>
</div>
</div>
);
case 2:
return (
<div className="flex flex-col items-center justify-center h-full">
<h2 className="text-3xl font-bold mb-2 text-center">
Login with your OpenAI account
</h2>
<p className="text-white/70 mb-6 text-center px-16">
We're the application layer for images and video. To provide you
with <b>100% free service</b>, we use your existing AI accounts.
To start off, you'll need to add your OpenAI Sora account. If you
have a ChatGPT subscription, you can log in with Sora.
</p>
<img
src={openAiLogo}
alt="OpenAI Logo"
className="w-72 h-72 mx-auto grow"
/>
</div>
);
case 3:
return (
<div className="flex flex-col items-center justify-center h-full">
<h2 className="text-3xl font-bold mb-2 text-center">
Thank you for signing in!
</h2>
<p className="text-white/70 mb-6 text-center px-24">
You're all set to start creating amazing content. Join our Discord
community to connect with other creators, share your work, and get
the latest updates.
</p>
<Button
variant="secondary"
onClick={() =>
window.open("https://discord.gg/75svZP2Vje", "_blank")
}
icon={faDiscord}
className="text-md bg-[#5865F2] hover:bg-[#6a76ff] rounded-xl"
>
Join our Discord
</Button>
</div>
);
default:
return null;
}
};
return createPortal(
<Transition appear show={isOpen}>
<div className="fixed inset-0 z-[100]">
<TransitionChild
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 cursor-pointer bg-black/60" />
</TransitionChild>
<div className="fixed inset-0 flex items-center justify-center p-4">
<TransitionChild
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div
className="relative h-[660px] max-w-4xl w-full rounded-xl bg-[#2C2C2C] text-white shadow-lg border border-white/5"
onClick={(e) => e.stopPropagation()}
>
<div className="flex flex-col gap-4 p-8 h-full">
<span className="text-sm text-center opacity-60 font-medium">
Step {step} of {totalSteps}
</span>
{renderProgress()}
{renderStepContent()}
<div className="flex items-end justify-center gap-2.5 mt-10 grow">
{step > 1 && step !== 3 ? (
<Button
variant="secondary"
onClick={handleBack}
disabled={isLoading}
>
Back
</Button>
) : null}
{step < totalSteps ? (
<Button
icon={step === 2 ? faRightToBracket : faArrowRight}
iconFlip={step !== 2}
onClick={handleNext}
loading={isLoading}
disabled={isLoading}
>
{step === 2 ? "Login with OpenAI" : "Continue"}
</Button>
) : (
<Button
icon={faArrowRight}
iconFlip={true}
onClick={handleClose}
>
Start Creating Now
</Button>
)}
</div>
</div>
</div>
</TransitionChild>
</div>
</div>
</Transition>,
document.body
);
}
export default LoginModal;
@@ -0,0 +1,13 @@
{
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../../tsconfig.base.json"
}
@@ -0,0 +1,45 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"types": [
"node",
"@nx/react/typings/cssmodule.d.ts",
"@nx/react/typings/image.d.ts",
"vite/client"
],
"rootDir": "src",
"jsx": "react-jsx",
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
},
"exclude": [
"out-tsc",
"dist",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
],
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx"
]
}
@@ -0,0 +1,34 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/vitest",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
],
"jsx": "react-jsx"
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}
@@ -0,0 +1,49 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import * as path from 'path';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../../node_modules/.vite/libs/components/login-modal',
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
// Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode
build: {
outDir: './dist',
emptyOutDir: true,
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
lib: {
// Could also be a dictionary or array of multiple entry points.
entry: 'src/index.ts',
name: '@storyteller/ui-login-modal',
fileName: 'index',
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ['es' as const]
},
rollupOptions: {
// External packages that should not be bundled into your library.
external: ['react','react-dom','react/jsx-runtime']
},
},
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: './test-output/vitest/coverage',
provider: 'v8' as const,
}
},
}));
@@ -2,27 +2,21 @@
"files": [],
"include": [],
"references": [
{
"path": "../modal"
},
{
"path": "../select"
},
{
"path": "../../tauri-api"
},
{
"path": "../input"
},
{
"path": "../button"
},
{
"path": "../modal"
},
{
"path": "../../api"
},
{
"path": "../../tauri-api"
},
{
"path": "../../tauri-utils"
},
{
"path": "../../soundboard"
},
@@ -43,29 +43,23 @@
"src/**/*.tsx"
],
"references": [
{
"path": "../modal/tsconfig.lib.json"
},
{
"path": "../select/tsconfig.lib.json"
},
{
"path": "../../tauri-api/tsconfig.lib.json"
},
{
"path": "../input/tsconfig.lib.json"
},
{
"path": "../button/tsconfig.lib.json"
},
{
"path": "../modal/tsconfig.lib.json"
},
{
"path": "../../api/tsconfig.lib.json"
},
{
"path": "../../tauri-api/tsconfig.lib.json"
},
{
"path": "../../tauri-utils/tsconfig.lib.json"
},
{
"path": "../../soundboard/tsconfig.lib.json"
},
}
]
}
+8
View File
@@ -2215,6 +2215,10 @@
"name": "@storyteller/ui-loading-spinner",
"version": "0.0.1"
},
"libs/components/login-modal": {
"name": "@storyteller/ui-login-modal",
"version": "0.0.1"
},
"libs/components/modal": {
"name": "@storyteller/ui-modal",
"version": "0.0.1"
@@ -10320,6 +10324,10 @@
"resolved": "libs/components/loading-spinner",
"link": true
},
"node_modules/@storyteller/ui-login-modal": {
"resolved": "libs/components/login-modal",
"link": true
},
"node_modules/@storyteller/ui-modal": {
"resolved": "libs/components/modal",
"link": true
+3
View File
@@ -65,6 +65,9 @@
},
{
"path": "./libs/tauri-api"
},
{
"path": "./libs/components/login-modal"
}
]
}