diff --git a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx index d59e9e3f08..29df29202b 100644 --- a/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx +++ b/frontend/apps/editor3d/app/src/pages/PageEnigma/PageEnigma.tsx @@ -28,6 +28,7 @@ import { PageEditor } from "~/pages/PageEnigma/PageEditor"; import { GalleryDragComponent } from "@storyteller/ui-gallery-modal"; import { LoadingDots } from "@storyteller/ui-loading"; import { PricingModal } from "@storyteller/ui-pricing-modal"; +import { ProviderSetupModal } from "@storyteller/provider-setup-modal"; import { isActionReminderOpen, @@ -35,6 +36,8 @@ import { ActionReminderModal, } from "@storyteller/ui-action-reminder-modal"; import { useFlashUserInputErrorEvent } from "@storyteller/tauri-events"; +import { useShowProviderLoginModalEvent } from "@storyteller/tauri-events"; + export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { useSignals(); @@ -93,6 +96,11 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { toast.error(event.message); }); + useShowProviderLoginModalEvent(async (event) => { + console.log("Show provider login modal event received:", event); + toast.error(event.provider); + }); + useEffect(() => { console.log("installing event listeners"); InstallSounds(); @@ -120,6 +128,7 @@ export const PageEnigma = ({ sceneToken }: { sceneToken?: string }) => { /> + diff --git a/frontend/apps/editor3d/tsconfig.json b/frontend/apps/editor3d/tsconfig.json index d85bc49534..0bc2dd50d0 100644 --- a/frontend/apps/editor3d/tsconfig.json +++ b/frontend/apps/editor3d/tsconfig.json @@ -115,6 +115,9 @@ { "path": "../../libs/components/upload-modal" }, + { + "path": "../../libs/components/provider-setup-modal" + }, { "path": "../../libs/components/modal" }, diff --git a/frontend/libs/common/src/lib/enums/GenerationProvider.ts b/frontend/libs/common/src/lib/enums/GenerationProvider.ts new file mode 100644 index 0000000000..e25d9f1a46 --- /dev/null +++ b/frontend/libs/common/src/lib/enums/GenerationProvider.ts @@ -0,0 +1,6 @@ +export enum GenerationProvider { + Artcraft = "artcraft", + Fal = "fal", + Midjourney = "midjourney", + Sora = "sora", +} diff --git a/frontend/libs/common/src/lib/enums/index.ts b/frontend/libs/common/src/lib/enums/index.ts index 424823d2f9..4fbc722446 100644 --- a/frontend/libs/common/src/lib/enums/index.ts +++ b/frontend/libs/common/src/lib/enums/index.ts @@ -1,8 +1,9 @@ -export * from "./EditorState"; -export * from "./Queue"; -export * from "./UploaderStates"; -export * from "./Hotkeys"; -export * from "./Toaster"; export * from "./AssetFilterOption"; -export * from "./ThumbnailsFileType"; +export * from "./EditorState"; +export * from "./GenerationProvider"; +export * from "./Hotkeys"; export * from "./MediaFileAnimationType"; +export * from "./Queue"; +export * from "./ThumbnailsFileType"; +export * from "./Toaster"; +export * from "./UploaderStates"; diff --git a/frontend/libs/components/provider-setup-modal/.babelrc b/frontend/libs/components/provider-setup-modal/.babelrc new file mode 100644 index 0000000000..1ea870ead4 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nx/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/frontend/libs/components/provider-setup-modal/README.md b/frontend/libs/components/provider-setup-modal/README.md new file mode 100644 index 0000000000..73ba461fa6 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/README.md @@ -0,0 +1,7 @@ +# @frontend/provider-setup-modal + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test @frontend/provider-setup-modal` to execute the unit tests via [Vitest](https://vitest.dev/). diff --git a/frontend/libs/components/provider-setup-modal/package.json b/frontend/libs/components/provider-setup-modal/package.json new file mode 100644 index 0000000000..cc6d4b7b11 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/package.json @@ -0,0 +1,17 @@ +{ + "name": "@storyteller/provider-setup-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" + } + } +} diff --git a/frontend/libs/components/provider-setup-modal/src/index.ts b/frontend/libs/components/provider-setup-modal/src/index.ts new file mode 100644 index 0000000000..27cb6b35a3 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/src/index.ts @@ -0,0 +1 @@ +export * from './lib/provider-setup-modal'; diff --git a/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.module.css b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.module.css new file mode 100644 index 0000000000..d0246c74b9 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.module.css @@ -0,0 +1,7 @@ +/* + * Replace this with your own classes + * + * e.g. + * .container { + * } +*/ \ No newline at end of file diff --git a/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.spec.tsx b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.spec.tsx new file mode 100644 index 0000000000..f97e6c5022 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.spec.tsx @@ -0,0 +1,12 @@ +import { render } from '@testing-library/react'; + +import FrontendProviderSetupModal from './provider-setup-modal'; + +describe('FrontendProviderSetupModal', () => { + + it('should render successfully', () => { + const { baseElement } = render(); + expect(baseElement).toBeTruthy(); + }); + +}); diff --git a/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx new file mode 100644 index 0000000000..15b08a797d --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx @@ -0,0 +1,118 @@ +import { Modal } from "@storyteller/ui-modal"; +import { invoke } from "@tauri-apps/api/core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faArrowRight, + faMagicWandSparkles, +} from "@fortawesome/pro-solid-svg-icons"; +import { Button } from "@storyteller/ui-button"; +import { useState } from "react"; +import { useShowProviderLoginModalEvent } from "@storyteller/tauri-events"; +import { GenerationProvider } from "@storyteller/common"; + +interface ProviderSetupModalProps { +} + +export function ProviderSetupModal({ +}: ProviderSetupModalProps) { + const [showModal, setShowModal] = useState(false); + const [provider, setProvider] = useState(GenerationProvider.Artcraft); + + useShowProviderLoginModalEvent(async (event) => { + console.log("Show provider login modal event received from Tauri:", event); + setProvider(event.provider); + setShowModal(true); + }); + + const serviceProviderName = getServiceProviderName(provider); + + const modalTitle = `Set up ${serviceProviderName}`; + const modalSubTitle = `Add your ${serviceProviderName} account to ArtCraft!`; + + const modalDescription = `You can add your ${serviceProviderName} account to ArtCraft by simply logging in. Use your credits and account directly within Artcraft. You can add all of your AI accounts to Artcraft to use them all in one place and build the ultimate AI art tool.`; + + const modalButtonText = `Set up ${serviceProviderName}`; + + const buttonOnClick = async () => { + switch (provider) { + case GenerationProvider.Midjourney: + await invoke("midjourney_open_login_command"); + break; + case GenerationProvider.Sora: + await invoke("open_sora_login_command"); // TODO: Rename in Tauri + break; + case GenerationProvider.Fal: + break; // TODO: None yet. + default: + break; + } + setShowModal(false); + }; + + return ( + { + setShowModal(false); + }} + className="max-w-2xl max-h-[500px] p-6" + showClose={true} + > +
+
+ +
+ +

+ + {modalTitle} +

+
+

{modalSubTitle}

+ +
+ +

{modalDescription}

+ +
+ +
+
+ + {/*
+ Test +
*/} + +
+
+ ); +} + +function getServiceProviderName(provider: GenerationProvider) : string { + switch (provider) { + case GenerationProvider.Sora: + return "Sora"; + case GenerationProvider.Fal: + return "Fal"; + case GenerationProvider.Midjourney: + return "Midjourney"; + case GenerationProvider.Artcraft: + default: + return "Artcraft"; + } +} + +export default ProviderSetupModal; diff --git a/frontend/libs/components/provider-setup-modal/tsconfig.json b/frontend/libs/components/provider-setup-modal/tsconfig.json new file mode 100644 index 0000000000..12b1e10ef8 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/tsconfig.json @@ -0,0 +1,28 @@ +{ + "files": [], + "include": [], + "references": [ + { + "path": "../button" + }, + { + "path": "../modal" + }, + { + "path": "../../tauri-events" + }, + { + "path": "../../tauri-api" + }, + { + "path": "../../common" + }, + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "extends": "../../../tsconfig.base.json" +} diff --git a/frontend/libs/components/provider-setup-modal/tsconfig.lib.json b/frontend/libs/components/provider-setup-modal/tsconfig.lib.json new file mode 100644 index 0000000000..4b429f449b --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/tsconfig.lib.json @@ -0,0 +1,62 @@ +{ + "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": "../button/tsconfig.lib.json" + }, + { + "path": "../modal/tsconfig.lib.json" + }, + { + "path": "../../tauri-events/tsconfig.lib.json" + }, + { + "path": "../../tauri-api/tsconfig.lib.json" + }, + { + "path": "../../common/tsconfig.lib.json" + } + ] +} diff --git a/frontend/libs/components/provider-setup-modal/tsconfig.spec.json b/frontend/libs/components/provider-setup-modal/tsconfig.spec.json new file mode 100644 index 0000000000..18b7de51b8 --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/tsconfig.spec.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" + } + ] +} diff --git a/frontend/libs/components/provider-setup-modal/vite.config.ts b/frontend/libs/components/provider-setup-modal/vite.config.ts new file mode 100644 index 0000000000..6720cbb26e --- /dev/null +++ b/frontend/libs/components/provider-setup-modal/vite.config.ts @@ -0,0 +1,49 @@ +/// +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/provider-setup-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/provider-setup-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, + } + }, +})); diff --git a/frontend/libs/tauri-api/src/lib/midjourney/MidjourneyGetCredentialInfo.ts b/frontend/libs/tauri-api/src/lib/midjourney/MidjourneyGetCredentialInfo.ts index de9000a778..db5f23a90a 100644 --- a/frontend/libs/tauri-api/src/lib/midjourney/MidjourneyGetCredentialInfo.ts +++ b/frontend/libs/tauri-api/src/lib/midjourney/MidjourneyGetCredentialInfo.ts @@ -12,6 +12,5 @@ export interface MidjourneyGetCredentialInfoSuccess extends CommandResult { export const MidjourneyGetCredentialInfo = async (): Promise => { const result = await invoke("midjourney_get_credential_info_command"); - console.log(">>> mj result", result); return result as MidjourneyGetCredentialInfoSuccess; }; diff --git a/frontend/libs/tauri-events/src/index.ts b/frontend/libs/tauri-events/src/index.ts index c0fffe071d..6dbc91bf26 100644 --- a/frontend/libs/tauri-events/src/index.ts +++ b/frontend/libs/tauri-events/src/index.ts @@ -1,4 +1,5 @@ -export * from './lib/tauri-events'; export * from './lib/common/BasicEventWrapper'; -export * from './lib/events/RefreshAccountStateEvent'; export * from './lib/events/FlashUserInputErrorEvent'; +export * from './lib/events/RefreshAccountStateEvent'; +export * from './lib/events/ShowProviderLoginModalEvent'; +export * from './lib/tauri-events'; diff --git a/frontend/libs/tauri-events/src/lib/events/ShowProviderLoginModalEvent.ts b/frontend/libs/tauri-events/src/lib/events/ShowProviderLoginModalEvent.ts new file mode 100644 index 0000000000..9801cb9d77 --- /dev/null +++ b/frontend/libs/tauri-events/src/lib/events/ShowProviderLoginModalEvent.ts @@ -0,0 +1,35 @@ +import { listen, UnlistenFn } from '@tauri-apps/api/event'; +import { BasicEventWrapper } from '../common/BasicEventWrapper'; +import { useEffect } from 'react'; +import { GenerationProvider } from '@storyteller/common'; + +const EVENT_NAME : string = 'show_provider_login_modal_event'; + +export interface ShowProviderLoginModalEvent { + provider: GenerationProvider, +} + +export const useShowProviderLoginModalEvent = (asyncCallback: (event: ShowProviderLoginModalEvent) => Promise) => { + useEffect(() => { + let isUnmounted = false; + let unlisten: Promise; + + const setup = async () => { + unlisten = listen>(EVENT_NAME, async (wrappedEvent) => { + await asyncCallback(wrappedEvent.payload.data); + }); + + if (isUnmounted) { + unlisten.then(f => f()); // Unsubscribe if unmounted early. + } + }; + + setup(); + + return () => { + isUnmounted = true; + unlisten.then(f => f()); + }; + + }, []); +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 22f5de6bbf..657699274c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1304,6 +1304,10 @@ "name": "@storyteller/ui-promptbox", "version": "0.0.1" }, + "libs/components/provider-setup-modal": { + "name": "@storyteller/provider-setup-modal", + "version": "0.0.1" + }, "libs/components/search": { "name": "@storyteller/ui-search", "version": "0.0.1" @@ -10363,6 +10367,10 @@ "resolved": "libs/model-list", "link": true }, + "node_modules/@storyteller/provider-setup-modal": { + "resolved": "libs/components/provider-setup-modal", + "link": true + }, "node_modules/@storyteller/soundboard": { "resolved": "libs/soundboard", "link": true diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 0a5bffeb2e..5952311a0e 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -152,6 +152,9 @@ }, { "path": "./libs/tauri-events" + }, + { + "path": "./libs/components/provider-setup-modal" } ] }