feat(renderer): add renderer rootcontext

This commit is contained in:
Sam Tolmay 2020-10-09 16:58:51 +02:00
parent 6f35d1e64f
commit 8b1da55133
9 changed files with 248 additions and 44 deletions

View File

@ -31,17 +31,27 @@
"@apollo/link-error": "2.0.0-beta.3",
"@apollo/link-retry": "2.0.0-beta.3",
"@lowdefy/block-tools": "1.0.1-experimental.1",
"@lowdefy/engine": "0.0.0-experimental.0",
"@lowdefy/get": "1.0.1",
"@lowdefy/helpers": "1.0.0",
"@lowdefy/layout": "1.0.0",
"@lowdefy/set": "1.0.1",
"graphql": "15.3.0",
"graphql-type-json": "0.3.2",
"react": "17.0.0-rc.2",
"react-dom": "17.0.0-rc.2"
"react-dom": "17.0.0-rc.2",
"react-helmet": "6.1.0",
"react-router-dom": "5.2.0"
},
"devDependencies": {
"@babel/core": "7.11.6",
"@babel/preset-react": "7.10.4",
"babel-loader": "8.1.0",
"bundle-loader": "0.5.6",
"css-loader": "4.3.0",
"html-webpack-plugin": "4.5.0",
"serve": "11.3.2",
"style-loader": "2.0.0",
"webpack": "5.0.0-rc.4",
"webpack-cli": "3.3.12",
"webpack-dev-server": "3.11.0"

View File

@ -1,36 +0,0 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { ApolloProvider } from '@apollo/client';
import { initEmotion } from '@lowdefy/block-tools';
import useGqlClient from './utils/useGqlClient';
import Page from './Page';
// const RemoteButton = React.lazy(() => import('block/Button'));
const Engine = () => {
initEmotion();
const client = useGqlClient();
return (
<ApolloProvider client={client}>
<h2>App</h2>
<Page />
</ApolloProvider>
);
};
export default Engine;

View File

@ -20,13 +20,17 @@ import { useQuery } from '@apollo/client';
import AutoBlock from './AutoBlock';
const GET_PAGE = gql`
query page {
page
query page($pageId: ID!) {
page(pageId: $pageId)
}
`;
const Page = () => {
const { loading, error, data } = useQuery(GET_PAGE);
const { loading, error, data } = useQuery(GET_PAGE, {
variables: {
pageId: 'page1',
},
});
if (loading) return <h2>Loading</h2>;
if (error) {
console.log(error);

View File

@ -0,0 +1,144 @@
/*
Copyright 2020 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { BrowserRouter, Route, Redirect, Switch, useLocation } from 'react-router-dom';
import { ApolloProvider, useQuery, gql } from '@apollo/client';
import { ErrorBoundary } from '@lowdefy/block-tools';
import get from '@lowdefy/get';
import useGqlClient from './utils/graphql/useGqlClient';
import PageContext from './PageContext';
import { initEmotion } from '@lowdefy/block-tools';
// eslint-disable-next-line no-undef
const windowContext = window;
// eslint-disable-next-line no-undef
const documentContext = document;
const Components = {};
const contexts = {};
const input = {};
initEmotion();
const GET_ROOT = gql`
fragment MenuLinkFragment on MenuLink {
id
type
properties
pageId
url
}
query getRoot {
lowdefyGlobal
menu {
menus {
id
menuId
properties
links {
...MenuLinkFragment
... on MenuGroup {
id
type
properties
links {
... on MenuGroup {
id
type
properties
links {
...MenuLinkFragment
}
}
...MenuLinkFragment
}
}
}
}
homePageId
}
}
`;
const RootContext = ({ children, client }) => {
const { data, loading, error } = useQuery(GET_ROOT);
if (error) return <h1>Error</h1>;
if (loading) return <h1>Loading Root Context</h1>;
return (
<>
{children({
client,
Components,
contexts,
input,
lowdefyGlobal: JSON.parse(JSON.stringify(get(data, 'lowdefyGlobal', { default: {} }))),
menus: get(data, 'menu.menus'),
homePageId: get(data, 'menu.homePageId'),
document: documentContext,
window: windowContext,
})}
</>
);
};
const Home = ({ rootContext }) => {
const { search } = useLocation();
if (rootContext.homePageId) {
return <Redirect to={{ pathname: `/${rootContext.homePageId}`, search }} />;
}
return <Redirect to="/404" />;
};
const Root = () => {
const client = useGqlClient();
return (
<ErrorBoundary>
<ApolloProvider client={client}>
<RootContext client={client}>
{(rootContext) => {
return (
<>
<Switch>
<Route exact path="/">
<Home rootContext={rootContext} />
</Route>
<Route exact path="/:pageId">
<ErrorBoundary>
<PageContext rootContext={rootContext} />
</ErrorBoundary>
</Route>
</Switch>
</>
);
}}
</RootContext>
</ApolloProvider>
</ErrorBoundary>
);
};
const Engine = () => (
<BrowserRouter>
<Root />
</BrowserRouter>
);
export default Engine;

View File

@ -16,5 +16,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Engine from './Engine';
ReactDOM.render(<Engine />, document.getElementById('root'));
import './index.css';
import Renderer from './Renderer';
ReactDOM.render(<Renderer />, document.getElementById('root'));

View File

@ -0,0 +1,16 @@
body {
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
@media print {
.hide-on-print {
display: none !important;
}
}

View File

@ -0,0 +1,40 @@
import { gql } from '@apollo/client';
import GraphQLJSON from 'graphql-type-json';
const typeDefs = gql`
extend type Query {
block(id: String!): BlockClass
pageState(id: String!): PageState
}
type BlockClass {
id: String!
t: Int
}
`;
const GET_BLOCK = gql`
fragment getBlockCache on BlockClass @client {
id
t
}
`;
const resolvers = {
JSON: GraphQLJSON,
Query: {
block: (parent, args, { cache }) => {
try {
return cache.readFragment({
id: args.id,
fragment: GET_BLOCK,
});
} catch (e) {
console.log(e);
}
return null;
},
},
};
export { typeDefs, resolvers };

View File

@ -20,12 +20,19 @@ import { ApolloClient } from '@apollo/client/core';
import { InMemoryCache } from '@apollo/client/cache';
import { onError } from '@apollo/link-error';
import { RetryLink } from '@apollo/link-retry';
import { typeDefs, resolvers } from './schema';
const cache = new InMemoryCache();
const cache = new InMemoryCache({
possibleTypes: {
MenuItem: ['MenuLink', 'MenuGroup'],
},
});
const retryLink = new RetryLink();
const httpLink = new HttpLink({
uri: '/graphql',
});
// TODO: Handle errors
const errorHandler = ({ graphQLErrors, networkError }) => {
console.log('graphQLErrors', graphQLErrors);
console.log('networkError', networkError);
@ -37,6 +44,8 @@ const useGqlClient = () => {
const clt = new ApolloClient({
link: ApolloLink.from([retryLink, onError(errorHandler), httpLink]),
cache,
resolvers,
typeDefs,
});
setClient(clt);
}

View File

@ -11,6 +11,9 @@ module.exports = {
contentBase: path.join(__dirname, 'dist'),
port: 3001,
},
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
@ -28,6 +31,17 @@ module.exports = {
presets: ['@babel/preset-react'],
},
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader', // translates CSS into CommonJS
},
],
},
],
},
plugins: [
@ -36,7 +50,7 @@ module.exports = {
library: { type: 'var', name: 'lowdefy_renderer' },
filename: 'remoteEntry.js',
exposes: {
'./Engine': './src/Engine',
'./Renderer': './src/Renderer',
},
shared: {
...deps,