Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read URL Query Params MVP #428

Merged
merged 1 commit into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/sandbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"react-dom": "^18"
},
"devDependencies": {
"@bos-web-engine/common": "workspace:*",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
Expand Down
27 changes: 27 additions & 0 deletions apps/sandbox/src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { QueryParams } from '@bos-web-engine/common';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';

export function useQueryParams() {
const searchParams = useSearchParams();
const [queryParams, setQueryParams] = useState<QueryParams>({});

useEffect(() => {
/*
This pattern gives us a more stable reference for queryParams to reduce
re-renders. We only update our state when searchParams changes.
*/

const params: QueryParams = {};

searchParams.forEach((value, key) => {
params[key] = value;
});

setQueryParams(params);
}, [searchParams]);

return {
queryParams,
};
}
8 changes: 7 additions & 1 deletion apps/sandbox/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Sandbox } from '@bos-web-engine/sandbox';

import { useQueryParams } from '@/hooks/useQueryParams';
import s from '@/styles/home.module.css';

export default function Home() {
const { queryParams } = useQueryParams();

return (
<div className={s.wrapper}>
<Sandbox height="calc(100vh - var(--gateway-header-height))" />
<Sandbox
height="calc(100vh - var(--gateway-header-height))"
queryParams={queryParams}
/>
</div>
);
}
5 changes: 5 additions & 0 deletions apps/web/src/components/WebEngineVariants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useHotReload } from '@bos-web-engine/hot-reload-client';
import { AccountState } from '@near-wallet-selector/core';
import { useCallback, useEffect, useState } from 'react';

import { useQueryParams } from '@/hooks/useQueryParams';
import { useComponentSourcesStore } from '@/stores/component-sources';
import { useContainerMessagesStore } from '@/stores/container-messages';
import { LocalFetchStatus, useDevToolsStore } from '@/stores/dev-tools';
Expand All @@ -25,6 +26,7 @@ export function WebEngine({
rootComponentPath,
flags,
}: WebEnginePropsVariantProps) {
const { queryParams } = useQueryParams();
const addSource = useComponentSourcesStore((store) => store.addSource);
const addMessage = useContainerMessagesStore((store) => store.addMessage);

Expand All @@ -41,6 +43,7 @@ export function WebEngine({
},
},
rootComponentPath,
queryParams,
});

