mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 09:48:44 +00:00
Add action reminder modal component and integrate it into the editor3d app
This commit is contained in:
1 parent
bc90084b37
commit
4a183fe6f3
21 files changed
+511
-23
No files matched your search
@@ -1,4 +1,3 @@
|
||||
import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackProvider";
|
||||
import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent";
|
||||
import { EngineProvider } from "./contexts/EngineContext";
|
||||
import { useActiveJobs } from "~/hooks/useActiveJobs";
|
||||
@@ -28,12 +27,16 @@ import { useGenerationCompleteEvent } from "./TauriEvents/generations/useGenerat
|
||||
import { PageEditor } from "~/pages/PageEnigma/PageEditor";
|
||||
import { GalleryDragComponent } from "@storyteller/ui-gallery-modal";
|
||||
import { LoadingDots } from "@storyteller/ui-loading";
|
||||
import {
|
||||
isActionReminderOpen,
|
||||
actionReminderProps,
|
||||
ActionReminderModal,
|
||||
} from "@storyteller/ui-action-reminder-modal";
|
||||
|
||||
export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
useSignals();
|
||||
useActiveJobs();
|
||||
useBackgroundLoadingMedia();
|
||||
// implement the code to handle incoming messages from the Engine
|
||||
useQueueHandler();
|
||||
|
||||
const [validGpu, setValidGpu] = useState("unknown");
|
||||
@@ -110,8 +113,9 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
return <TurnOnGpu />;
|
||||
}
|
||||
|
||||
return (
|
||||
const currentReminderModalProps = actionReminderProps.value;
|
||||
|
||||
return (
|
||||
<EngineProvider sceneToken={sceneToken}>
|
||||
<PageEditor />
|
||||
<DragComponent />
|
||||
@@ -127,7 +131,22 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => {
|
||||
|
||||
<EditorLoadingBar />
|
||||
<Toaster offsetTop={70} offsetRight={12} />
|
||||
</EngineProvider>
|
||||
|
||||
{currentReminderModalProps && (
|
||||
<ActionReminderModal
|
||||
isOpen={isActionReminderOpen.value}
|
||||
onClose={currentReminderModalProps.onClose}
|
||||
reminderType={currentReminderModalProps.reminderType}
|
||||
onPrimaryAction={currentReminderModalProps.onPrimaryAction}
|
||||
title={currentReminderModalProps.title}
|
||||
message={currentReminderModalProps.message}
|
||||
primaryActionText={currentReminderModalProps.primaryActionText}
|
||||
secondaryActionText={currentReminderModalProps.secondaryActionText}
|
||||
onSecondaryAction={currentReminderModalProps.onSecondaryAction}
|
||||
isLoading={currentReminderModalProps.isLoading}
|
||||
openAiLogo={currentReminderModalProps.openAiLogo}
|
||||
/>
|
||||
)}
|
||||
</EngineProvider>
|
||||
);
|
||||
};
|
||||
@@ -58,6 +58,9 @@
|
||||
{
|
||||
"path": "../../libs/components/button-dropdown"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/action-reminder-modal"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/components/login-modal"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @storyteller/ui-action-reminder-modal
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @storyteller/ui-action-reminder-modal` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-action-reminder-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/action-reminder-modal-signals";
|
||||
export * from "./lib/action-reminder-modal";
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// frontend/apps/editor3d/app/src/signals/actionReminderSignals.ts
|
||||
import { signal } from "@preact/signals-react";
|
||||
import { ReactNode } from "react";
|
||||
import { ReminderType } from "@storyteller/ui-action-reminder-modal";
|
||||
|
||||
export interface ShowActionReminderOptions {
|
||||
reminderType: ReminderType;
|
||||
onPrimaryAction: () => void;
|
||||
title?: string;
|
||||
message?: ReactNode;
|
||||
primaryActionText?: string;
|
||||
secondaryActionText?: string;
|
||||
onSecondaryAction?: () => void;
|
||||
isLoading?: boolean;
|
||||
openAiLogo?: string;
|
||||
}
|
||||
|
||||
interface ActionReminderModalFullProps extends ShowActionReminderOptions {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const isActionReminderOpen = signal(false);
|
||||
export const actionReminderProps = signal<ActionReminderModalFullProps | null>(
|
||||
null
|
||||
);
|
||||
|
||||
export function showActionReminder(options: ShowActionReminderOptions): void {
|
||||
actionReminderProps.value = {
|
||||
...options,
|
||||
onClose: () => {
|
||||
isActionReminderOpen.value = false;
|
||||
},
|
||||
};
|
||||
isActionReminderOpen.value = true;
|
||||
}
|
||||
|
||||
// Example of how to trigger it - BFlat:
|
||||
// import { showGlobalActionReminder } from "@storyteller/ui-action-reminder-modal";
|
||||
// import openAiLogo from 'path/to/logo.svg';
|
||||
//
|
||||
// function someAction() {
|
||||
// showActionReminder({
|
||||
// reminderType: 'soraLogin',
|
||||
// onPrimaryAction: () => console.log('Login with Sora!'),
|
||||
// openAiLogo: openAiLogo,
|
||||
// title: 'Sora Login Needed'
|
||||
// });
|
||||
// }
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import ActionReminderModal from './action-reminder-modal';
|
||||
|
||||
describe('ActionReminderModal', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<ActionReminderModal />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,140 @@
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { Modal } from "@storyteller/ui-modal";
|
||||
import { ReactNode } from "react";
|
||||
import { faRightToBracket } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
export type ReminderType = "default" | "soraLogin" | "artcraftLogin";
|
||||
|
||||
interface ActionReminderModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
reminderType?: ReminderType;
|
||||
onPrimaryAction: () => void;
|
||||
title?: string;
|
||||
hideTitle?: boolean;
|
||||
message?: ReactNode;
|
||||
children?: ReactNode;
|
||||
primaryActionText?: string;
|
||||
secondaryActionText?: string;
|
||||
onSecondaryAction?: () => void;
|
||||
isLoading?: boolean;
|
||||
openAiLogo?: string;
|
||||
modalClassName?: string;
|
||||
}
|
||||
|
||||
export function ActionReminderModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
reminderType = "default",
|
||||
onPrimaryAction,
|
||||
title: customTitle,
|
||||
hideTitle = false,
|
||||
message: customMessage,
|
||||
children: customChildren,
|
||||
primaryActionText: customPrimaryActionText,
|
||||
secondaryActionText: customSecondaryActionText,
|
||||
onSecondaryAction,
|
||||
isLoading = false,
|
||||
openAiLogo,
|
||||
modalClassName: customModalClassName = "",
|
||||
}: ActionReminderModalProps) {
|
||||
let titleText: string | undefined;
|
||||
let messageContent: ReactNode;
|
||||
let primaryActionBtnText: string;
|
||||
let primaryActionIcon = faRightToBracket;
|
||||
|
||||
if (!hideTitle) {
|
||||
switch (reminderType) {
|
||||
case "soraLogin":
|
||||
titleText = customTitle || "Link Your OpenAI Sora Account";
|
||||
break;
|
||||
case "artcraftLogin":
|
||||
titleText = customTitle || "Login to ArtCraft";
|
||||
break;
|
||||
default:
|
||||
titleText = customTitle || "Action Required";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
titleText = undefined;
|
||||
}
|
||||
|
||||
switch (reminderType) {
|
||||
case "soraLogin":
|
||||
titleText = customTitle || "Link Your OpenAI Account";
|
||||
messageContent = customMessage || (
|
||||
<p>
|
||||
To use this feature, please connect your OpenAI account. This allows
|
||||
us to leverage your existing subscription.
|
||||
</p>
|
||||
);
|
||||
primaryActionBtnText = customPrimaryActionText || "Login with OpenAI";
|
||||
break;
|
||||
case "artcraftLogin":
|
||||
messageContent = customMessage || (
|
||||
<p className="text-sm text-white/70">
|
||||
Please log in or sign up to ArtCraft to proceed. This will allow you
|
||||
to save your work and access all features.
|
||||
</p>
|
||||
);
|
||||
primaryActionBtnText = customPrimaryActionText || "Login / Sign Up";
|
||||
break;
|
||||
default:
|
||||
messageContent = customMessage || (
|
||||
<p className="text-sm text-white/70">
|
||||
Please complete the required action.
|
||||
</p>
|
||||
);
|
||||
primaryActionBtnText = customPrimaryActionText || "Proceed";
|
||||
break;
|
||||
}
|
||||
|
||||
const effectiveSecondaryAction = onSecondaryAction || onClose;
|
||||
const effectiveSecondaryActionText = customSecondaryActionText || "Cancel";
|
||||
|
||||
let modalSpecificClasses = "";
|
||||
|
||||
const finalModalClassName =
|
||||
`${customModalClassName} ${modalSpecificClasses}`.trim();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
title={titleText}
|
||||
className={finalModalClassName}
|
||||
>
|
||||
<div className="pt-2">
|
||||
{customChildren ? (
|
||||
<div className="space-y-4">{customChildren}</div>
|
||||
) : (
|
||||
<div className="space-y-4">{messageContent}</div>
|
||||
)}
|
||||
|
||||
<div className="mt-6 flex flex-col sm:flex-row-reverse gap-3">
|
||||
<Button
|
||||
onClick={onPrimaryAction}
|
||||
loading={isLoading}
|
||||
disabled={isLoading}
|
||||
icon={primaryActionIcon}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{primaryActionBtnText}
|
||||
</Button>
|
||||
{(onSecondaryAction || customSecondaryActionText) && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={effectiveSecondaryAction}
|
||||
disabled={isLoading}
|
||||
className="w-full sm:w-auto"
|
||||
>
|
||||
{effectiveSecondaryActionText}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ActionReminderModal;
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../modal"
|
||||
},
|
||||
{
|
||||
"path": "../button"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"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"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../button/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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/action-reminder-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-action-reminder-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,
|
||||
}
|
||||
},
|
||||
}));
|
||||
File renamed without changes.
@@ -14,7 +14,7 @@ import {
|
||||
useSoraLoginListener,
|
||||
} from "@storyteller/tauri-api";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { ArtCraftSignUp } from "./ArtCraftSignUp";
|
||||
import { ArtCraftSignUp } from "./artcraft-signup";
|
||||
import { UsersApi } from "@storyteller/api";
|
||||
interface LoginModalProps {
|
||||
onClose: () => void;
|
||||
@@ -42,11 +42,21 @@ export function LoginModal({
|
||||
const artCraftFormRef = useRef<HTMLFormElement>(null);
|
||||
const [initialArtCraftLoginStatusForUI, setInitialArtCraftLoginStatusForUI] =
|
||||
useState<boolean | null>(null);
|
||||
const [initialSoraSessionStatusForUI, setInitialSoraSessionStatusForUI] =
|
||||
useState<boolean | null>(null);
|
||||
|
||||
const uiTotalSteps = initialArtCraftLoginStatusForUI ? 3 : 4;
|
||||
// Determine total steps and current step for UI
|
||||
let uiTotalSteps = 4;
|
||||
let uiCurrentStep = step;
|
||||
|
||||
if (initialArtCraftLoginStatusForUI) {
|
||||
if (initialSoraSessionStatusForUI) {
|
||||
// If Sora session exists, remove the Sora step (step 3)
|
||||
uiTotalSteps = 3;
|
||||
if (step === 1) uiCurrentStep = 1;
|
||||
else if (step === 2) uiCurrentStep = 2;
|
||||
else if (step === 4) uiCurrentStep = 3;
|
||||
} else if (initialArtCraftLoginStatusForUI) {
|
||||
// If ArtCraft is logged in but not Sora, keep 4 steps
|
||||
uiTotalSteps = 4;
|
||||
if (step === 1) uiCurrentStep = 1;
|
||||
else if (step === 3) uiCurrentStep = 2;
|
||||
else if (step === 4) uiCurrentStep = 3;
|
||||
@@ -67,11 +77,7 @@ export function LoginModal({
|
||||
|
||||
useSoraLoginListener((payload: any) => {
|
||||
console.log("Login success!", payload);
|
||||
if (isLoggedInArtCraft) {
|
||||
setStep(4);
|
||||
} else {
|
||||
setStep(2);
|
||||
}
|
||||
setStep(4); // Always go to the final step after Sora login
|
||||
});
|
||||
|
||||
// Check session on component mount
|
||||
@@ -80,8 +86,13 @@ export function LoginModal({
|
||||
Promise.all([initSession(), checkArtCraftLogin()]).then(
|
||||
([soraSessionExists, loggedIn]) => {
|
||||
setInitialArtCraftLoginStatusForUI(loggedIn || false); // Set initial status for UI step counting
|
||||
setInitialSoraSessionStatusForUI(soraSessionExists || false); // Set initial Sora status for UI step counting
|
||||
|
||||
if (soraSessionExists && !loggedIn) {
|
||||
if (soraSessionExists && loggedIn) {
|
||||
setIsLoggedInArtCraft(true);
|
||||
setIsOpen(false); // Close the modal if fully logged in
|
||||
return;
|
||||
} else if (soraSessionExists && !loggedIn) {
|
||||
// Sora session exists, but not logged in to ArtCraft
|
||||
setIsOpen(true);
|
||||
setStep(2);
|
||||
@@ -96,14 +107,10 @@ export function LoginModal({
|
||||
setIsOpen(true);
|
||||
setStep(1);
|
||||
setIsLoggedInArtCraft(false); // Ensure this is also set
|
||||
} else if (loggedIn) {
|
||||
// Already logged in, no need to show modal
|
||||
setIsLoggedInArtCraft(true);
|
||||
// setIsOpen(false); // This would prevent modal from showing if fully logged in
|
||||
}
|
||||
}
|
||||
);
|
||||
}, []); // Empty dependency array ensures this runs once on mount
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (onOpenChange) onOpenChange(isOpen);
|
||||
@@ -124,14 +131,14 @@ export function LoginModal({
|
||||
const result = await CheckSoraSession();
|
||||
const sessionExists = result.state === SoraSessionState.Valid;
|
||||
if (sessionExists) {
|
||||
setStep(step + 1);
|
||||
setStep(4); // Always go to the final step after Sora login
|
||||
}
|
||||
} else {
|
||||
alert("Please open the desktop app to login");
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
const sessionExists = true;
|
||||
if (sessionExists) {
|
||||
setStep(step + 1);
|
||||
setStep(4); // Always go to the final step after Sora login
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -279,7 +286,13 @@ export function LoginModal({
|
||||
const userInfo = session.data?.user;
|
||||
if (userInfo) onArtCraftAuthSuccess(userInfo);
|
||||
}
|
||||
setStep(3);
|
||||
// Check for Sora session and skip to step 4 if present
|
||||
const soraSessionExists = await initSession();
|
||||
if (soraSessionExists) {
|
||||
setStep(4);
|
||||
} else {
|
||||
setStep(3);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setErrorMessage(
|
||||
|
||||
@@ -37,8 +37,13 @@ import {
|
||||
SoraImageRemix,
|
||||
SoraImageRemixErrorType,
|
||||
SoraImageRemixAspectRatio,
|
||||
CheckSoraSession,
|
||||
SoraSessionState,
|
||||
waitForSoraLogin,
|
||||
} from "@storyteller/tauri-api";
|
||||
import { SoundRegistry } from "@storyteller/soundboard";
|
||||
import { showActionReminder } from "@storyteller/ui-action-reminder-modal";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
interface ReferenceImage {
|
||||
id: string;
|
||||
@@ -243,7 +248,30 @@ export const PromptBox2D = ({
|
||||
setPrompt(e.target.value);
|
||||
};
|
||||
|
||||
// Helper to show Sora login reminder and wait for login
|
||||
const handleSoraLoginReminder = async () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
showActionReminder({
|
||||
reminderType: "soraLogin",
|
||||
onPrimaryAction: async () => {
|
||||
await invoke("open_sora_login_command");
|
||||
await waitForSoraLogin();
|
||||
toast.success("Logged in to Sora!");
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleTauriEnqueue = async () => {
|
||||
// Check if the Sora session is valid
|
||||
const soraSession = await CheckSoraSession();
|
||||
if (soraSession.state !== SoraSessionState.Valid) {
|
||||
setIsEnqueueing(false);
|
||||
await handleSoraLoginReminder();
|
||||
return;
|
||||
}
|
||||
|
||||
const api = new PromptsApi();
|
||||
let image = getCanvasRenderBitmap();
|
||||
if (image === undefined) {
|
||||
@@ -403,7 +431,7 @@ export const PromptBox2D = ({
|
||||
const getCurrentAspectRatioIcon = () => {
|
||||
const selected = aspectRatioList.find((item) => item.selected);
|
||||
if (!selected || !selected.icon) return faRectangle;
|
||||
const iconElement = selected.icon as React.ReactElement;
|
||||
const iconElement = selected.icon as React.ReactElement<any, any>;
|
||||
return iconElement.props.icon;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,3 +12,4 @@ export * from "./lib/sora/LogoutSoraSession.js";
|
||||
export * from "./lib/sora/SoraImageRemix.js";
|
||||
export * from "./lib/sora/useSoraLoginListener.js";
|
||||
export * from "./lib/system/GetBuildInfo.js";
|
||||
export * from "./lib/sora/waitForSoraLogin.js";
|
||||
@@ -0,0 +1,13 @@
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
|
||||
export function waitForSoraLogin(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
let unlisten: () => void;
|
||||
listen("sora-login-success", () => {
|
||||
if (unlisten) unlisten();
|
||||
resolve();
|
||||
}).then((stop) => {
|
||||
unlisten = stop;
|
||||
});
|
||||
});
|
||||
}
|
||||
Generated
+8
@@ -1297,6 +1297,10 @@
|
||||
"version": "0.0.1",
|
||||
"extraneous": true
|
||||
},
|
||||
"libs/components/action-reminder-modal": {
|
||||
"name": "@storyteller/ui-action-reminder-modal",
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"libs/components/badge": {
|
||||
"name": "@storyteller/ui-badge",
|
||||
"version": "0.0.1"
|
||||
@@ -9171,6 +9175,10 @@
|
||||
"resolved": "libs/tauri-utils",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@storyteller/ui-action-reminder-modal": {
|
||||
"resolved": "libs/components/action-reminder-modal",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@storyteller/ui-badge": {
|
||||
"resolved": "libs/components/badge",
|
||||
"link": true
|
||||
|
||||
@@ -128,6 +128,9 @@
|
||||
},
|
||||
{
|
||||
"path": "./libs/components/upload-modal"
|
||||
},
|
||||
{
|
||||
"path": "./libs/components/action-reminder-modal"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user