From a3bbeb7defc16a7a8bd2208fdbcd7d8ac22684a4 Mon Sep 17 00:00:00 2001 From: Josemaria Nriagu <49484425+josenriagu@users.noreply.github.com> Date: Tue, 7 May 2024 13:58:17 +0100 Subject: [PATCH] Fix: content block render (#2241) * chore(ui): update content block render logic * chore(settings): update base layout style * refactor(lib): update content block renderer logic * refactor(lib): content block extension * chore(ui): revert hook * chore(ui): update content block store * chore(ui): fix logic * refactor(ui): update render logic - add comments - remove redundant state value * chore(ui): comment for test case * refactor(ui): update click handler * chore(ui): revert comment --- .../pages/editor-page/editor-page.tsx | 3 +- .../extensions/beam-editor/footer/index.tsx | 3 +- .../src/extensions/beam-editor/header.tsx | 1 + .../src/plugins/content-block-store.ts | 9 + .../src/components/pages/base-layout.tsx | 2 +- .../src/react/content-block/block-parcel.tsx | 2 +- .../index.tsx} | 157 +++++++----------- .../content-block-extension/render-block.tsx | 91 ++++++++++ .../content-block-extension/render-error.tsx | 42 +++++ .../beam-card/content-block-renderer.tsx | 75 ++++----- 10 files changed, 243 insertions(+), 142 deletions(-) rename ui/lib/extensions/src/react/content-block/{content-block-extension.tsx => content-block-extension/index.tsx} (52%) create mode 100644 ui/lib/extensions/src/react/content-block/content-block-extension/render-block.tsx create mode 100644 ui/lib/extensions/src/react/content-block/content-block-extension/render-error.tsx diff --git a/ui/apps/akasha/src/components/pages/editor-page/editor-page.tsx b/ui/apps/akasha/src/components/pages/editor-page/editor-page.tsx index acece4e466..6c6d3c9c13 100644 --- a/ui/apps/akasha/src/components/pages/editor-page/editor-page.tsx +++ b/ui/apps/akasha/src/components/pages/editor-page/editor-page.tsx @@ -14,11 +14,12 @@ const EditorPage: React.FC = () => { const { getRoutingPlugin } = useRootComponentProps(); const navigateTo = React.useRef(getRoutingPlugin().navigateTo); const { t } = useTranslation(); + return ( - Beam Editor + {t('Beam Editor')} {!authenticatedDID && ( diff --git a/ui/apps/akasha/src/extensions/beam-editor/footer/index.tsx b/ui/apps/akasha/src/extensions/beam-editor/footer/index.tsx index 8824f2789a..f058e94a0c 100644 --- a/ui/apps/akasha/src/extensions/beam-editor/footer/index.tsx +++ b/ui/apps/akasha/src/extensions/beam-editor/footer/index.tsx @@ -58,8 +58,7 @@ export const Footer: React.FC = props => { justify="between" align="center" direction="row" - customStyle="rounded-b-xl" - background={{ light: 'white', dark: 'grey2' }} + customStyle="rounded-b-2xl" > {/* render content based on the value of uiState */} {uiState === 'blocks' && ( diff --git a/ui/apps/akasha/src/extensions/beam-editor/header.tsx b/ui/apps/akasha/src/extensions/beam-editor/header.tsx index 20157a69ce..c40e30ecb7 100644 --- a/ui/apps/akasha/src/extensions/beam-editor/header.tsx +++ b/ui/apps/akasha/src/extensions/beam-editor/header.tsx @@ -42,6 +42,7 @@ export const Header: React.FC = props => { justify={uiState === 'editor' ? 'between' : 'center'} direction="row" align="center" + customStyle="rounded-t-2xl" > {renderTitle()} {uiState === 'editor' && ( diff --git a/ui/apps/extensions/src/plugins/content-block-store.ts b/ui/apps/extensions/src/plugins/content-block-store.ts index 10c1fbe0e4..3be2235d4f 100644 --- a/ui/apps/extensions/src/plugins/content-block-store.ts +++ b/ui/apps/extensions/src/plugins/content-block-store.ts @@ -9,6 +9,15 @@ import { hasOwn } from '@akashaorg/ui-awf-hooks'; import { AkashaContentBlockLabeledValue } from '@akashaorg/typings/lib/sdk/graphql-types-new'; import { BaseStore } from './base-store'; +/** + * When app-loader loads the applications config (the return object of the register function); + * it checks if the config has the 'contentBlocks' property. + * if it does, it will emit the 'ContentBlockEvents.RegisterContentBlock' event. + * + * This class ContentBlockStore listens for this event and stores the received data into the blocks property (this.#blocks). + * + * The getMatchingBlocks method takes as param, 'blockInfo', iterates over the blocks and tries to find the block(s) matching the 'propertyType' and the 'appName' of the passed 'blockInfo' + */ export class ContentBlockStore extends BaseStore { static instance: ContentBlockStore; #blocks: ContentBlockRegisterEvent['data']; diff --git a/ui/apps/settings-app/src/components/pages/base-layout.tsx b/ui/apps/settings-app/src/components/pages/base-layout.tsx index 985f748615..18a2cbfeca 100644 --- a/ui/apps/settings-app/src/components/pages/base-layout.tsx +++ b/ui/apps/settings-app/src/components/pages/base-layout.tsx @@ -11,7 +11,7 @@ export interface IBaseLayout { const BaseLayout: React.FC> = props => { const { title, children } = props; return ( - + {title} diff --git a/ui/lib/extensions/src/react/content-block/block-parcel.tsx b/ui/lib/extensions/src/react/content-block/block-parcel.tsx index c477486854..dad3bac245 100644 --- a/ui/lib/extensions/src/react/content-block/block-parcel.tsx +++ b/ui/lib/extensions/src/react/content-block/block-parcel.tsx @@ -6,7 +6,7 @@ import { BlockInstanceMethods, ContentBlockModes } from '@akashaorg/typings/lib/ import { ParcelConfigObject } from 'single-spa'; import { useRootComponentProps } from '@akashaorg/ui-awf-hooks'; -type BlockParcelProps = { +export type BlockParcelProps = { matchingBlock: MatchingBlock & { config: ParcelConfigObject }; blockId: string; index: number; diff --git a/ui/lib/extensions/src/react/content-block/content-block-extension.tsx b/ui/lib/extensions/src/react/content-block/content-block-extension/index.tsx similarity index 52% rename from ui/lib/extensions/src/react/content-block/content-block-extension.tsx rename to ui/lib/extensions/src/react/content-block/content-block-extension/index.tsx index 889f68c0eb..48c56d3856 100644 --- a/ui/lib/extensions/src/react/content-block/content-block-extension.tsx +++ b/ui/lib/extensions/src/react/content-block/content-block-extension/index.tsx @@ -1,28 +1,27 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import Text from '@akashaorg/design-system-core/lib/components/Text'; -import TextLine from '@akashaorg/design-system-core/lib/components/TextLine'; -import Stack from '@akashaorg/design-system-core/lib/components/Stack'; -import Button from '@akashaorg/design-system-core/lib/components/Button'; +import { ParcelConfigObject } from 'single-spa'; import { hasOwn, useRootComponentProps } from '@akashaorg/ui-awf-hooks'; -import { ContentBlockModes, BlockInstanceMethods } from '@akashaorg/typings/lib/ui'; +import { BlockInstanceMethods, ContentBlockModes } from '@akashaorg/typings/lib/ui'; import { GetContentBlockByIdQuery } from '@akashaorg/typings/lib/sdk/graphql-operation-types-new'; import { useGetContentBlockByIdLazyQuery } from '@akashaorg/ui-awf-hooks/lib/generated/apollo'; -import { ParcelConfigObject } from 'single-spa'; -import { BlockParcel } from './block-parcel'; -import { MatchingBlock } from './common.types'; -import { resolveConfigs } from './resolve-configs'; -import { BlockError, BlockErrorCard } from './block-error-card'; +import { RenderError } from './render-error'; +import { RenderBlock } from './render-block'; +import { BlockError } from '../block-error-card'; +import { resolveConfigs } from '../resolve-configs'; +import { MatchingBlock } from '../common.types'; export type ContentBlockExtensionProps = { blockRef?: React.RefObject; fetchError?: BlockError; contentLoadError?: BlockError; + installButtonLabel: string; notInstalledTitle: string; notInstalledDescription1: string; notInstalledDescription2: string; - //Cache to prevent re-loading block config with the same block id + // cache to prevent re-loading block config with the same block id cacheBlockConfig?: boolean; onError?: (error: Error) => void; + onClickInstall: (e: React.SyntheticEvent) => void; } & ( | { blockID: string; @@ -41,11 +40,13 @@ export const ContentBlockExtension: React.FC = props blockRef, fetchError, contentLoadError, + installButtonLabel, notInstalledTitle, notInstalledDescription1, notInstalledDescription2, cacheBlockConfig, onError, + onClickInstall, ...remainingProps } = props; const { logger, getExtensionsPlugin } = useRootComponentProps(); @@ -53,17 +54,17 @@ export const ContentBlockExtension: React.FC = props const [hasContentLoadError, setHasContentLoadError] = useState(false); const [state, setState] = useState<{ parcels: (MatchingBlock & { config: ParcelConfigObject })[]; - isMatched: boolean; }>({ parcels: [], - isMatched: false, }); const [fetchBlockInfo, blockInfoQuery] = useGetContentBlockByIdLazyQuery(); + // fetch data error const fetchDataError = useMemo(() => { return hasOwn(remainingProps, 'blockData') ? remainingProps?.error : blockInfoQuery.error?.message; }, [remainingProps, blockInfoQuery]); + // block data const blockData = useMemo(() => { if (hasOwn(remainingProps, 'blockData')) { if (remainingProps.blockData && hasOwn(remainingProps.blockData, 'id')) { @@ -75,6 +76,8 @@ export const ContentBlockExtension: React.FC = props } return null; }, [remainingProps, blockInfoQuery]); + + // find matching blocks const matchingBlocks: MatchingBlock[] = useMemo(() => { if (hasOwn(remainingProps, 'blockData') && remainingProps?.matchingBlocks) return remainingProps.matchingBlocks; @@ -82,37 +85,36 @@ export const ContentBlockExtension: React.FC = props }, [blockData, remainingProps]); useLayoutEffect(() => { - if ( - matchingBlocks && - matchingBlocks.length && - matchingBlocks.length !== state.parcels.length && - !state.isMatched - ) { + if (matchingBlocks && matchingBlocks.length !== state.parcels.length) { resolveConfigs({ matchingBlocks, mode: ContentBlockModes.READONLY, cache: cacheBlockConfig }) .then(newBlocks => { setState({ parcels: newBlocks, - isMatched: true, }); }) .catch(err => { setHasContentLoadError(true); logger.error('failed to load content blocks', err); }); - } else if ( - matchingBlocks && - !matchingBlocks.length && - !state.isMatched && - blockInfoQuery.called && - !blockInfoQuery.loading - ) { + } else if (blockInfoQuery.called && !blockInfoQuery.loading) { setState({ parcels: [], - isMatched: true, }); } - }, [logger, blockInfoQuery.called, blockInfoQuery.loading, matchingBlocks, state]); + }, [ + logger, + blockInfoQuery.called, + blockInfoQuery.loading, + matchingBlocks, + state, + cacheBlockConfig, + ]); + /** + * remainingProps could have either just the blockID or other necessary data passed from the parent + * Each of the variables and hooks below evaluates the props available in the remainingProps and acts accordingly. + */ + // if only blockID, fetch the necessary data useEffect(() => { if (hasOwn(remainingProps, 'blockID')) { fetchBlockInfo({ @@ -126,7 +128,6 @@ export const ContentBlockExtension: React.FC = props useEffect(() => { return () => { setState({ - isMatched: false, parcels: [], }); }; @@ -140,74 +141,40 @@ export const ContentBlockExtension: React.FC = props if (hasContentLoadError || fetchDataError) { return ( - - {hasContentLoadError && ( - - )} - {fetchDataError && ( - { - if (hasOwn(remainingProps, 'blockID')) { - blockInfoQuery.refetch({ - id: remainingProps?.blockID, - }); - } - - if (hasOwn(remainingProps, 'blockData')) { - remainingProps.onRefresh?.(); - } - }} - /> - )} - + { + if (hasOwn(remainingProps, 'blockID')) { + blockInfoQuery.refetch({ + id: remainingProps?.blockID, + }); + } + if (hasOwn(remainingProps, 'blockData')) { + remainingProps.onRefresh?.(); + } + }} + /> ); } return ( - <> - {!state.parcels.length && !state.isMatched && ( - - - - - )} - {!state.parcels.length && state.isMatched && appInfo && ( - - - - {appInfo.displayName} {notInstalledTitle} - -