mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-03-31 15:20:32 +08:00
feat: Link and basePath implementation for dev server.
This commit is contained in:
parent
1294914fae
commit
d487a1c7fd
@ -16,6 +16,7 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { urlQuery } from '@lowdefy/helpers';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import Page from './Page.js';
|
||||
@ -23,26 +24,27 @@ import Reload from './Reload.js';
|
||||
import setPageId from '../utils/setPageId.js';
|
||||
import setupLink from '../utils/setupLink.js';
|
||||
import useRootConfig from '../utils/useRootConfig.js';
|
||||
import createComponents from './createComponents.js';
|
||||
|
||||
const App = ({ lowdefy }) => {
|
||||
const router = useRouter();
|
||||
const { data: rootConfig } = useRootConfig();
|
||||
const { data: rootConfig } = useRootConfig(router.basePath);
|
||||
|
||||
window.lowdefy = lowdefy;
|
||||
|
||||
lowdefy._internal.router = router;
|
||||
lowdefy._internal.link = setupLink(lowdefy);
|
||||
lowdefy._internal.components = createComponents(lowdefy);
|
||||
|
||||
lowdefy.basePath = lowdefy._internal.router.basePath;
|
||||
lowdefy.home = rootConfig.home;
|
||||
lowdefy.lowdefyGlobal = rootConfig.lowdefyGlobal;
|
||||
lowdefy.menus = rootConfig.menus;
|
||||
|
||||
lowdefy._internal.basePath = router.basePath;
|
||||
lowdefy._internal.pathname = router.pathname;
|
||||
lowdefy._internal.query = router.query;
|
||||
lowdefy._internal.router = router;
|
||||
lowdefy._internal.link = setupLink({ lowdefy });
|
||||
lowdefy.urlQuery = urlQuery.parse(window.location.search.slice(1));
|
||||
|
||||
const redirect = setPageId(lowdefy);
|
||||
if (redirect) {
|
||||
lowdefy._internal.router.push(`/${lowdefy.pageId}`);
|
||||
lowdefy._internal.router.push(`${lowdefy.basePath}/${lowdefy.pageId}`); // TODO: test redirect
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -19,32 +19,30 @@ import React from 'react';
|
||||
import callRequest from '../utils/callRequest.js';
|
||||
import blockComponents from '../../build/plugins/blocks.js';
|
||||
import operators from '../../build/plugins/operatorsClient.js';
|
||||
import components from './components.js';
|
||||
|
||||
const LowdefyContext = ({ children }) => {
|
||||
const lowdefy = {
|
||||
_internal: {
|
||||
const LowdefyContext = ({ children, lowdefy }) => {
|
||||
if (!lowdefy._internal) {
|
||||
lowdefy._internal = {
|
||||
blockComponents,
|
||||
callRequest,
|
||||
components,
|
||||
components: {},
|
||||
document,
|
||||
operators,
|
||||
updaters: {},
|
||||
window,
|
||||
displayMessage: ({ content }) => {
|
||||
alert(content);
|
||||
console.log(content);
|
||||
return () => undefined;
|
||||
},
|
||||
link: () => undefined,
|
||||
},
|
||||
contexts: {},
|
||||
inputs: {},
|
||||
lowdefyGlobal: {},
|
||||
};
|
||||
};
|
||||
lowdefy.contexts = {};
|
||||
lowdefy.inputs = {};
|
||||
lowdefy.lowdefyGlobal = {};
|
||||
}
|
||||
lowdefy._internal.updateBlock = (blockId) =>
|
||||
lowdefy._internal.updaters[blockId] && lowdefy._internal.updaters[blockId]();
|
||||
|
||||
return <>{children(lowdefy)}</>;
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default LowdefyContext;
|
||||
|
@ -24,9 +24,9 @@ import usePageConfig from '../utils/usePageConfig.js';
|
||||
const LoadingBlock = () => <div>Loading...</div>;
|
||||
|
||||
const Page = ({ lowdefy }) => {
|
||||
const { data: pageConfig } = usePageConfig(lowdefy.pageId);
|
||||
const { data: pageConfig } = usePageConfig(lowdefy.pageId, lowdefy.basePath);
|
||||
if (!pageConfig) {
|
||||
lowdefy._internal.router.replace(`/404`);
|
||||
lowdefy._internal.router.replace(`${lowdefy.basePath}/404`); // TODO: test redirect
|
||||
return <LoadingBlock />;
|
||||
}
|
||||
return (
|
||||
|
@ -20,9 +20,9 @@ import useMutateCache from '../utils/useMutateCache.js';
|
||||
import waitForRestartedServer from '../utils/waitForRestartedServer.js';
|
||||
|
||||
const Reload = ({ children, lowdefy }) => {
|
||||
const mutateCache = useMutateCache();
|
||||
const mutateCache = useMutateCache(lowdefy.basePath);
|
||||
useEffect(() => {
|
||||
const sse = new EventSource('/api/reload');
|
||||
const sse = new EventSource(`${lowdefy.basePath}/api/reload`);
|
||||
|
||||
sse.addEventListener('reload', () => {
|
||||
mutateCache();
|
||||
|
@ -64,12 +64,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => {
|
||||
setValue: block.setValue,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
// TODO: React throws a basePath warning
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
@ -98,11 +96,10 @@ const CategorySwitch = ({ block, Blocks, context, lowdefy }) => {
|
||||
registerMethod: block.registerMethod,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
|
@ -65,12 +65,11 @@ const Container = ({ block, Blocks, Component, context, lowdefy }) => {
|
||||
registerMethod: block.registerMethod,
|
||||
triggerEvent: block.triggerEvent,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
content={content}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
loading={block.loading}
|
||||
menus={lowdefy.menus}
|
||||
|
@ -72,11 +72,10 @@ const List = ({ block, Blocks, Component, context, lowdefy }) => {
|
||||
triggerEvent: block.triggerEvent,
|
||||
unshiftItem: block.unshiftItem,
|
||||
})}
|
||||
basePath={lowdefy._internal.basePath}
|
||||
basePath={lowdefy.basePath}
|
||||
blockId={block.blockId}
|
||||
components={lowdefy._internal.components}
|
||||
events={block.eval.events}
|
||||
homePageId={lowdefy.home.pageId}
|
||||
key={block.blockId}
|
||||
list={contentList}
|
||||
loading={block.loading}
|
||||
|
@ -14,12 +14,16 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Link from 'next/link';
|
||||
import { createIcon } from '@lowdefy/block-utils';
|
||||
|
||||
import createLinkComponent from './createLinkComponent.js';
|
||||
import icons from '../../build/plugins/icons.js';
|
||||
|
||||
export default {
|
||||
Link,
|
||||
Icon: createIcon(icons),
|
||||
const createComponents = (lowdefy) => {
|
||||
return {
|
||||
Link: createLinkComponent(lowdefy),
|
||||
Icon: createIcon(icons),
|
||||
};
|
||||
};
|
||||
|
||||
export default createComponents;
|
97
packages/server-dev/src/components/createLinkComponent.js
Normal file
97
packages/server-dev/src/components/createLinkComponent.js
Normal file
@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import NextLink from 'next/link';
|
||||
import { createLink } from '@lowdefy/engine';
|
||||
import { type } from '@lowdefy/helpers';
|
||||
|
||||
const createLinkComponent = (lowdefy) => {
|
||||
const backLink = ({ ariaLabel, children, className, id, rel }) => (
|
||||
<a
|
||||
id={id}
|
||||
onClick={() => lowdefy._internal.router.back()}
|
||||
className={className}
|
||||
rel={rel}
|
||||
aria-label={ariaLabel || 'back'}
|
||||
>
|
||||
{type.isFunction(children) ? children(id) : children}
|
||||
</a>
|
||||
);
|
||||
const newOriginLink = ({
|
||||
ariaLabel,
|
||||
children,
|
||||
className,
|
||||
id,
|
||||
newTab,
|
||||
pageId,
|
||||
query,
|
||||
rel,
|
||||
url,
|
||||
}) => {
|
||||
return (
|
||||
<a
|
||||
id={id}
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
href={`${url}${query ? `?${query}` : ''}`}
|
||||
rel={rel || (newTab && 'noopener noreferrer')}
|
||||
target={newTab && '_blank'}
|
||||
>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
const sameOriginLink = ({
|
||||
ariaLabel,
|
||||
children,
|
||||
className,
|
||||
id,
|
||||
newTab,
|
||||
pageId,
|
||||
pathname,
|
||||
query,
|
||||
rel,
|
||||
replace,
|
||||
scroll,
|
||||
setInput,
|
||||
url,
|
||||
}) => {
|
||||
if (newTab) {
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-no-target-blank
|
||||
<a
|
||||
id={id}
|
||||
aria-label={ariaLabel}
|
||||
className={className}
|
||||
href={`${window.location.origin}${lowdefy.basePath}${pathname}${
|
||||
query ? `?${query}` : ''
|
||||
}`}
|
||||
rel={rel || 'noopener noreferrer'}
|
||||
target="_blank"
|
||||
>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<NextLink href={{ pathname, query }} replace={replace} scroll={scroll}>
|
||||
<a id={id} aria-label={ariaLabel} className={className} rel={rel} onClick={setInput}>
|
||||
{type.isFunction(children) ? children(pageId || url || id) : children}
|
||||
</a>
|
||||
</NextLink>
|
||||
);
|
||||
};
|
||||
const noLink = ({ className, children, id }) => (
|
||||
<span id={id} className={className}>
|
||||
{type.isFunction(children) ? children(id) : children}
|
||||
</span>
|
||||
);
|
||||
return createLink({
|
||||
backLink,
|
||||
lowdefy,
|
||||
newOriginLink,
|
||||
sameOriginLink,
|
||||
noLink,
|
||||
disabledLink: noLink,
|
||||
});
|
||||
};
|
||||
|
||||
export default createLinkComponent;
|
@ -22,12 +22,14 @@ import LowdefyContext from '../components/LowdefyContext.js';
|
||||
|
||||
import '../../build/plugins/styles.less';
|
||||
|
||||
const lowdefy = {};
|
||||
|
||||
function App({ Component, pageProps }) {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Suspense>
|
||||
<LowdefyContext>
|
||||
{(lowdefy) => <Component lowdefy={lowdefy} {...pageProps} />}
|
||||
<LowdefyContext lowdefy={lowdefy}>
|
||||
<Component lowdefy={lowdefy} {...pageProps} />
|
||||
</LowdefyContext>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
|
@ -25,6 +25,7 @@ export default async function handler(req, res) {
|
||||
throw new Error('Only POST requests are supported.');
|
||||
}
|
||||
// TODO: configure API context
|
||||
// TODO: configure build directory?
|
||||
const apiContext = await createApiContext({
|
||||
buildDirectory: './build',
|
||||
connections,
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
import request from './request.js';
|
||||
|
||||
function callRequest({ pageId, payload, requestId }) {
|
||||
function callRequest(apiContext, { pageId, payload, requestId }) {
|
||||
return request({
|
||||
url: `/api/request/${pageId}/${requestId}`,
|
||||
url: `${apiContext.config.basePath}/api/request/${pageId}/${requestId}`,
|
||||
method: 'POST',
|
||||
body: { payload },
|
||||
});
|
||||
|
@ -15,18 +15,18 @@
|
||||
*/
|
||||
|
||||
function setPageId(lowdefy) {
|
||||
if (lowdefy._internal.pathname === '/404') {
|
||||
if (lowdefy._internal.router.pathname === `/404`) {
|
||||
lowdefy.pageId = '404';
|
||||
return false;
|
||||
}
|
||||
if (!lowdefy._internal.query.pageId) {
|
||||
if (!lowdefy._internal.router.query.pageId) {
|
||||
lowdefy.pageId = lowdefy.home.pageId;
|
||||
if (lowdefy.home.configured === false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
lowdefy.pageId = lowdefy._internal.query.pageId;
|
||||
lowdefy.pageId = lowdefy._internal.router.query.pageId;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -16,29 +16,37 @@
|
||||
|
||||
import { createLink } from '@lowdefy/engine';
|
||||
|
||||
function setupLink({ lowdefy }) {
|
||||
function setupLink(lowdefy) {
|
||||
const { router, window } = lowdefy._internal;
|
||||
const sameOriginLink = (path, newTab) => {
|
||||
const backLink = () => router.back();
|
||||
const disabledLink = () => {};
|
||||
const newOriginLink = ({ url, query, newTab }) => {
|
||||
if (newTab) {
|
||||
return window.open(`${window.location.origin}${lowdefy.basePath}${path}`, '_blank').focus();
|
||||
return window.open(`${url}${query ? `?${query}` : ''}`, '_blank').focus();
|
||||
} else {
|
||||
// Next handles the basePath here.
|
||||
return window.location.assign(`${url}${query ? `?${query}` : ''}`);
|
||||
}
|
||||
};
|
||||
const sameOriginLink = ({ newTab, pathname, query, setInput }) => {
|
||||
if (newTab) {
|
||||
return window
|
||||
.open(
|
||||
`${window.location.origin}${lowdefy.basePath}${pathname}${query ? `?${query}` : ''}`,
|
||||
'_blank'
|
||||
)
|
||||
.focus();
|
||||
} else {
|
||||
setInput();
|
||||
return router.push({
|
||||
pathname: path,
|
||||
// TODO: Do we handle urlQuery as a param here?
|
||||
// query: {},
|
||||
pathname,
|
||||
query,
|
||||
});
|
||||
}
|
||||
};
|
||||
const newOriginLink = (path, newTab) => {
|
||||
if (newTab) {
|
||||
return window.open(path, '_blank').focus();
|
||||
} else {
|
||||
return (window.location.href = path);
|
||||
}
|
||||
const noLink = () => {
|
||||
throw new Error(`Invalid Link.`);
|
||||
};
|
||||
const backLink = () => window.history.back();
|
||||
return createLink({ backLink, lowdefy, newOriginLink, sameOriginLink });
|
||||
return createLink({ backLink, disabledLink, lowdefy, newOriginLink, noLink, sameOriginLink });
|
||||
}
|
||||
|
||||
export default setupLink;
|
||||
|
@ -16,13 +16,13 @@
|
||||
|
||||
import { useSWRConfig } from 'swr';
|
||||
|
||||
function useMutateCache() {
|
||||
function useMutateCache(basePath) {
|
||||
const { cache, mutate } = useSWRConfig();
|
||||
return () => {
|
||||
const keys = ['/api/root'];
|
||||
const keys = [`${basePath}/api/root`];
|
||||
|
||||
for (const key of cache.keys()) {
|
||||
if (key.startsWith('/api/page')) {
|
||||
if (key.startsWith(`${basePath}/api/page`)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,8 @@ function fetchPageConfig(url) {
|
||||
return request({ url });
|
||||
}
|
||||
|
||||
function usePageConfig(pageId) {
|
||||
if (!pageId) {
|
||||
pageId = 'NULL';
|
||||
}
|
||||
const { data } = useSWR(`/api/page/${pageId}`, fetchPageConfig, { suspense: true });
|
||||
function usePageConfig(pageId, basePath) {
|
||||
const { data } = useSWR(`${basePath}/api/page/${pageId}`, fetchPageConfig, { suspense: true });
|
||||
return { data };
|
||||
}
|
||||
|
||||
|
@ -17,12 +17,12 @@ import request from './request.js';
|
||||
|
||||
// TODO: Handle TokenExpiredError
|
||||
|
||||
function fetchRootConfig() {
|
||||
return request({ url: '/api/root' });
|
||||
function fetchRootConfig(url) {
|
||||
return request({ url });
|
||||
}
|
||||
|
||||
function useRootConfig() {
|
||||
const { data } = useSWR('root', fetchRootConfig, { suspense: true });
|
||||
function useRootConfig(basePath) {
|
||||
const { data } = useSWR(`${basePath}/api/root`, fetchRootConfig, { suspense: true });
|
||||
return { data };
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ function waitForRestartedServer(lowdefy) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await request({
|
||||
url: '/api/ping',
|
||||
url: `${lowdefy.basePath}/api/ping`,
|
||||
});
|
||||
lowdefy._internal.window.location.reload();
|
||||
} catch (error) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user