Skip to content

Commit

Permalink
wait for bos-loader fetch. handle local fetch states
Browse files Browse the repository at this point in the history
  • Loading branch information
mpeterdev committed Apr 1, 2024
1 parent 57b94c8 commit 8407b18
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 63 deletions.
6 changes: 3 additions & 3 deletions apps/web/src/components/Inspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Messaging } from '@/components/Messaging';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import { useComponentSourcesStore } from '@/stores/component-sources';
import { useContainerMessagesStore } from '@/stores/container-messages';
import { useFlagsStore } from '@/stores/flags';
import { useDevToolsStore } from '@/stores/dev-tools';
import { usePortalStore } from '@/stores/portal';

export function Inspector() {
Expand Down Expand Up @@ -56,8 +56,8 @@ export function Inspector() {
}
}, [sortedSources, selectedComponent]);

const flags = useFlagsStore((state) => state.flags);
const updateFlags = useFlagsStore((state) => state.updateFlags);
const flags = useDevToolsStore((state) => state.flags);
const updateFlags = useDevToolsStore((state) => state.updateFlags);

const [inputBosLoaderUrl, setInputBosLoaderUrl] = useState(
flags?.bosLoaderUrl || ''
Expand Down
86 changes: 63 additions & 23 deletions apps/web/src/components/WebEngineVariants.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import {
ComponentTree,
UseWebEngineSandboxParams,
WebEngineFlags,
useWebEngine,
useWebEngineSandbox,
} from '@bos-web-engine/application';
import { AccountState } from '@near-wallet-selector/core';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';

import { useComponentSourcesStore } from '@/stores/component-sources';
import { useContainerMessagesStore } from '@/stores/container-messages';
import { LocalFetchStatus, useDevToolsStore } from '@/stores/dev-tools';

const PREACT_VERSION = '10.17.1';

Expand Down Expand Up @@ -60,34 +62,72 @@ export function SandboxWebEngine({
rootComponentPath,
flags,
}: WebEnginePropsVariantProps) {
const addSource = useComponentSourcesStore((store) => store.addSource);
const addMessage = useContainerMessagesStore((store) => store.addMessage);
const setLocalFetchStatus = useDevToolsStore(
(state) => state.setLocalFetchStatus
);

// null while loading
// empty object on error
const [localComponents, setLocalComponents] = useState<
UseWebEngineSandboxParams['localComponents'] | null
>(null);

const fetchLocalComponents = useCallback(
async (url: string) => {
setLocalFetchStatus(LocalFetchStatus.LOADING);
try {
const response = await fetch(url);
const data = await response.json();

const [localComponents, setLocalComponents] = useState();
// FIXME: change engine to expect `code` so we do not need to
// transform data from having property called `code` to property called `component`
Object.keys(data.components).forEach((key) => {
data.components[key].component = data.components[key].code + '\n';
delete data.components[key].code;
});

setLocalComponents(data.components);
setLocalFetchStatus(LocalFetchStatus.SUCCESS);
} catch (error) {
console.error(`Failed to load local components from ${url}`, error);
setLocalComponents({});
setLocalFetchStatus(LocalFetchStatus.ERROR);
}
},
[setLocalFetchStatus]
);

useEffect(() => {
if (!flags?.bosLoaderUrl) return;

fetchLocalComponents(flags?.bosLoaderUrl);
}, [flags?.bosLoaderUrl]);

async function fetchLocalComponents(url: string) {
try {
const response = await fetch(url);
const data = await response.json();

// FIXME: change engine to expect `code` so we do not need to
// transform data from having property called `code` to property called `component`
Object.keys(data.components).forEach((key) => {
data.components[key].component = data.components[key].code + '\n';
delete data.components[key].code;
});

setLocalComponents(data.components);
} catch (error) {
console.error(error);
}
}
}, [flags?.bosLoaderUrl, fetchLocalComponents]);

return localComponents ? (
<PreparedLocalSandbox
account={account}
rootComponentPath={rootComponentPath}
flags={flags}
localComponents={localComponents}
/>
) : (
<></>
);
}