return (
Expand Down Expand Up @@ -131,6 +134,7 @@ function PreparedLocalSandbox({
flags,
localComponents,
}: WebEnginePropsVariantProps & { localComponents: any }) {
const { queryParams } = useQueryParams();
const addSource = useComponentSourcesStore((store) => store.addSource);
const addMessage = useContainerMessagesStore((store) => store.addMessage);

Expand All @@ -148,6 +152,7 @@ function PreparedLocalSandbox({
},
localComponents,
rootComponentPath,
queryParams,
});

return (
Expand Down
27 changes: 27 additions & 0 deletions apps/web/src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { QueryParams } from '@bos-web-engine/common';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';

export function useQueryParams() {
const searchParams = useSearchParams();
const [queryParams, setQueryParams] = useState<QueryParams>({});

useEffect(() => {
/*
This pattern gives us a more stable reference for queryParams to reduce
re-renders. We only update our state when searchParams changes.
*/

const params: QueryParams = {};

searchParams.forEach((value, key) => {
params[key] = value;
});

setQueryParams(params);
}, [searchParams]);

return {
queryParams,
};
}
11 changes: 9 additions & 2 deletions packages/application/src/components/ComponentTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ export default function ComponentTree({
.map(
([
componentId,
{ trust, props, componentSource, parentId, moduleImports },
{
trust,
props,
componentSource,
parentId,
moduleImports,
queryParams,
},
]) => (
/*
NOTE: Including currentUserAccountId as part of the key forces the entire tree
Expand All @@ -48,7 +55,7 @@ export default function ComponentTree({
id={getIframeId(componentId)}
trust={trust}
scriptSrc={componentSource}
componentProps={props}
componentProps={{ ...queryParams, ...props }}
parentContainerId={parentId}
moduleImports={moduleImports}
/>
Expand Down
39 changes: 26 additions & 13 deletions packages/application/src/hooks/useComponents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ComponentCompilerResponse } from '@bos-web-engine/compiler';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import { UseComponentsParams } from '../types';

Expand All @@ -15,6 +15,7 @@ export function useComponents({
compiler,
config,
rootComponentPath,
queryParams,
}: UseComponentsParams) {
const [components, setComponents] = useState<{ [key: string]: any }>({});
const [isValidRootComponentPath, setIsValidRootComponentPath] =
Expand Down Expand Up @@ -42,8 +43,6 @@ export function useComponents({
[components]
);

const hooks = { ...config?.hooks } || {};

useEffect(() => {
setIsValidRootComponentPath(
!!rootComponentPath &&
Expand All @@ -53,16 +52,22 @@ export function useComponents({
);
}, [rootComponentPath]);

hooks.componentRendered = (componentId: string) => {
config?.hooks?.componentRendered?.(componentId);
setComponents((currentComponents) => ({
...currentComponents,
[componentId]: {
...currentComponents[componentId],
renderCount: currentComponents?.[componentId]?.renderCount + 1 || 0,
},
}));
};
const hooks = useMemo(() => {
const result = { ...config?.hooks } || {};

result.componentRendered = (componentId: string) => {
config?.hooks?.componentRendered?.(componentId);
setComponents((currentComponents) => ({
...currentComponents,
[componentId]: {
...currentComponents[componentId],
renderCount: currentComponents?.[componentId]?.renderCount + 1 || 0,
},
}));
};

return result;
}, [config?.hooks]);

useEffect(() => {
if (!rootComponentPath || !isValidRootComponentPath || !compiler) {
Expand All @@ -78,6 +83,7 @@ export function useComponents({
containerStyles,
error: loadError,
importedModules,
queryParams,
} = data;

if (loadError) {
Expand All @@ -96,6 +102,7 @@ export function useComponents({
componentId,
componentSource,
moduleImports: importedModules,
queryParams,
};

if (!rootComponentSource && componentId === rootComponentPath) {
Expand All @@ -108,14 +115,20 @@ export function useComponents({
compiler.postMessage({
action: 'execute',
componentId: rootComponentPath,
queryParams,
});
}, [
addComponent,
appendStylesheet,
components,
hooks,
compiler,
rootComponentPath,
rootComponentSource,
error,
isValidRootComponentPath,
config?.flags?.bosLoaderUrl,
queryParams,
]);

return {
Expand Down
2 changes: 2 additions & 0 deletions packages/application/src/hooks/useWebEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { UseWebEngineParams } from '../types';
export function useWebEngine({
config,
rootComponentPath,
queryParams,
}: UseWebEngineParams) {
const { appendStylesheet } = useCss();
const compiler = useCompiler({ config });
Expand All @@ -16,6 +17,7 @@ export function useWebEngine({
compiler,
config,
rootComponentPath,
queryParams,
});

useComponentTree({
Expand Down
2 changes: 2 additions & 0 deletions packages/application/src/hooks/useWebEngineSandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function useWebEngineSandbox({
localComponents,
config,
rootComponentPath,
queryParams,
}: UseWebEngineSandboxParams) {
const [nonce, setNonce] = useState('');

Expand All @@ -27,6 +28,7 @@ export function useWebEngineSandbox({
compiler,
config,
rootComponentPath,
queryParams,
});

const { domRoots } = useComponentTree({
Expand Down
2 changes: 2 additions & 0 deletions packages/application/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ComponentChildMetadata,
ComponentTrust,
MessagePayload,
QueryParams,
SerializedArgs,
SerializedNode,
} from '@bos-web-engine/common';
Expand Down Expand Up @@ -119,6 +120,7 @@ export interface CompilerWorker extends Omit<Worker, 'postMessage'> {
export interface UseWebEngineParams {
config?: WebEngineConfiguration;
rootComponentPath?: string;
queryParams?: QueryParams;
}

export interface UseComponentsParams extends UseWebEngineParams {
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './plugins';
export * from './render';
export * from './serialization';
export * from './trust';
export * from './url';
1 change: 1 addition & 0 deletions packages/common/src/types/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type QueryParams = Record<string, string | undefined>;
4 changes: 3 additions & 1 deletion packages/compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export class ComponentCompiler {
* Build the source for a container rooted at the target Component
* @param componentId ID for the new container's root Component
*/
async compileComponent({ componentId }: CompilerExecuteAction) {
async compileComponent({ componentId, queryParams }: CompilerExecuteAction) {
// wait on CSS initialization
await this.cssParser.init();

Expand Down Expand Up @@ -299,6 +299,7 @@ export class ComponentCompiler {
rawSource: moduleEntry.component,
componentPath,
importedModules: retrievedData.importedModules,
queryParams,
});

return;
Expand Down Expand Up @@ -354,6 +355,7 @@ export class ComponentCompiler {
rawSource: moduleEntry.component,
componentPath,
importedModules,
queryParams,
});
}
}
4 changes: 3 additions & 1 deletion packages/compiler/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BOSModule } from '@bos-web-engine/common';
import type { BOSModule, QueryParams } from '@bos-web-engine/common';
import type {
BLOCK_HEIGHT_KEY,
SOCIAL_COMPONENT_NAMESPACE,
Expand All @@ -12,6 +12,7 @@ export type ComponentCompilerRequest =
export interface CompilerExecuteAction {
action: 'execute';
componentId: string;
queryParams?: QueryParams;
}

export type LocalComponentMap = { [path: string]: BOSModule };
Expand All @@ -35,6 +36,7 @@ export interface ComponentCompilerResponse {
componentPath: string;
error?: Error;
importedModules: Map<string, string>;
queryParams?: QueryParams;
}

export type SendMessageCallback = (res: ComponentCompilerResponse) => void;
Expand Down
2 changes: 2 additions & 0 deletions packages/sandbox/src/components/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type WebEngineLocalComponents = { [path: string]: BOSModule };

export function Preview() {
const { account } = useWallet();
const queryParams = useSandboxStore((store) => store.queryParams);
const containerElement = useSandboxStore((store) => store.containerElement);
const activeFilePath = useSandboxStore((store) => store.activeFilePath);
const pinnedPreviewFilePath = useSandboxStore(
Expand All @@ -43,6 +44,7 @@ export function Preview() {
const { components, nonce } = useWebEngineSandbox({
localComponents,
rootComponentPath,
queryParams,
});

useEffect(() => {
Expand Down
9 changes: 8 additions & 1 deletion packages/sandbox/src/components/Sandbox.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QueryParams } from '@bos-web-engine/common';
import { useEffect, useRef, useState } from 'react';

import { Layout } from './Layout';
Expand All @@ -8,13 +9,15 @@ import { useSourceAccountReplace } from '../hooks/useSourceAccountReplace';

type Props = {
height: string;
queryParams?: QueryParams;
};

export function Sandbox({ height }: Props) {
export function Sandbox({ height, queryParams }: Props) {
const containerRef = useRef<HTMLDivElement | null>(null);
const setContainerElement = useSandboxStore(
(store) => store.setContainerElement
);
const setQueryParams = useSandboxStore((store) => store.setQueryParams);
const [shouldRender, setShouldRender] = useState(false);

usePublishedFilesSync();
Expand All @@ -34,6 +37,10 @@ export function Sandbox({ height }: Props) {
}
});

useEffect(() => {
setQueryParams(queryParams);
}, [setQueryParams, queryParams]);

if (!shouldRender) return null;

return (
Expand Down
Loading
Loading