From 53c99bb2b3e2112255065d94acf6699e08811236 Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Wed, 12 Feb 2025 11:09:44 +0200 Subject: [PATCH 01/13] feat(): w builder page structure, world data form WIP --- .../src/components/app-routes/index.tsx | 77 ++++---- .../save-config-success.tsx => dashboard.tsx} | 4 +- .../src/components/pages/home-page.tsx | 2 +- .../src/components/pages/index.ts | 9 +- .../save-config-step1-page.tsx | 47 ----- .../save-config-step2-page.tsx | 47 ----- .../save-config-step3-page.tsx | 47 ----- .../components/pages/world-config-form.tsx | 90 ++++++++++ .../src/components/pages/world-data-form.tsx | 165 ++++++++++++++++++ ...main-page.tsx => world-meta-info-form.tsx} | 68 ++++---- extensions/apps/world-builder/src/routes.ts | 12 +- 11 files changed, 339 insertions(+), 229 deletions(-) rename extensions/apps/world-builder/src/components/pages/{save-config-form/save-config-success.tsx => dashboard.tsx} (94%) delete mode 100644 extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step1-page.tsx delete mode 100644 extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step2-page.tsx delete mode 100644 extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step3-page.tsx create mode 100644 extensions/apps/world-builder/src/components/pages/world-config-form.tsx create mode 100644 extensions/apps/world-builder/src/components/pages/world-data-form.tsx rename extensions/apps/world-builder/src/components/pages/{save-config-form/save-config-main-page.tsx => world-meta-info-form.tsx} (56%) diff --git a/extensions/apps/world-builder/src/components/app-routes/index.tsx b/extensions/apps/world-builder/src/components/app-routes/index.tsx index 9717facb8..574319674 100644 --- a/extensions/apps/world-builder/src/components/app-routes/index.tsx +++ b/extensions/apps/world-builder/src/components/app-routes/index.tsx @@ -8,13 +8,12 @@ import { redirect, } from '@tanstack/react-router'; import { ICreateRouter, IRouterContext } from '@akashaorg/typings/lib/ui'; -import { HomePage } from '../pages/index'; import { - SaveConfigMainPage, - SaveConfigStep1Page, - SaveConfigStep2Page, - SaveConfigStep3Page, - SaveConfigSuccessPage, + HomePage, + DashboardPage, + WorldConfigFormPage, + WorldMetaInfoFormPage, + WorldDataFormPage, } from '../pages/index'; import { NotFoundComponent } from './not-found-component'; import { RouteErrorComponent } from './error-component'; @@ -44,77 +43,65 @@ const homeRoute = createRoute({ }, }); -const saveConfigMainRoute = createRoute({ +const dashboardRoute = createRoute({ getParentRoute: () => rootRoute, - path: `/save-config`, + path: `/dashboard`, notFoundComponent: () => , component: () => { return ( 'save_config_main_reset'} + getResetKey={() => 'dashboard_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, }); -const saveConfigStep1Route = createRoute({ - getParentRoute: () => saveConfigMainRoute, - path: '/step1', - component: () => { - return ( - 'save_config_step1_reset'} - errorComponent={RouteErrorComponent} - > - - - ); - }, -}); - -const saveConfigStep2Route = createRoute({ - getParentRoute: () => saveConfigMainRoute, - path: '/step2', +const worldDataRoute = createRoute({ + getParentRoute: () => rootRoute, + path: `/world-data`, + notFoundComponent: () => , component: () => { return ( 'save_config_step2_reset'} + getResetKey={() => 'world_data_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, }); -const saveConfigStep3Route = createRoute({ - getParentRoute: () => saveConfigMainRoute, - path: '/step3', +const worldConfigRoute = createRoute({ + getParentRoute: () => rootRoute, + path: `/world-config`, + notFoundComponent: () => , component: () => { return ( 'save_config_step3_reset'} + getResetKey={() => 'world_config_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, }); -const saveConfigSuccessRoute = createRoute({ - getParentRoute: () => saveConfigMainRoute, - path: '/success', +const worldMetaInfoRoute = createRoute({ + getParentRoute: () => rootRoute, + path: `/world-meta-info`, + notFoundComponent: () => , component: () => { return ( 'save_config_success_page_reset'} + getResetKey={() => 'world_meta_info_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, @@ -123,12 +110,10 @@ const saveConfigSuccessRoute = createRoute({ const routeTree = rootRoute.addChildren([ defaultRoute, homeRoute, - saveConfigMainRoute.addChildren([ - saveConfigStep1Route, - saveConfigStep2Route, - saveConfigStep3Route, - saveConfigSuccessRoute, - ]), + dashboardRoute, + worldDataRoute, + worldConfigRoute, + worldMetaInfoRoute, ]); export const router = ({ baseRouteName, apolloClient }: ICreateRouter) => diff --git a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-success.tsx b/extensions/apps/world-builder/src/components/pages/dashboard.tsx similarity index 94% rename from extensions/apps/world-builder/src/components/pages/save-config-form/save-config-success.tsx rename to extensions/apps/world-builder/src/components/pages/dashboard.tsx index 45535b0e0..cb0e69ec0 100644 --- a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-success.tsx +++ b/extensions/apps/world-builder/src/components/pages/dashboard.tsx @@ -12,12 +12,12 @@ import { import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { Image, ImageRoot } from '@akashaorg/ui/lib/akasha-components/image'; -export const SaveConfigSuccessPage: React.FC = () => { +export const DashboardPage: React.FC = () => { const navigate = useNavigate(); const { t } = useTranslation('app-world-builder'); const handleOpenPreview = () => { - navigate({ to: '/save-config/success' }); + navigate({ to: '/home' }); }; // TODO fetch real data diff --git a/extensions/apps/world-builder/src/components/pages/home-page.tsx b/extensions/apps/world-builder/src/components/pages/home-page.tsx index 0a8dbd883..810c8e678 100644 --- a/extensions/apps/world-builder/src/components/pages/home-page.tsx +++ b/extensions/apps/world-builder/src/components/pages/home-page.tsx @@ -47,7 +47,7 @@ export const HomePage: React.FC = () => { }; const handleNavigateToForm = () => { - navigate({ to: '/save-config/step1' }); + navigate({ to: '/world-data' }); }; const [acceptedTerms, setAcceptedTerms] = React.useState(false); diff --git a/extensions/apps/world-builder/src/components/pages/index.ts b/extensions/apps/world-builder/src/components/pages/index.ts index 42422556a..acb065f21 100644 --- a/extensions/apps/world-builder/src/components/pages/index.ts +++ b/extensions/apps/world-builder/src/components/pages/index.ts @@ -1,6 +1,5 @@ export * from './home-page'; -export * from './save-config-form/save-config-main-page'; -export * from './save-config-form/save-config-step1-page'; -export * from './save-config-form/save-config-step2-page'; -export * from './save-config-form/save-config-step3-page'; -export * from './save-config-form/save-config-success'; +export * from './dashboard'; +export * from './world-data-form'; +export * from './world-config-form'; +export * from './world-meta-info-form'; diff --git a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step1-page.tsx b/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step1-page.tsx deleted file mode 100644 index 8cf984e4c..000000000 --- a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step1-page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from '@tanstack/react-router'; -import { Button } from '@akashaorg/ui/lib/akasha-components/button'; -import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; -import { Stepper } from '@akashaorg/ui/lib/akasha-components/stepper'; -import { - CardContent, - CardFooter, - CardHeader, - CardTitle, -} from '@akashaorg/ui/lib/akasha-components/card'; -import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; - -export const SaveConfigStep1Page: React.FC = () => { - const navigate = useNavigate(); - const { t } = useTranslation('app-world-builder'); - - const handleNavigateToStep2 = () => { - navigate({ to: '/save-config/step2' }); - }; - const handleCancel = () => { - navigate({ to: '/home' }); - }; - - return ( - <> - - - - - - {t('World Config')} - - - - - - - - - ); -}; diff --git a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step2-page.tsx b/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step2-page.tsx deleted file mode 100644 index 89c3dd360..000000000 --- a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step2-page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from '@tanstack/react-router'; -import { Button } from '@akashaorg/ui/lib/akasha-components/button'; -import { Stepper } from '@akashaorg/ui/lib/akasha-components/stepper'; -import { - CardContent, - CardFooter, - CardHeader, - CardTitle, -} from '@akashaorg/ui/lib/akasha-components/card'; -import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; -import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; - -export const SaveConfigStep2Page: React.FC = () => { - const navigate = useNavigate(); - const { t } = useTranslation('app-world-builder'); - - const handleNavigateToStep3 = () => { - navigate({ to: '/save-config/step3' }); - }; - const handleNavigateBack = () => { - navigate({ to: '/save-config/step1' }); - }; - - return ( - <> - - - - - - {t('World Default Apps')} - - - - - - - - - ); -}; diff --git a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step3-page.tsx b/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step3-page.tsx deleted file mode 100644 index 384aa8c98..000000000 --- a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-step3-page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from '@tanstack/react-router'; -import { Button } from '@akashaorg/ui/lib/akasha-components/button'; -import { Stepper } from '@akashaorg/ui/lib/akasha-components/stepper'; -import { - CardContent, - CardFooter, - CardHeader, - CardTitle, -} from '@akashaorg/ui/lib/akasha-components/card'; -import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; -import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; - -export const SaveConfigStep3Page: React.FC = () => { - const navigate = useNavigate(); - const { t } = useTranslation('app-world-builder'); - - const handleSaveConfig = () => { - navigate({ to: '/save-config/success' }); - }; - const handleNavigateBack = () => { - navigate({ to: '/save-config/step2' }); - }; - - return ( - <> - - - - - - {t(`World's Foundations`)} - - - - - - - - - ); -}; diff --git a/extensions/apps/world-builder/src/components/pages/world-config-form.tsx b/extensions/apps/world-builder/src/components/pages/world-config-form.tsx new file mode 100644 index 000000000..ea53fae02 --- /dev/null +++ b/extensions/apps/world-builder/src/components/pages/world-config-form.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import appRoutes, { WORLD_DATA_FORM } from '../../routes'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from '@tanstack/react-router'; +import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; +import { Stepper } from '@akashaorg/ui/lib/akasha-components/stepper'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@akashaorg/ui/lib/akasha-components/card'; +import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; +import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; +import { + ErrorLoader, + ErrorLoaderTitle, + ErrorLoaderDescription, + ErrorLoaderFooter, +} from '@akashaorg/ui/lib/akasha-components/error-loader'; + +export const WorldConfigFormPage: React.FC = () => { + const { t } = useTranslation('app-extensions'); + + const { baseRouteName, getCorePlugins } = useRootComponentProps(); + const navigate = useNavigate(); + const navigateTo = getCorePlugins().routing.navigateTo; + + const { + data: { authenticatedDID }, + } = useAkashaStore(); + + const handleConnectButtonClick = () => { + navigateTo?.({ + appName: '@akashaorg/app-auth-ewa', + getNavigationUrl: (routes: Record) => { + return `${routes.Connect}?${new URLSearchParams({ + redirectTo: `${baseRouteName}/${appRoutes[WORLD_DATA_FORM]}`, + }).toString()}`; + }, + }); + }; + + const handleSave = () => { + navigate({ to: '/dashboard' }); + }; + const handleCancel = () => { + navigate({ to: '/dashboard' }); + }; + + if (!authenticatedDID) { + return ( + + {`${t('Uh-oh')}! ${t('You are not connected')}!`} + + {`${t('To create a world configuration you must be connected')} ⚡️`} + + + + + + ); + } + + return ( + + + + + + + {t('World Config')} + + + + + + + + + ); +}; diff --git a/extensions/apps/world-builder/src/components/pages/world-data-form.tsx b/extensions/apps/world-builder/src/components/pages/world-data-form.tsx new file mode 100644 index 000000000..243ae8569 --- /dev/null +++ b/extensions/apps/world-builder/src/components/pages/world-data-form.tsx @@ -0,0 +1,165 @@ +import React from 'react'; +import appRoutes, { WORLD_DATA_FORM } from '../../routes'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from '@tanstack/react-router'; +import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@akashaorg/ui/lib/akasha-components/card'; +import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; +import { useAkashaStore, useRootComponentProps, useSaveImage } from '@akashaorg/ui-core-hooks'; +import { + ErrorLoader, + ErrorLoaderTitle, + ErrorLoaderDescription, + ErrorLoaderFooter, +} from '@akashaorg/ui/lib/akasha-components/error-loader'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@akashaorg/ui/lib/akasha-components/form'; +import { Input } from '@akashaorg/ui/lib/components/input'; +import { NotificationEvents, NotificationTypes } from '@akashaorg/typings/lib/ui'; +import getSDK from '@akashaorg/core-sdk'; + +export const WorldDataFormPage: React.FC = () => { + const { t } = useTranslation('app-extensions'); + + const { uiEvents, baseRouteName, getCorePlugins } = useRootComponentProps(); + + const uiEventsRef = React.useRef(uiEvents); + + const navigate = useNavigate(); + const navigateTo = getCorePlugins().routing.navigateTo; + + const sdk = React.useRef(getSDK()); + + const indexingDID = sdk.current.services.gql.indexingDID; + + const { + data: { authenticatedDID }, + } = useAkashaStore(); + + const handleConnectButtonClick = () => { + navigateTo?.({ + appName: '@akashaorg/app-auth-ewa', + getNavigationUrl: (routes: Record) => { + return `${routes.Connect}?${new URLSearchParams({ + redirectTo: `${baseRouteName}/${appRoutes[WORLD_DATA_FORM]}`, + }).toString()}`; + }, + }); + }; + + const showErrorNotification = React.useCallback((title: string) => { + uiEventsRef.current.next({ + event: NotificationEvents.ShowNotification, + data: { + type: NotificationTypes.Error, + title, + }, + }); + }, []); + + const FormSchema = z.object({ + name: z.string().min(2, { + message: t('World name must be at least 2 characters.'), + }), + instanceUrl: z.string().url({ message: t('Must be URL') }), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + name: '', + }, + }); + + const { + image: worldImage, + saveImage: saveWorldImage, + loading: isSavingWorldImage, + } = useSaveImage(); + + const onSaveImageError = () => { + showErrorNotification(t("The image wasn't uploaded correctly. Please try again!")); + }; + + const onSubmit = (data: z.infer) => { + console.log('form data: ', data); + navigate({ to: '/dashboard' }); + }; + const handleCancel = () => { + navigate({ to: '/home' }); + }; + + if (!authenticatedDID) { + return ( + + {`${t('Uh-oh')}! ${t('You are not connected')}!`} + + {`${t('To create a world configuration you must be connected')} ⚡️`} + + + + + + ); + } + + return ( + + + + {t('Create Your World')} + + {' '} +
+ + + ( + + {t('World Name')} + + + + + {t(`Remember, the world's name cannot be changed once it is set.`)} + + + + )} + /> + + + + + + +
+ +
+ ); +}; diff --git a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-main-page.tsx b/extensions/apps/world-builder/src/components/pages/world-meta-info-form.tsx similarity index 56% rename from extensions/apps/world-builder/src/components/pages/save-config-form/save-config-main-page.tsx rename to extensions/apps/world-builder/src/components/pages/world-meta-info-form.tsx index 16c2175e2..ad1a7c6db 100644 --- a/extensions/apps/world-builder/src/components/pages/save-config-form/save-config-main-page.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-meta-info-form.tsx @@ -1,8 +1,16 @@ -import React, { createContext } from 'react'; -import { Outlet } from '@tanstack/react-router'; -import { atomWithStorage, createJSONStorage } from 'jotai/utils'; -import appRoutes, { SAVE_CONFIG } from '../../../routes'; +import React from 'react'; +import appRoutes, { WORLD_DATA_FORM } from '../../routes'; import { useTranslation } from 'react-i18next'; +import { useNavigate } from '@tanstack/react-router'; +import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@akashaorg/ui/lib/akasha-components/card'; +import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; import { ErrorLoader, @@ -10,51 +18,36 @@ import { ErrorLoaderDescription, ErrorLoaderFooter, } from '@akashaorg/ui/lib/akasha-components/error-loader'; -import { Card } from '@akashaorg/ui/lib/akasha-components/card'; -import { Button } from '@akashaorg/ui/lib/akasha-components/button'; - -export const SAVE_WORLD_CONFIG_FORM = 'save-world-config-form'; - -export const AtomContext = createContext(null); - -const storage = createJSONStorage(() => sessionStorage); - -export type FormData = { - lastCompletedStep?: number; - name?: string; -}; -export const SaveConfigMainPage: React.FC = () => { +export const WorldMetaInfoFormPage: React.FC = () => { const { t } = useTranslation('app-extensions'); const { baseRouteName, getCorePlugins } = useRootComponentProps(); - + const navigate = useNavigate(); const navigateTo = getCorePlugins().routing.navigateTo; const { data: { authenticatedDID }, } = useAkashaStore(); - const formData = atomWithStorage( - SAVE_WORLD_CONFIG_FORM, - { - lastCompletedStep: 0, - name: '', - }, - storage, - ); - const handleConnectButtonClick = () => { navigateTo?.({ appName: '@akashaorg/app-auth-ewa', getNavigationUrl: (routes: Record) => { return `${routes.Connect}?${new URLSearchParams({ - redirectTo: `${baseRouteName}/${appRoutes[SAVE_CONFIG]}/step1`, + redirectTo: `${baseRouteName}/${appRoutes[WORLD_DATA_FORM]}`, }).toString()}`; }, }); }; + const handleSave = () => { + navigate({ to: '/dashboard' }); + }; + const handleCancel = () => { + navigate({ to: '/dashboard' }); + }; + if (!authenticatedDID) { return ( @@ -73,9 +66,20 @@ export const SaveConfigMainPage: React.FC = () => { return ( - - - + + + {t('Customise your World')} + + + + + + + ); }; diff --git a/extensions/apps/world-builder/src/routes.ts b/extensions/apps/world-builder/src/routes.ts index ece5ac2c0..d17e3dd01 100644 --- a/extensions/apps/world-builder/src/routes.ts +++ b/extensions/apps/world-builder/src/routes.ts @@ -1,7 +1,15 @@ export const HOME = 'Home'; -export const SAVE_CONFIG = 'Save Config'; +export const DASHBOARD = 'Dashboard'; +export const WORLD_DATA_FORM = 'World Data From'; +export const WORLD_CONFIG_FORM = 'World Config Form'; +export const WORLD_META_INFO_FORM = 'World Meta Info Form'; +export const MY_WORLD = 'My World'; export default { [HOME]: '/home', - [SAVE_CONFIG]: '/save-config', + [DASHBOARD]: '/dashboard', + [WORLD_DATA_FORM]: '/world-data-form', + [WORLD_CONFIG_FORM]: '/world-config-form', + [WORLD_META_INFO_FORM]: '/world-meta-info-form', + [MY_WORLD]: '/my-world', }; From b39bbb4a47f1badbb4073fbbc889392c308d0f69 Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Thu, 20 Feb 2025 10:05:29 +0200 Subject: [PATCH 02/13] feat(): hooks, pages updates --- .../src/components/app-routes/index.tsx | 122 +++++-- .../src/components/pages/index.ts | 13 +- .../src/components/pages/main/dashboard.tsx | 131 +++++++ .../landing-page-component.tsx} | 47 +-- .../config-success.tsx} | 18 +- .../world-config/world-config-form-step1.tsx | 166 +++++++++ .../world-config/world-config-form-step2.tsx | 53 +++ .../world-config-main-page.tsx} | 80 +++-- .../pages/world-create/create-success.tsx | 70 ++++ .../pages/world-create/world-create-form.tsx | 332 ++++++++++++++++++ .../world-customize-form.tsx} | 6 +- .../src/components/pages/world-data-form.tsx | 165 --------- extensions/apps/world-builder/src/routes.ts | 12 +- .../src/__generated__/graphql-api.ts | 27 ++ libs/hooks/src/generated/apollo.ts | 61 ++++ .../get-worlds-by-creator-did-query.ts | 26 ++ libs/sdk/src/gql/index.ts | 1 + .../registry-mutations-composedb.graphql | 1 + .../sdk/src/registry/registry-queries.graphql | 22 ++ .../src/sdk/graphql-operation-types-new.ts | 25 +- 20 files changed, 1075 insertions(+), 303 deletions(-) create mode 100644 extensions/apps/world-builder/src/components/pages/main/dashboard.tsx rename extensions/apps/world-builder/src/components/pages/{home-page.tsx => main/landing-page-component.tsx} (66%) rename extensions/apps/world-builder/src/components/pages/{dashboard.tsx => world-config/config-success.tsx} (83%) create mode 100644 extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx create mode 100644 extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx rename extensions/apps/world-builder/src/components/pages/{world-config-form.tsx => world-config/world-config-main-page.tsx} (51%) create mode 100644 extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx create mode 100644 extensions/apps/world-builder/src/components/pages/world-create/world-create-form.tsx rename extensions/apps/world-builder/src/components/pages/{world-meta-info-form.tsx => world-customize/world-customize-form.tsx} (92%) delete mode 100644 extensions/apps/world-builder/src/components/pages/world-data-form.tsx create mode 100644 libs/hooks/src/selectors/get-worlds-by-creator-did-query.ts diff --git a/extensions/apps/world-builder/src/components/app-routes/index.tsx b/extensions/apps/world-builder/src/components/app-routes/index.tsx index 574319674..55bd2fee8 100644 --- a/extensions/apps/world-builder/src/components/app-routes/index.tsx +++ b/extensions/apps/world-builder/src/components/app-routes/index.tsx @@ -9,11 +9,14 @@ import { } from '@tanstack/react-router'; import { ICreateRouter, IRouterContext } from '@akashaorg/typings/lib/ui'; import { - HomePage, DashboardPage, - WorldConfigFormPage, - WorldMetaInfoFormPage, - WorldDataFormPage, + WorldConfigMainPage, + WorldConfigFormStep1Page, + WorldConfigFormStep2Page, + ConfigSuccessPage, + WorldCustomizeFormPage, + WorldCreateFormPage, + CreateSuccessPage, } from '../pages/index'; import { NotFoundComponent } from './not-found-component'; import { RouteErrorComponent } from './error-component'; @@ -27,81 +30,141 @@ const defaultRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', beforeLoad: () => { - throw redirect({ to: '/home', replace: true }); + throw redirect({ to: '/dashboard', replace: true }); }, }); -const homeRoute = createRoute({ +const dashboardRoute = createRoute({ getParentRoute: () => rootRoute, - path: '/home', + path: '/dashboard', component: () => { return ( - 'home_page_reset'} errorComponent={RouteErrorComponent}> - + 'dashboard_main_reset'} + errorComponent={RouteErrorComponent} + > + ); }, }); -const dashboardRoute = createRoute({ +const configSuccessRoute = createRoute({ getParentRoute: () => rootRoute, - path: `/dashboard`, + path: `/config-success`, notFoundComponent: () => , component: () => { return ( 'dashboard_main_reset'} + getResetKey={() => 'config_success_main_reset'} errorComponent={RouteErrorComponent} > - + + + ); + }, +}); + +export type WorldCreateSuccessSearch = { worldId: string; worldName: string }; + +const createSuccessRoute = createRoute({ + getParentRoute: () => rootRoute, + path: `/create-success`, + notFoundComponent: () => , + validateSearch: (search: Record): WorldCreateSuccessSearch => { + return { worldId: search.worldId as string, worldName: search.worldName as string }; + }, + component: () => { + const { worldId, worldName } = createSuccessRoute.useSearch(); + return ( + 'create_success_main_reset'} + errorComponent={RouteErrorComponent} + > + ); }, }); -const worldDataRoute = createRoute({ +const worldCreateRoute = createRoute({ getParentRoute: () => rootRoute, - path: `/world-data`, + path: `/world-create-form`, notFoundComponent: () => , component: () => { return ( 'world_data_main_reset'} + getResetKey={() => 'world_create_form_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, }); -const worldConfigRoute = createRoute({ +const worldConfigMainRoute = createRoute({ getParentRoute: () => rootRoute, - path: `/world-config`, + path: `/world-config-form/$worldId`, + notFoundComponent: () => , + component: () => { + const { worldId } = worldConfigMainRoute.useParams(); + return ( + 'world_config_main_main_reset'} + errorComponent={RouteErrorComponent} + > + + + ); + }, +}); + +const worldConfigStep1Route = createRoute({ + getParentRoute: () => worldConfigMainRoute, + path: `/step1`, + notFoundComponent: () => , + component: () => { + const { worldId } = worldConfigMainRoute.useParams(); + return ( + 'world_config_form_step1_reset'} + errorComponent={RouteErrorComponent} + > + + + ); + }, +}); + +const worldConfigStep2Route = createRoute({ + getParentRoute: () => worldConfigMainRoute, + path: `/step2`, notFoundComponent: () => , component: () => { + const { worldId } = worldConfigMainRoute.useParams(); return ( 'world_config_main_reset'} + getResetKey={() => 'world_config_form_step2_reset'} errorComponent={RouteErrorComponent} > - + ); }, }); -const worldMetaInfoRoute = createRoute({ +const worldCustomizeRoute = createRoute({ getParentRoute: () => rootRoute, - path: `/world-meta-info`, + path: `/world-customize-form`, notFoundComponent: () => , component: () => { return ( 'world_meta_info_main_reset'} + getResetKey={() => 'world_customize_form_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, @@ -109,11 +172,12 @@ const worldMetaInfoRoute = createRoute({ const routeTree = rootRoute.addChildren([ defaultRoute, - homeRoute, + configSuccessRoute, dashboardRoute, - worldDataRoute, - worldConfigRoute, - worldMetaInfoRoute, + createSuccessRoute, + worldCreateRoute, + worldConfigMainRoute.addChildren([worldConfigStep1Route, worldConfigStep2Route]), + worldCustomizeRoute, ]); export const router = ({ baseRouteName, apolloClient }: ICreateRouter) => diff --git a/extensions/apps/world-builder/src/components/pages/index.ts b/extensions/apps/world-builder/src/components/pages/index.ts index acb065f21..6bb6cdb5f 100644 --- a/extensions/apps/world-builder/src/components/pages/index.ts +++ b/extensions/apps/world-builder/src/components/pages/index.ts @@ -1,5 +1,8 @@ -export * from './home-page'; -export * from './dashboard'; -export * from './world-data-form'; -export * from './world-config-form'; -export * from './world-meta-info-form'; +export * from './main/dashboard'; +export * from './world-create/create-success'; +export * from './world-create/world-create-form'; +export * from './world-config/world-config-main-page'; +export * from './world-config/world-config-form-step1'; +export * from './world-config/world-config-form-step2'; +export * from './world-config/config-success'; +export * from './world-customize/world-customize-form'; diff --git a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx new file mode 100644 index 000000000..d0ae3164e --- /dev/null +++ b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from '@tanstack/react-router'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@akashaorg/ui/lib/akasha-components/card'; +import { + ErrorLoader, + ErrorLoaderTitle, + ErrorLoaderDescription, + ErrorLoaderFooter, +} from '@akashaorg/ui/lib/akasha-components/error-loader'; +import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; +import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; +import { HOME } from '../../../routes'; +import { LandingPageComponent } from './landing-page-component'; +import { useGetWorldsByCreatorDidQuery } from '@akashaorg/ui-core-hooks/lib/generated'; +import { Eye, Loader2 } from 'lucide-react'; +import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; +import { selectWorldData } from '@akashaorg/ui-core-hooks/lib/selectors/get-worlds-by-creator-did-query'; + +export const DashboardPage: React.FC = () => { + const navigate = useNavigate(); + const { t } = useTranslation('app-world-builder'); + + const { baseRouteName, getCorePlugins } = useRootComponentProps(); + const navigateTo = getCorePlugins().routing.navigateTo; + + const { + data: { authenticatedDID }, + } = useAkashaStore(); + + const handleConnectButtonClick = () => { + navigateTo?.({ + appName: '@akashaorg/app-auth-ewa', + getNavigationUrl: (routes: Record) => { + return `${routes.Connect}?${new URLSearchParams({ + redirectTo: `${baseRouteName}/${routes[HOME]}`, + }).toString()}`; + }, + }); + }; + + const { + data: worldsByCreatorDidReq, + loading: loadingWorldsByCreatorDidQuery, + error: worldsByCreatorDidError, + } = useGetWorldsByCreatorDidQuery({ + variables: { id: authenticatedDID, first: 10 }, + }); + + const worldData = selectWorldData(worldsByCreatorDidReq); + + const handleNavToConfigfForm = () => { + navigate({ to: '/world-config-form/$worldId/step1', params: { worldId: worldData?.id } }); + }; + + if (!authenticatedDID) { + return ( + + {`${t('Uh-oh')}! ${t('You are not connected')}!`} + + {`${t('To create a world configuration you must be connected')} ⚡️`} + + + + + + ); + } + if (loadingWorldsByCreatorDidQuery) { + return ( + + + + ); + } + if (!loadingWorldsByCreatorDidQuery && !worldData) { + return ; + } + if (!loadingWorldsByCreatorDidQuery && worldData) { + return ( + + + + {t('World Builder Dashboard')} + + + + {t( + 'Right now, you can create only one world at a time 🌍✨ But don’t worry! More possibilities are coming soon! 🚀', + )} + + + + + + + {worldData?.name} + + + + {t( + 'Your world doesn’t have a description yet! Let’s bring it to life by adding one in the World Customizer section.', + )} + + + + {t('World Creation')} + + + + {t('World Config')} + + + + + + ); + } +}; diff --git a/extensions/apps/world-builder/src/components/pages/home-page.tsx b/extensions/apps/world-builder/src/components/pages/main/landing-page-component.tsx similarity index 66% rename from extensions/apps/world-builder/src/components/pages/home-page.tsx rename to extensions/apps/world-builder/src/components/pages/main/landing-page-component.tsx index 810c8e678..29502efa1 100644 --- a/extensions/apps/world-builder/src/components/pages/home-page.tsx +++ b/extensions/apps/world-builder/src/components/pages/main/landing-page-component.tsx @@ -9,45 +9,20 @@ import { CardHeader, CardTitle, } from '@akashaorg/ui/lib/akasha-components/card'; -import { - ErrorLoader, - ErrorLoaderTitle, - ErrorLoaderDescription, - ErrorLoaderFooter, -} from '@akashaorg/ui/lib/akasha-components/error-loader'; + import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { Button } from '@akashaorg/ui/lib/akasha-components/button'; import { ImageRoot, Image } from '@akashaorg/ui/lib/akasha-components/image'; import { Checkbox } from '@akashaorg/ui/lib/components/checkbox'; -import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; -import { HOME } from '../../routes'; const TERMS_OF_USE = '/@akashaorg/app-legal/terms-of-use'; -export const HomePage: React.FC = () => { +export const LandingPageComponent: React.FC = () => { const navigate = useNavigate(); const { t } = useTranslation('app-world-builder'); - const { baseRouteName, getCorePlugins } = useRootComponentProps(); - const navigateTo = getCorePlugins().routing.navigateTo; - - const { - data: { authenticatedDID }, - } = useAkashaStore(); - - const handleConnectButtonClick = () => { - navigateTo?.({ - appName: '@akashaorg/app-auth-ewa', - getNavigationUrl: (routes: Record) => { - return `${routes.Connect}?${new URLSearchParams({ - redirectTo: `${baseRouteName}/${routes[HOME]}`, - }).toString()}`; - }, - }); - }; - const handleNavigateToForm = () => { - navigate({ to: '/world-data' }); + navigate({ to: '/world-create-form' }); }; const [acceptedTerms, setAcceptedTerms] = React.useState(false); @@ -56,22 +31,6 @@ export const HomePage: React.FC = () => { setAcceptedTerms(!acceptedTerms); }; - if (!authenticatedDID) { - return ( - - {`${t('Uh-oh')}! ${t('You are not connected')}!`} - - {`${t('To create a world configuration you must be connected')} ⚡️`} - - - - - - ); - } - return ( diff --git a/extensions/apps/world-builder/src/components/pages/dashboard.tsx b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx similarity index 83% rename from extensions/apps/world-builder/src/components/pages/dashboard.tsx rename to extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx index cb0e69ec0..4ed69341a 100644 --- a/extensions/apps/world-builder/src/components/pages/dashboard.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx @@ -8,23 +8,28 @@ import { CardHeader, CardTitle, CardDescription, + Card, } from '@akashaorg/ui/lib/akasha-components/card'; import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { Image, ImageRoot } from '@akashaorg/ui/lib/akasha-components/image'; -export const DashboardPage: React.FC = () => { +export const ConfigSuccessPage: React.FC = () => { const navigate = useNavigate(); const { t } = useTranslation('app-world-builder'); + const handleNavToDashboard = () => { + navigate({ to: '/dashboard' }); + }; + const handleOpenPreview = () => { - navigate({ to: '/home' }); + navigate({ to: '/dashboard' }); }; // TODO fetch real data const worldName = 'Test World'; return ( - <> + @@ -46,11 +51,14 @@ export const DashboardPage: React.FC = () => { {t(`You can now preview this world!`)} {t(`The preview will open in a new tab`)} - + + - + ); }; diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx new file mode 100644 index 000000000..cb0f6ceb2 --- /dev/null +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx @@ -0,0 +1,166 @@ +import React, { useContext, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from '@tanstack/react-router'; +import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; +import { Stepper } from '@akashaorg/ui/lib/akasha-components/stepper'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@akashaorg/ui/lib/akasha-components/card'; +import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@akashaorg/ui/lib/akasha-components/form'; +import { Autocomplete, Option } from '@akashaorg/ui/lib/akasha-components/autocomplete'; +import { useAtom } from 'jotai'; +import { AtomContext, FormData } from './world-config-main-page'; + +type WorldConfigFormStep1Props = { + worldId: string; +}; + +export const WorldConfigFormStep1Page: React.FC = ({ worldId }) => { + const { t } = useTranslation('app-extensions'); + + const navigate = useNavigate(); + + const registryExtensionOptions = [ + { label: 'Akasha Extension App', value: '@akashaorg/app-extensions' }, + ]; + const layoutExtensionOptions = [ + { label: 'Akasha World Default Layout', value: '@akashaorg/ui-widget-layout' }, + ]; + + const FormSchema = z.object({ + layoutExtension: z.string(), + registryExtension: z.string(), + }); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + layoutExtension: '', + registryExtension: '', + }, + }); + + const [selectedLayoutValue, setSelectedLayoutValue] = useState - - - { - return fetchMore({ - variables: { - after: pageInfo?.endCursor, - }, - }); - }} - > - - {index => { - const extensionData = akashaApps[index]; - return ( - - - - + { + return fetchMore({ + variables: { + after: pageInfo?.endCursor, + }, + }); + }} + > + + {index => { + const extensionData = akashaApps[index]; + return ( + + + + + + + + + {extensionData?.displayName} + + + - - - - - {extensionData?.displayName} - - - - - - {extensionData?.author?.akashaProfile?.name} - - - - {extensionData?.description} - - - -1}> - { - addExtension(extensionData); - }} - > - {t('Add')} - - { - removeExtension(extensionData?.id); - }} - > - {t('Added')} - - - - ); - }} - - - + + + {extensionData?.author?.akashaProfile?.name} + + + + {extensionData?.description} + + + -1}> + { + addExtension(extensionData); + }} + > + {t('Add')} + + { + removeExtension(extensionData?.id); + }} + > + {t('Added')} + + + + ); + }} + + {t('You have selected:')} diff --git a/libs/sdk/src/gql/index.ts b/libs/sdk/src/gql/index.ts index 245de3cdc..9a85abd8e 100644 --- a/libs/sdk/src/gql/index.ts +++ b/libs/sdk/src/gql/index.ts @@ -114,6 +114,8 @@ class Gql { fields: { akashaBeamIndex: relayStylePagination(['sorting', 'filters']), akashaAppReleaseIndex: relayStylePagination(['sorting', 'filters']), + akashaAppIndex: relayStylePagination(['sorting', 'filters']), + akashaWorldConfigExtensionIndex: relayStylePagination(['sorting', 'filters']), }, }, }, From 623704af188367bf41e43579650a739761987eb8 Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Tue, 25 Feb 2025 18:18:35 +0200 Subject: [PATCH 07/13] chore(): style fixes --- .../components/pages/world-config/world-config-form-step2.tsx | 2 +- extensions/apps/world-builder/src/ui/extension-card.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx index 55d770ef3..b8a1e51c9 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx @@ -227,7 +227,7 @@ export const WorldConfigFormStep2Page: React.FC = ({ - + ); @@ -103,7 +103,7 @@ const ExtensionCardDescription = ({ ); From 197b0ddbe38d7298cb91e99efbfc1462b8498545 Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Wed, 26 Feb 2025 11:41:56 +0200 Subject: [PATCH 08/13] chore(): btn sizes and ext card props fix --- .../pages/world-config/config-success.tsx | 4 ++-- .../world-config/world-config-form-step1.tsx | 4 ++-- .../world-config/world-config-form-step2.tsx | 10 +++++++--- .../pages/world-create/create-success.tsx | 4 ++-- .../pages/world-create/world-create-form.tsx | 4 ++-- .../world-builder/src/ui/extension-avatar.tsx | 16 ++++++++-------- .../apps/world-builder/src/ui/extension-card.tsx | 8 ++++---- .../world-builder/src/ui/extension-type-icon.tsx | 14 +++++++------- .../world-builder/src/ui/types/extension-type.ts | 6 ------ 9 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 extensions/apps/world-builder/src/ui/types/extension-type.ts diff --git a/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx index 70455bd92..c0c722a93 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx @@ -55,10 +55,10 @@ export const ConfigSuccessPage: React.FC = ({ worldId, w {t(`The preview will open in a new tab`)} - - diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx index e662b15bb..14606ce80 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx @@ -165,10 +165,10 @@ export const WorldConfigFormStep1Page: React.FC = ({ /> - - diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx index b8a1e51c9..a2e4725ae 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx @@ -249,7 +249,11 @@ export const WorldConfigFormStep2Page: React.FC = ({ return ( - + @@ -346,10 +350,10 @@ export const WorldConfigFormStep2Page: React.FC = ({ - - diff --git a/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx b/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx index 579c288a3..78e3f2953 100644 --- a/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx @@ -58,10 +58,10 @@ export const CreateSuccessPage: React.FC = ({ worldId, w - - diff --git a/extensions/apps/world-builder/src/components/pages/world-create/world-create-form.tsx b/extensions/apps/world-builder/src/components/pages/world-create/world-create-form.tsx index 3042538e7..2b92a3123 100644 --- a/extensions/apps/world-builder/src/components/pages/world-create/world-create-form.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-create/world-create-form.tsx @@ -318,10 +318,10 @@ export const WorldCreateFormPage: React.FC = () => { /> - - diff --git a/extensions/apps/world-builder/src/ui/extension-avatar.tsx b/extensions/apps/world-builder/src/ui/extension-avatar.tsx index 559299d59..d004db6bc 100644 --- a/extensions/apps/world-builder/src/ui/extension-avatar.tsx +++ b/extensions/apps/world-builder/src/ui/extension-avatar.tsx @@ -6,8 +6,8 @@ import { EyeOff } from 'lucide-react'; import { cn } from '@/ui/library/utils'; import { getImageFromSeed } from '@/ui/library/get-image-from-seed'; -import { ExtensionType } from '@/ui/types/extension-type'; import { Image, ImageFallback, ImageRoot } from '@akashaorg/ui/lib/akasha-components/image'; +import { AkashaAppApplicationType } from '@akashaorg/typings/lib/sdk/graphql-types-new'; const extensionVariants = cva( 'flex justify-center items-center rounded-lg overflow-hidden shrink-0', @@ -28,7 +28,7 @@ const extensionVariants = cva( const ExtensionAvatarContext = React.createContext<{ extensionId: string; - extensionType: ExtensionType; + extensionType: AkashaAppApplicationType; nsfw: boolean; publicImgPath: string; } | null>(null); @@ -46,7 +46,7 @@ const useExtensionAvatarContext = () => { const ExtensionAvatar = ({ extensionId = '', size = 'lg', - extensionType = ExtensionType.Widget, + extensionType = AkashaAppApplicationType.Widget, publicImgPath = '/images', nsfw = false, className, @@ -54,7 +54,7 @@ const ExtensionAvatar = ({ }: React.ComponentProps<'div'> & VariantProps & { extensionId?: string; - extensionType?: ExtensionType; + extensionType?: AkashaAppApplicationType; nsfw?: boolean; publicImgPath?: string; }) => ( @@ -91,16 +91,16 @@ const ExtensionAvatarFallback = ({ let avatarFallback: string; switch (extensionType) { - case ExtensionType.App: + case AkashaAppApplicationType.App: avatarFallback = `${publicImgPath}/app-${seed}.webp`; break; - case ExtensionType.Widget: + case AkashaAppApplicationType.Widget: avatarFallback = `${publicImgPath}/widget-${seed}.webp`; break; - case ExtensionType.Plugin: + case AkashaAppApplicationType.Plugin: avatarFallback = `${publicImgPath}/plugin-${seed}.webp`; break; - case ExtensionType.Other: + case AkashaAppApplicationType.Other: avatarFallback = `${publicImgPath}/other-${seed}.webp`; break; default: diff --git a/extensions/apps/world-builder/src/ui/extension-card.tsx b/extensions/apps/world-builder/src/ui/extension-card.tsx index 88a786978..e55f699bb 100644 --- a/extensions/apps/world-builder/src/ui/extension-card.tsx +++ b/extensions/apps/world-builder/src/ui/extension-card.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { Check } from 'lucide-react'; import { cn } from '@/ui/library/utils'; -import { ExtensionType } from '@/ui/types/extension-type'; import { Badge } from '@akashaorg/ui/lib/components/badge'; import { ExtensionAvatar, @@ -11,10 +10,11 @@ import { } from '@/ui/extension-avatar'; import { ExtensionTypeIcon } from '@/ui/extension-type-icon'; import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; +import { AkashaAppApplicationType } from '@akashaorg/typings/lib/sdk/graphql-types-new'; const ExtensionCardContext = React.createContext<{ extensionId?: string; - extensionType?: ExtensionType; + extensionType?: AkashaAppApplicationType; } | null>(null); const useExtensionCardContext = () => { @@ -27,12 +27,12 @@ const useExtensionCardContext = () => { const ExtensionCard = ({ extensionId = '', - extensionType = ExtensionType.App, + extensionType = AkashaAppApplicationType.App, className, ...props }: React.ComponentProps<'div'> & { extensionId?: string; - extensionType?: ExtensionType; + extensionType?: AkashaAppApplicationType; }) => { return ( diff --git a/extensions/apps/world-builder/src/ui/extension-type-icon.tsx b/extensions/apps/world-builder/src/ui/extension-type-icon.tsx index 0ec67ce1a..41e9d8870 100644 --- a/extensions/apps/world-builder/src/ui/extension-type-icon.tsx +++ b/extensions/apps/world-builder/src/ui/extension-type-icon.tsx @@ -2,15 +2,15 @@ import * as React from 'react'; import { LayoutGrid, LayoutPanelLeft, Puzzle } from 'lucide-react'; import { cn } from '@/ui/library/utils'; -import { ExtensionType } from '@/ui/types/extension-type'; +import { AkashaAppApplicationType } from '@akashaorg/typings/lib/sdk/graphql-types-new'; -const Icon = ({ extensionType }: { extensionType: ExtensionType }) => { +const Icon = ({ extensionType }: { extensionType: AkashaAppApplicationType }) => { switch (extensionType) { - case ExtensionType.App: + case AkashaAppApplicationType.App: return ; - case ExtensionType.Plugin: + case AkashaAppApplicationType.Plugin: return ; - case ExtensionType.Widget: + case AkashaAppApplicationType.Widget: return ; default: return ; @@ -18,11 +18,11 @@ const Icon = ({ extensionType }: { extensionType: ExtensionType }) => { }; const ExtensionTypeIcon = ({ - extensionType = ExtensionType.App, + extensionType = AkashaAppApplicationType.App, className, ...props }: React.ComponentProps<'div'> & { - extensionType?: ExtensionType; + extensionType?: AkashaAppApplicationType; }) => { return (
Date: Wed, 26 Feb 2025 12:49:22 +0200 Subject: [PATCH 09/13] refactor(): create world config func --- .../world-config/world-config-form-step1.tsx | 22 +++--- .../world-config/world-config-form-step2.tsx | 78 +++++++++++-------- .../world-config/world-config-main-page.tsx | 2 - 3 files changed, 57 insertions(+), 45 deletions(-) diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx index 14606ce80..fd3871b77 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step1.tsx @@ -120,12 +120,11 @@ export const WorldConfigFormStep1Page: React.FC = ({ - {layoutExtensionOptions?.length > 0 && - layoutExtensionOptions?.map(opt => ( - - {opt.label} - - ))} + {layoutExtensionOptions?.map(opt => ( + + {opt.label} + + ))} @@ -151,12 +150,11 @@ export const WorldConfigFormStep1Page: React.FC = ({ - {registryExtensionOptions?.length > 0 && - registryExtensionOptions?.map(opt => ( - - {opt.label} - - ))} + {registryExtensionOptions?.map(opt => ( + + {opt.label} + + ))} diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx index a2e4725ae..135c296d1 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx @@ -57,7 +57,6 @@ import { X } from 'lucide-react'; import { NotificationEvents, NotificationTypes } from '@akashaorg/typings/lib/ui'; import getSDK from '@akashaorg/core-sdk'; import { selectWorldData } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-by-id-query'; -import { AkashaAppApplicationType, SortOrder } from '@akashaorg/typings/lib/sdk/graphql-types-new'; type WorldConfigFormStep2Props = { worldId: string; @@ -146,38 +145,51 @@ export const WorldConfigFormStep2Page: React.FC = ({ }, }); + const getUniqueExtensionIds = () => { + const defaultExtensionsIDs = selectedExtensions + ?.map(ext => ext.id) + .concat([formValue?.layoutExtension, formValue?.registryExtension]); + return defaultExtensionsIDs; + }; + + const createExtensions = (worldConfigId: string, extensionIds: string[]) => { + return Promise.all( + [...new Set(extensionIds)]?.map(extensionID => { + const worldConfigExtensionData = { + worldConfigID: worldConfigId, + extensionID: extensionID, + active: true, + createdAt: new Date().toISOString(), + }; + createWorldConfigExtensionMutation({ + variables: { + i: { + content: worldConfigExtensionData, + }, + }, + }); + }), + ); + }; + + const navToConfigSuccessPage = () => { + navigate({ + to: '/config-success', + search: { + worldId: worldId, + worldName: worldData?.name, + }, + }); + }; + const [createWorldConfigMutation, { loading: loadingWorldConfigMutation }] = useCreateAkashaWorldConfigMutation({ context: { source: sdk.current.services.gql.contextSources.composeDB }, - onCompleted: data => { - const defaultExtensionsIDs = selectedExtensions - ?.map(ext => ext.id) - .concat([formValue?.layoutExtension, formValue?.registryExtension]); - Promise.all( - [...new Set(defaultExtensionsIDs)]?.map(extensionID => { - const worldConfigExtensionData = { - active: true, - createdAt: new Date().toISOString(), - worldConfigID: data?.setAkashaWorldConfig?.document?.id, - extensionID: extensionID, - }; - createWorldConfigExtensionMutation({ - variables: { - i: { - content: worldConfigExtensionData, - }, - }, - }); - }), - ).then(() => - navigate({ - to: '/config-success', - search: { - worldId: worldId, - worldName: worldData?.name, - }, - }), - ); + onCompleted: async data => { + const worldConfigId = data?.setAkashaWorldConfig?.document?.id; + const uniqueExtIds = getUniqueExtensionIds(); + await createExtensions(worldConfigId, uniqueExtIds); + navToConfigSuccessPage(); }, onError: error => { showErrorNotification( @@ -353,7 +365,11 @@ export const WorldConfigFormStep2Page: React.FC = ({ - diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-main-page.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-main-page.tsx index ae981df0a..003197406 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-main-page.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-main-page.tsx @@ -18,7 +18,6 @@ export const AtomContext = createContext(null); const storage = createJSONStorage(() => sessionStorage); export type FormData = { - lastCompletedStep?: number; layoutExtension?: string; registryExtension?: string; homepageExtension?: string; @@ -55,7 +54,6 @@ export const WorldConfigMainPage: React.FC = ({ worldI atomWithStorage( worldId, { - lastCompletedStep: 0, layoutExtension: '', registryExtension: '', homepageExtension: '', From 233b1b9f175be0e21dfb04ad3170735082ee6a4b Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Wed, 26 Feb 2025 12:02:04 +0200 Subject: [PATCH 10/13] chore(): add world avatar --- .../src/components/pages/main/dashboard.tsx | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx index 19a5100b1..b3786954f 100644 --- a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx +++ b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx @@ -16,7 +16,7 @@ import { } from '@akashaorg/ui/lib/akasha-components/error-loader'; import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { Button } from '@akashaorg/ui/lib/akasha-components/button'; -import { useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; +import { transformSource, useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; import { HOME } from '../../../routes'; import { LandingPageComponent } from './landing-page-component'; import { @@ -27,6 +27,11 @@ import { Eye, Loader2, Pencil } from 'lucide-react'; import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; import { selectWorldData } from '@akashaorg/ui-core-hooks/lib/selectors/get-worlds-by-creator-did-query'; import { selectWorldConfigData } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-query'; +import { + ExtensionAvatar, + ExtensionAvatarFallback, + ExtensionAvatarImage, +} from '@/ui/extension-avatar'; export const DashboardPage: React.FC = () => { const navigate = useNavigate(); @@ -122,19 +127,25 @@ export const DashboardPage: React.FC = () => { - - - {worldData?.name} - + + + + + + + + {worldData?.name} + + + + {t( + 'Your world doesn’t have a description yet! Let’s bring it to life by adding one in the World Customizer section.', + )} + - - {t( - 'Your world doesn’t have a description yet! Let’s bring it to life by adding one in the World Customizer section.', - )} - @@ -147,7 +158,9 @@ export const DashboardPage: React.FC = () => { {t('World Config')} - + From 4c77ef9d9822dd716df0a9c46a3543fe3a5b0fcf Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Wed, 26 Feb 2025 21:45:20 +0200 Subject: [PATCH 11/13] chore() --- .../src/components/pages/main/dashboard.tsx | 120 ++++++++++++++++-- .../get-world-config-extensions-query.ts | 25 ++++ .../selectors/get-world-full-info-query.ts | 13 ++ 3 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 libs/hooks/src/selectors/get-world-config-extensions-query.ts create mode 100644 libs/hooks/src/selectors/get-world-full-info-query.ts diff --git a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx index b3786954f..c6672e871 100644 --- a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx +++ b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx @@ -20,18 +20,24 @@ import { transformSource, useAkashaStore, useRootComponentProps } from '@akashao import { HOME } from '../../../routes'; import { LandingPageComponent } from './landing-page-component'; import { - useGetWorldConfigQuery, + useGetWorldConfigExtensionsQuery, + useGetWorldFullInfoQuery, useGetWorldsByCreatorDidQuery, } from '@akashaorg/ui-core-hooks/lib/generated'; import { Eye, Loader2, Pencil } from 'lucide-react'; import { Stack } from '@akashaorg/ui/lib/akasha-components/stack'; import { selectWorldData } from '@akashaorg/ui-core-hooks/lib/selectors/get-worlds-by-creator-did-query'; -import { selectWorldConfigData } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-query'; +import { + selectWorldConfigData, + selectWorldMetaInfoData, +} from '@akashaorg/ui-core-hooks/lib/selectors/get-world-full-info-query'; +import { selectWorldConfigExtensions } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-extensions-query'; import { ExtensionAvatar, ExtensionAvatarFallback, ExtensionAvatarImage, } from '@/ui/extension-avatar'; +import { Image, ImageRoot } from '@akashaorg/ui/lib/akasha-components/image'; export const DashboardPage: React.FC = () => { const navigate = useNavigate(); @@ -66,20 +72,36 @@ export const DashboardPage: React.FC = () => { const worldData = selectWorldData(worldsByCreatorDidReq); const { - data: worldConfigReq, - loading: loadingWorldConfigQuery, - error: worldConfigError, - } = useGetWorldConfigQuery({ - variables: { worldID: worldData?.id }, + data: worldFullInfoReq, + loading: loadingWorldFullInfoQuery, + error: worldFullInfoError, + } = useGetWorldFullInfoQuery({ + variables: { id: worldData?.id, creator: authenticatedDID }, skip: !worldData?.id, }); - const worldConfig = selectWorldConfigData(worldConfigReq); + const worldConfig = selectWorldConfigData(worldFullInfoReq); + const worldMetaInfo = selectWorldMetaInfoData(worldFullInfoReq); + + const { + data: worldConfigExtensionsReq, + loading: loadingWorldConfigExtensionsQuery, + error: worldConfigExtensionsError, + } = useGetWorldConfigExtensionsQuery({ + variables: { configID: worldConfig?.id }, + skip: !worldConfig?.id, + }); + + const worldConfigExtensions = selectWorldConfigExtensions(worldConfigExtensionsReq); - const handleNavToConfigfForm = () => { + const handleNavToConfigForm = () => { navigate({ to: '/world-config-form/$worldId/step1', params: { worldId: worldData?.id } }); }; + const handleNavToCustomizeForm = () => { + navigate({ to: '/world-customize-form', params: { worldId: worldData?.id } }); + }; + const handleNavToWorldCreate = () => { navigate({ to: '/world-create-form' }); }; @@ -128,7 +150,7 @@ export const DashboardPage: React.FC = () => { - + @@ -150,18 +172,92 @@ export const DashboardPage: React.FC = () => { {t('World Creation')} - + {worldData?.extensionPublishers?.length > 0 && ( + + + {t('Extension Publishers')} + +
+ {worldData?.extensionPublishers?.map((extPublisher, idx) => ( + + {extPublisher?.id} + + ))} +
+
+ )} + {worldData?.icon && ( + + + {t('Icon')} + + + + + + )} + {worldData?.instanceURL && ( + + + {t('Instance URL')} + + {worldData?.instanceURL} + + )}
{t('World Config')} - + + + {t('Layout')} + + {worldConfig?.layoutExtension} + + + + {t('Extension App')} + + {worldConfig?.registryExtension} + + + + {t('World Extensions')} + +
+ {worldConfigExtensions?.map((extension, idx) => ( + + {extension?.extensionID} + + ))} +
+
+ + + {t('Homepage')} + + {worldConfig?.homepageExtension} + +
+ + + {t('World Customisation')} + +
diff --git a/libs/hooks/src/selectors/get-world-config-extensions-query.ts b/libs/hooks/src/selectors/get-world-config-extensions-query.ts new file mode 100644 index 000000000..a03dafc5d --- /dev/null +++ b/libs/hooks/src/selectors/get-world-config-extensions-query.ts @@ -0,0 +1,25 @@ +import { GetWorldConfigExtensionsQuery } from '@akashaorg/typings/lib/sdk/graphql-operation-types-new'; +import { + AkashaWorldConfigExtensionEdge, + PageInfo, +} from '@akashaorg/typings/lib/sdk/graphql-types-new'; + +const isWorldConfigExtensionsEdgeNode = ( + data: GetWorldConfigExtensionsQuery, +): data is { + akashaWorldConfigExtensionIndex: { edges: AkashaWorldConfigExtensionEdge[]; pageInfo: PageInfo }; +} => { + return ( + data && + 'akashaWorldConfigExtensionIndex' in data && + typeof data['akashaWorldConfigExtensionIndex'] === 'object' && + 'edges' in data.akashaWorldConfigExtensionIndex && + Array.isArray(data.akashaWorldConfigExtensionIndex.edges) + ); +}; + +export const selectWorldConfigExtensions = (respData: GetWorldConfigExtensionsQuery) => { + if (isWorldConfigExtensionsEdgeNode(respData)) { + return respData.akashaWorldConfigExtensionIndex.edges.map(edge => edge?.node); + } +}; diff --git a/libs/hooks/src/selectors/get-world-full-info-query.ts b/libs/hooks/src/selectors/get-world-full-info-query.ts new file mode 100644 index 000000000..769673fd2 --- /dev/null +++ b/libs/hooks/src/selectors/get-world-full-info-query.ts @@ -0,0 +1,13 @@ +import { GetWorldFullInfoQuery } from '@akashaorg/typings/lib/sdk/graphql-operation-types-new'; + +export const selectWorldConfigData = (respData: GetWorldFullInfoQuery) => { + if (respData?.node !== undefined && 'configInfo' in respData.node) { + return respData?.node?.configInfo?.edges[0]?.node; + } +}; + +export const selectWorldMetaInfoData = (respData: GetWorldFullInfoQuery) => { + if (respData?.node !== undefined && 'metaInfo' in respData.node) { + return respData?.node?.metaInfo?.edges[0]?.node; + } +}; From 206b74da1fc77b1fcacf6828b84fc68f9cef27ba Mon Sep 17 00:00:00 2001 From: Vali Cotea Date: Mon, 3 Mar 2025 13:06:53 +0200 Subject: [PATCH 12/13] feat(): update WIP --- .../src/components/app-routes/index.tsx | 6 +- .../src/components/pages/index.ts | 2 +- .../src/components/pages/main/dashboard.tsx | 79 ++++-- .../pages/world-config/config-success.tsx | 2 +- .../world-config/world-config-form-step1.tsx | 60 +++- .../world-config/world-config-form-step2.tsx | 264 ++++++++++++------ .../pages/world-create/create-success.tsx | 2 +- .../pages/world-create/world-create-form.tsx | 139 +++++---- .../world-customise-form.tsx} | 4 +- 9 files changed, 386 insertions(+), 172 deletions(-) rename extensions/apps/world-builder/src/components/pages/{world-customize/world-customize-form.tsx => world-customise/world-customise-form.tsx} (94%) diff --git a/extensions/apps/world-builder/src/components/app-routes/index.tsx b/extensions/apps/world-builder/src/components/app-routes/index.tsx index 7affd53f0..bb395e671 100644 --- a/extensions/apps/world-builder/src/components/app-routes/index.tsx +++ b/extensions/apps/world-builder/src/components/app-routes/index.tsx @@ -14,7 +14,7 @@ import { WorldConfigFormStep1Page, WorldConfigFormStep2Page, ConfigSuccessPage, - WorldCustomizeFormPage, + WorldCustomiseFormPage, WorldCreateFormPage, CreateSuccessPage, } from '../pages/index'; @@ -59,7 +59,7 @@ const configSuccessRoute = createRoute({ return { worldId: search.worldId as string, worldName: search.worldName as string }; }, component: () => { - const { worldId, worldName } = createSuccessRoute.useSearch(); + const { worldId, worldName } = configSuccessRoute.useSearch(); return ( 'config_success_main_reset'} @@ -168,7 +168,7 @@ const worldCustomizeRoute = createRoute({ getResetKey={() => 'world_customize_form_main_reset'} errorComponent={RouteErrorComponent} > - + ); }, diff --git a/extensions/apps/world-builder/src/components/pages/index.ts b/extensions/apps/world-builder/src/components/pages/index.ts index 6bb6cdb5f..302dd8a9c 100644 --- a/extensions/apps/world-builder/src/components/pages/index.ts +++ b/extensions/apps/world-builder/src/components/pages/index.ts @@ -5,4 +5,4 @@ export * from './world-config/world-config-main-page'; export * from './world-config/world-config-form-step1'; export * from './world-config/world-config-form-step2'; export * from './world-config/config-success'; -export * from './world-customize/world-customize-form'; +export * from './world-customise/world-customise-form'; diff --git a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx index c6672e871..29ccf42e3 100644 --- a/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx +++ b/extensions/apps/world-builder/src/components/pages/main/dashboard.tsx @@ -16,11 +16,13 @@ import { } from '@akashaorg/ui/lib/akasha-components/error-loader'; import { Typography } from '@akashaorg/ui/lib/akasha-components/typography'; import { Button } from '@akashaorg/ui/lib/akasha-components/button'; +import { Separator } from '@akashaorg/ui/lib/components/separator'; import { transformSource, useAkashaStore, useRootComponentProps } from '@akashaorg/ui-core-hooks'; import { HOME } from '../../../routes'; import { LandingPageComponent } from './landing-page-component'; import { useGetWorldConfigExtensionsQuery, + useGetWorldConfigQuery, useGetWorldFullInfoQuery, useGetWorldsByCreatorDidQuery, } from '@akashaorg/ui-core-hooks/lib/generated'; @@ -31,6 +33,7 @@ import { selectWorldConfigData, selectWorldMetaInfoData, } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-full-info-query'; +import { selectWorldConfigData as selectWorldConfigInfo } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-query'; import { selectWorldConfigExtensions } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-extensions-query'; import { ExtensionAvatar, @@ -38,6 +41,7 @@ import { ExtensionAvatarImage, } from '@/ui/extension-avatar'; import { Image, ImageRoot } from '@akashaorg/ui/lib/akasha-components/image'; +import { IconContainer } from '@akashaorg/ui/lib/akasha-components/icon-container'; export const DashboardPage: React.FC = () => { const navigate = useNavigate(); @@ -76,11 +80,20 @@ export const DashboardPage: React.FC = () => { loading: loadingWorldFullInfoQuery, error: worldFullInfoError, } = useGetWorldFullInfoQuery({ - variables: { id: worldData?.id, creator: authenticatedDID }, + variables: { id: worldData?.id, creator: worldData?.creator?.id }, skip: !worldData?.id, }); - const worldConfig = selectWorldConfigData(worldFullInfoReq); + const { + data: worldConfigReq, + loading: loadingWorldConfigQuery, + error: worldConfigError, + } = useGetWorldConfigQuery({ + variables: { worldID: worldData?.id }, + skip: !worldData?.id, + }); + + const worldConfig = selectWorldConfigInfo(worldConfigReq); const worldMetaInfo = selectWorldMetaInfoData(worldFullInfoReq); const { @@ -98,7 +111,7 @@ export const DashboardPage: React.FC = () => { navigate({ to: '/world-config-form/$worldId/step1', params: { worldId: worldData?.id } }); }; - const handleNavToCustomizeForm = () => { + const handleNavToCustomiseForm = () => { navigate({ to: '/world-customize-form', params: { worldId: worldData?.id } }); }; @@ -106,6 +119,11 @@ export const DashboardPage: React.FC = () => { navigate({ to: '/world-create-form' }); }; + const getExtensionDataById = (extId: string) => { + const extension = worldConfigExtensions?.find(ext => ext.extensionID === extId); + return extension?.extension; + }; + if (!authenticatedDID) { return ( @@ -170,11 +188,13 @@ export const DashboardPage: React.FC = () => { - + {t('World Creation')} - + {worldData?.extensionPublishers?.length > 0 && ( @@ -213,24 +233,36 @@ export const DashboardPage: React.FC = () => { )} + - + {t('World Config')} - + + {worldConfig?.id ? ( + + ) : ( + + )} {t('Layout')} - {worldConfig?.layoutExtension} + + {getExtensionDataById(worldConfig?.layoutExtension)?.displayName} + {t('Extension App')} - {worldConfig?.registryExtension} + + {getExtensionDataById(worldConfig?.registryExtension)?.displayName} + @@ -239,7 +271,7 @@ export const DashboardPage: React.FC = () => {
{worldConfigExtensions?.map((extension, idx) => ( - {extension?.extensionID} + {extension?.extension?.displayName} ))}
@@ -248,15 +280,24 @@ export const DashboardPage: React.FC = () => { {t('Homepage')} - {worldConfig?.homepageExtension} + + {getExtensionDataById(worldConfig?.homepageExtension)?.displayName} +
+ - + {t('World Customisation')} - + {worldMetaInfo?.id ? ( + + ) : ( + + )} diff --git a/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx index c0c722a93..b77ceeb4a 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/config-success.tsx @@ -55,7 +55,7 @@ export const ConfigSuccessPage: React.FC = ({ worldId, w {t(`The preview will open in a new tab`)} - - diff --git a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx index 135c296d1..6b9adecf8 100644 --- a/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-config/world-config-form-step2.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from '@tanstack/react-router'; import { Button } from '@akashaorg/ui/lib/akasha-components/button'; @@ -46,6 +46,8 @@ import { useCreateAkashaWorldConfigMutation, useGetAppsQuery, useGetWorldByIdQuery, + useGetWorldConfigQuery, + useUpdateAkashaWorldConfigExtensionMutation, } from '@akashaorg/ui-core-hooks/lib/generated'; import { selectAkashaApps, @@ -57,6 +59,8 @@ import { X } from 'lucide-react'; import { NotificationEvents, NotificationTypes } from '@akashaorg/typings/lib/ui'; import getSDK from '@akashaorg/core-sdk'; import { selectWorldData } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-by-id-query'; +import { selectWorldConfigData } from '@akashaorg/ui-core-hooks/lib/selectors/get-world-config-query'; +import { Separator } from '@akashaorg/ui/lib/components/separator'; type WorldConfigFormStep2Props = { worldId: string; @@ -102,6 +106,17 @@ export const WorldConfigFormStep2Page: React.FC = ({ const worldData = selectWorldData(getWorldByIdReq); + const { + data: worldConfigReq, + loading: loadingWorldConfigQuery, + error: worldConfigError, + } = useGetWorldConfigQuery({ + variables: { worldID: worldData?.id }, + skip: !worldData?.id, + }); + + const worldConfig = selectWorldConfigData(worldConfigReq); + const { data: getAppsReq, loading: loadingGetAppsQuery, @@ -118,6 +133,22 @@ export const WorldConfigFormStep2Page: React.FC = ({ const [selectedExtensions, setSelectedExtensions] = useState([]); + // if there is already a configuration for this world prefill the extensions in the UI + useEffect(() => { + if (worldConfig?.extensions?.edges?.length > 0) { + const extensions = worldConfig.extensions.edges?.map(extNode => { + const extData = extNode?.node; + return { + id: extData.extensionID, + ...extData.extension, + }; + }); + setSelectedExtensions(prev => { + return [...new Set([...prev, ...extensions])]; + }); + } + }, [worldConfig?.extensions]); + const addExtension = ext => { setSelectedExtensions(prev => { return [...new Set([...prev, ext])]; @@ -134,44 +165,104 @@ export const WorldConfigFormStep2Page: React.FC = ({ const [homepage, setHomepage] = useState(null); - const [createWorldConfigExtensionMutation, { loading: loadingWorldConfigExtensionMutation }] = - useCreateAkashaWorldConfigExtensionMutation({ - context: { source: sdk.current.services.gql.contextSources.composeDB }, - onError: error => { - showErrorNotification( - `${t(`Something went wrong when creating the world configuration extensions`)}.`, - error.message, - ); - }, + const [ + createWorldConfigExtensionMutation, + { loading: loadingWorldConfigCreateExtensionMutation }, + ] = useCreateAkashaWorldConfigExtensionMutation({ + context: { source: sdk.current.services.gql.contextSources.composeDB }, + onError: error => { + showErrorNotification( + `${t(`Something went wrong when creating the world configuration extensions`)}.`, + error.message, + ); + }, + }); + + // const [ + // updateWorldConfigExtensionMutation, + // { loading: loadingWorldConfigUpdateExtensionMutation }, + // ] = useUpdateAkashaWorldConfigExtensionMutation({ + // context: { source: sdk.current.services.gql.contextSources.composeDB }, + // onError: error => { + // showErrorNotification( + // `${t(`Something went wrong when updating the world configuration extensions`)}.`, + // error.message, + // ); + // }, + // }); + + const getUniqueExtensionsData = (worldConfigId: string) => { + const newExtensionsIDs = new Set( + selectedExtensions + ?.map(ext => ext.id) + .concat([formValue?.layoutExtension, formValue?.registryExtension]), + ); + + const oldExtensionsIDs = new Set( + worldConfig?.extensions?.edges?.map(ext => ext.node?.extensionID), + ); + + const oldExtensionsToBeRemovedSet = oldExtensionsIDs.difference(newExtensionsIDs); + + const newExtensionsData = [...newExtensionsIDs].map(extensionID => { + const createdAt = worldConfig?.extensions.edges?.find( + extData => extData?.node?.extensionID === extensionID, + )?.node?.createdAt; + const worldConfigExtensionData = { + worldConfigID: worldConfigId, + extensionID: extensionID, + active: true, + createdAt: createdAt ?? new Date().toISOString(), + }; + return worldConfigExtensionData; + }); + + const extensionsToBeRemoved = [...oldExtensionsToBeRemovedSet].map(extensionID => { + const createdAt = worldConfig?.extensions.edges?.find( + extData => extData?.node?.extensionID === extensionID, + )?.node?.createdAt; + const worldConfigExtensionData = { + worldConfigID: worldConfigId, + extensionID: extensionID, + active: false, + createdAt, + }; + return worldConfigExtensionData; }); - const getUniqueExtensionIds = () => { - const defaultExtensionsIDs = selectedExtensions - ?.map(ext => ext.id) - .concat([formValue?.layoutExtension, formValue?.registryExtension]); - return defaultExtensionsIDs; + return [...newExtensionsData, ...extensionsToBeRemoved]; }; - const createExtensions = (worldConfigId: string, extensionIds: string[]) => { + const createExtensions = (worldConfigId: string) => { + const extensions = getUniqueExtensionsData(worldConfigId); return Promise.all( - [...new Set(extensionIds)]?.map(extensionID => { - const worldConfigExtensionData = { - worldConfigID: worldConfigId, - extensionID: extensionID, - active: true, - createdAt: new Date().toISOString(), - }; + extensions.map(extData => createWorldConfigExtensionMutation({ variables: { i: { - content: worldConfigExtensionData, + content: extData, }, }, - }); - }), + }), + ), ); }; + // const updateExtensions = (worldConfigId: string) => { + // const extensions = getUniqueExtensionsData(worldConfigId)?.extensionsToBeRemoved; + // return Promise.all( + // extensions.map(extData => + // updateWorldConfigExtensionMutation({ + // variables: { + // i: { + // content: extData, + // }, + // }, + // }), + // ), + // ); + // }; + const navToConfigSuccessPage = () => { navigate({ to: '/config-success', @@ -187,8 +278,7 @@ export const WorldConfigFormStep2Page: React.FC = ({ context: { source: sdk.current.services.gql.contextSources.composeDB }, onCompleted: async data => { const worldConfigId = data?.setAkashaWorldConfig?.document?.id; - const uniqueExtIds = getUniqueExtensionIds(); - await createExtensions(worldConfigId, uniqueExtIds); + await createExtensions(worldConfigId); navToConfigSuccessPage(); }, onError: error => { @@ -203,10 +293,10 @@ export const WorldConfigFormStep2Page: React.FC = ({ const worldConfigData = { layoutExtension: formValue?.layoutExtension, registryExtension: formValue?.registryExtension, - homepageExtension: homepage, + homepageExtension: homepage || worldConfig?.homepageExtension, worldID: worldId, active: true, - createdAt: new Date().toISOString(), + createdAt: worldConfig?.createdAt ?? new Date().toISOString(), }; createWorldConfigMutation({ variables: { @@ -259,58 +349,62 @@ export const WorldConfigFormStep2Page: React.FC = ({ {index => { const extensionData = akashaApps[index]; return ( - - - - - - - - - {extensionData?.displayName} - - - + + + + - - - {extensionData?.author?.akashaProfile?.name} - - - - {extensionData?.description} - - - -1}> - { - addExtension(extensionData); - }} - > - {t('Add')} - - { - removeExtension(extensionData?.id); - }} - > - {t('Added')} - - - + + + + + {extensionData?.displayName} + + + + + + {extensionData?.author?.akashaProfile?.name} + + + + {extensionData?.description} + + + -1}> + { + addExtension(extensionData); + }} + > + {t('Add')} + + { + removeExtension(extensionData?.id); + }} + > + {t('Added')} + + + + {pageInfo?.hasNextPage && index < akashaApps?.length - 1 && } + ); }} @@ -346,7 +440,11 @@ export const WorldConfigFormStep2Page: React.FC = ({ 'Selecting an extension sets it as the default homepage when members enter the world.', )} - @@ -368,7 +466,7 @@ export const WorldConfigFormStep2Page: React.FC = ({ diff --git a/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx b/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx index 78e3f2953..d659d13cf 100644 --- a/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-create/create-success.tsx @@ -58,7 +58,7 @@ export const CreateSuccessPage: React.FC = ({ worldId, w - - + )} /> @@ -321,7 +345,12 @@ export const WorldCreateFormPage: React.FC = () => { - diff --git a/extensions/apps/world-builder/src/components/pages/world-customize/world-customize-form.tsx b/extensions/apps/world-builder/src/components/pages/world-customise/world-customise-form.tsx similarity index 94% rename from extensions/apps/world-builder/src/components/pages/world-customize/world-customize-form.tsx rename to extensions/apps/world-builder/src/components/pages/world-customise/world-customise-form.tsx index 8bd67f9dc..0ced2e7fb 100644 --- a/extensions/apps/world-builder/src/components/pages/world-customize/world-customize-form.tsx +++ b/extensions/apps/world-builder/src/components/pages/world-customise/world-customise-form.tsx @@ -19,7 +19,7 @@ import { ErrorLoaderFooter, } from '@akashaorg/ui/lib/akasha-components/error-loader'; -export const WorldCustomizeFormPage: React.FC = () => { +export const WorldCustomiseFormPage: React.FC = () => { const { t } = useTranslation('app-extensions'); const { baseRouteName, getCorePlugins } = useRootComponentProps(); @@ -53,7 +53,7 @@ export const WorldCustomizeFormPage: React.FC = () => { {`${t('Uh-oh')}! ${t('You are not connected')}!`} - {`${t('To create a world configuration you must be connected')} ⚡️`} + {`${t('To customise your world you must be connected')} ⚡️`}