/**
* Actual sandbox redering takes place here, but should only be loaded
* once local components have been successfully resolved for the first
* time at startup
*/
function PreparedLocalSandbox({
account,
rootComponentPath,
flags,
localComponents,
}: WebEnginePropsVariantProps & { localComponents: any }) {
const addSource = useComponentSourcesStore((store) => store.addSource);
const addMessage = useContainerMessagesStore((store) => store.addMessage);

const { components, error, nonce } = useWebEngineSandbox({
config: {
Expand Down
7 changes: 4 additions & 3 deletions apps/web/src/pages/[[...root]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect } from 'react';

import { Inspector } from '@/components/Inspector';
import { SandboxWebEngine, WebEngine } from '@/components/WebEngineVariants';
import { useFlagsStore } from '@/stores/flags';
import { useDevToolsStore } from '@/stores/dev-tools';

const DEFAULT_COMPONENT = process.env.NEXT_PUBLIC_DEFAULT_ROOT_COMPONENT;

Expand All @@ -20,7 +20,8 @@ export default function Root() {
? query.root.join('/')
: undefined;

const flags = useFlagsStore((state) => state.flags);
const flags = useDevToolsStore((state) => state.flags);
const devToolsLoaded = useDevToolsStore((state) => state.devToolsLoaded);

const showContainerBoundaries =
queryShowContainerBoundaries || flags.showContainerBoundaries;
Expand All @@ -38,7 +39,7 @@ export default function Root() {
<div
className={`bwe-app ${showContainerBoundaries ? 'bwe-debug' : ''}`}
>
{rootComponentPath && (
{rootComponentPath && devToolsLoaded && (
<>
{flags?.bosLoaderUrl ? (
<SandboxWebEngine
Expand Down
96 changes: 66 additions & 30 deletions apps/web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@ import '@near-wallet-selector/modal-ui/styles.css';
import '@/styles/globals.css';

import { SocialProvider } from '@bos-web-engine/social-db';
import { NearIconSvg, ThemeProvider, Tooltip } from '@bos-web-engine/ui';
import {
NearIconSvg,
Spinner,
ThemeProvider,
Tooltip,
} from '@bos-web-engine/ui';
import {
WalletSelectorControl,
WalletSelectorProvider,
} from '@bos-web-engine/wallet-selector-control';
import type { WalletSelector } from '@near-wallet-selector/core';
import type { AppProps } from 'next/app';
import Link from 'next/link';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';

import { MAINNET_WALLET_SELECTOR_PARAMS } from '@/constants';
import { useFlagsStore } from '@/stores/flags';
import { LocalFetchStatus, useDevToolsStore } from '@/stores/dev-tools';
import { usePortalStore } from '@/stores/portal';
import s from '@/styles/app.module.css';

Expand All @@ -26,17 +31,20 @@ export default function App({ Component, pageProps }: AppProps) {
);

// load application flags from browser storage on startup
const updateFlags = useFlagsStore((state) => state.updateFlags);
const updateFlags = useDevToolsStore((state) => state.updateFlags);
const markDevToolsLoaded = useDevToolsStore(
(state) => state.markDevToolsLoaded
);
useEffect(() => {
const savedFlags = localStorage.getItem('flags')
? JSON.parse(localStorage.getItem('flags') || '')
: null;
if (savedFlags) {
updateFlags(savedFlags);
}
}, [updateFlags]);
markDevToolsLoaded();
}, [updateFlags, markDevToolsLoaded]);

const flags = useFlagsStore((state) => state.flags);
const headerRef = useRef(null);
const setPortal = usePortalStore((store) => store.setPortal);

Expand All @@ -59,30 +67,7 @@ export default function App({ Component, pageProps }: AppProps) {
<NearIconSvg />
<h1>BWE Demo</h1>
</Link>
<div className={s.loaderIcon}>
{flags?.bosLoaderUrl && (
<Tooltip
content={
<>
Loading components from:<code>{flags.bosLoaderUrl}</code>
</>
}
side="right"
container={headerRef.current}
className={s.tooltip}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
fill="hsl(115, 42.60%, 46.50%)"
viewBox="0 0 256 256"
>
<path d="M24,96v72a8,8,0,0,0,8,8h80a8,8,0,0,1,0,16H96v16h16a8,8,0,0,1,0,16H64a8,8,0,0,1,0-16H80V192H32A24,24,0,0,1,8,168V96A24,24,0,0,1,32,72h80a8,8,0,0,1,0,16H32A8,8,0,0,0,24,96ZM208,64H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm0,32H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm40-48V208a16,16,0,0,1-16,16H152a16,16,0,0,1-16-16V48a16,16,0,0,1,16-16h80A16,16,0,0,1,248,48ZM232,208V48H152V208h80Zm-40-40a12,12,0,1,0,12,12A12,12,0,0,0,192,168Z"></path>
</svg>
</Tooltip>
)}
</div>
<LocalFetchIndicator />

<WalletSelectorControl />
</header>
Expand All @@ -95,3 +80,54 @@ export default function App({ Component, pageProps }: AppProps) {
</WalletSelectorProvider>
);
}

function LocalFetchIndicator() {
const flags = useDevToolsStore((state) => state.flags);
const localFetchStatus = useDevToolsStore((state) => state.localFetchStatus);
const portal = usePortalStore((store) => store.portal);

const indicator = useMemo(() => {
if (localFetchStatus === LocalFetchStatus.NONE) {
return <></>;
}

if (localFetchStatus === LocalFetchStatus.LOADING) {
return <Spinner size="1.5rem" />;
}

// success or error
return (
<Tooltip
content={
<>
{localFetchStatus === LocalFetchStatus.SUCCESS ? (
<>
Loading components from:
<code>{flags.bosLoaderUrl}</code>
</>
) : (
'An error occurred while loading local components. Check your console'
)}
</>
}
side="right"
container={portal}
className={s.tooltip}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
data-status={
localFetchStatus === LocalFetchStatus.ERROR ? 'error' : ''
}
viewBox="0 0 256 256"
>
<path d="M24,96v72a8,8,0,0,0,8,8h80a8,8,0,0,1,0,16H96v16h16a8,8,0,0,1,0,16H64a8,8,0,0,1,0-16H80V192H32A24,24,0,0,1,8,168V96A24,24,0,0,1,32,72h80a8,8,0,0,1,0,16H32A8,8,0,0,0,24,96ZM208,64H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm0,32H176a8,8,0,0,0,0,16h32a8,8,0,0,0,0-16Zm40-48V208a16,16,0,0,1-16,16H152a16,16,0,0,1-16-16V48a16,16,0,0,1,16-16h80A16,16,0,0,1,248,48ZM232,208V48H152V208h80Zm-40-40a12,12,0,1,0,12,12A12,12,0,0,0,192,168Z"></path>
</svg>
</Tooltip>
);
}, [localFetchStatus, flags.bosLoaderUrl, portal]);

return <div className={`${s.loaderIcon}`}>{indicator}</div>;
}
File renamed without changes.
5 changes: 1 addition & 4 deletions apps/web/src/styles/app.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@
svg {
fill: hsl(115, 42.6%, 46.5%);
}
}

.loaderError {
svg {
svg[data-status='error'] {
fill: hsl(0, 64%, 42%);
}
}
Expand Down

0 comments on commit 8407b18

Please sign in to comment.