Skip to content

Commit

Permalink
Cherrypick some changes from direction 13 branch (#1599)
Browse files Browse the repository at this point in the history
  • Loading branch information
Janpot authored Jan 26, 2023
1 parent 8372f43 commit 19a5356
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 110 deletions.
14 changes: 7 additions & 7 deletions packages/toolpad-app/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,13 @@ export default class MyDocument extends Document<ToolpadDocumentProps> {
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${config.gaId}', {
page_path: window.location.pathname,
});
`,
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${config.gaId}', {
page_path: window.location.pathname,
});
`,
}}
/>
</Head>
Expand Down
15 changes: 13 additions & 2 deletions packages/toolpad-app/pages/app-canvas/[[...path]].tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import type { NextPage } from 'next';
import { asArray } from '@mui/toolpad-core/utils/collections';
import type { GetServerSideProps, NextPage } from 'next';
import * as React from 'react';
import AppCanvas, { AppCanvasProps } from '../../src/canvas';

const App: NextPage<AppCanvasProps> = () => <AppCanvas basename="/app-canvas" />;
export const getServerSideProps: GetServerSideProps<AppCanvasProps> = async ({ query }) => {
const [appId] = asArray(query.path);
return {
props: {
basename: `/app-canvas/${appId}`,
},
};
};

const App: NextPage<AppCanvasProps> = (props) => <AppCanvas {...props} />;

export default App;
17 changes: 17 additions & 0 deletions packages/toolpad-app/src/appDom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1093,3 +1093,20 @@ export function deref(nodeRef: Maybe<NodeReference>): NodeId | null {
}
return null;
}

export function createDefaultDom(): AppDom {
let dom = createDom();
const appNode = getApp(dom);

// Create default page
const newPageNode = createNode(dom, 'page', {
name: 'Page 1',
attributes: {
title: createConst('Page 1'),
},
});

dom = addNode(dom, newPageNode, appNode, 'pages');

return dom;
}
10 changes: 7 additions & 3 deletions packages/toolpad-app/src/canvas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import invariant from 'invariant';
import { throttle } from 'lodash-es';
import { CanvasEventsContext } from '@mui/toolpad-core/runtime';
import { ToolpadComponent } from '@mui/toolpad-core';
import ToolpadApp from '../runtime';
import { NodeHashes, RuntimeState } from '../types';
import getPageViewState from './getPageViewState';
Expand All @@ -22,11 +23,13 @@ const handleScreenUpdate = throttle(
);

export interface AppCanvasProps {
initialState?: AppCanvasState | null;
basename: string;
catalog?: Record<string, ToolpadComponent>;
}

export default function AppCanvas({ basename }: AppCanvasProps) {
const [state, setState] = React.useState<AppCanvasState | null>(null);
export default function AppCanvas({ catalog, basename, initialState = null }: AppCanvasProps) {
const [state, setState] = React.useState<AppCanvasState | null>(initialState);

const appRootRef = React.useRef<HTMLDivElement>();
const appRootCleanupRef = React.useRef<() => void>();
Expand Down Expand Up @@ -130,9 +133,10 @@ export default function AppCanvas({ basename }: AppCanvasProps) {
<CanvasEventsContext.Provider value={bridge.canvasEvents}>
<ToolpadApp
rootRef={onAppRoot}
catalog={catalog}
hidePreviewBanner
version="preview"
basename={`${basename}/${state.appId}`}
basename={basename}
state={state}
/>
</CanvasEventsContext.Provider>
Expand Down
2 changes: 2 additions & 0 deletions packages/toolpad-app/src/createRuntimeState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ function compileModules(dom: appDom.AppDom): Record<string, CompiledModule> {
const result: Record<string, CompiledModule> = {};
const root = appDom.getApp(dom);
const { codeComponents = [], pages = [] } = appDom.getChildNodes(dom, root);

for (const node of codeComponents) {
const src = node.attributes.code.value;
const name = `codeComponents/${node.id}`;
result[name] = compileModule(src, name);
}

for (const node of pages) {
const src = node.attributes.module?.value;
if (src) {
Expand Down
13 changes: 11 additions & 2 deletions packages/toolpad-app/src/runtime/ComponentsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,23 @@ function isToolpadComponent(maybeComponent: unknown): maybeComponent is ToolpadC
}

interface ComponentsContextProps {
catalog?: Record<string, ToolpadComponent>;
dom: appDom.AppDom;
children?: React.ReactNode;
}

export default function ComponentsContext({ dom, children }: ComponentsContextProps) {
export default function ComponentsContext({
catalog: componentsCatalog,
dom,
children,
}: ComponentsContextProps) {
const modules = useAppModules();

const components = React.useMemo(() => {
if (componentsCatalog) {
return componentsCatalog;
}

const catalog = getToolpadComponents(dom);
const result: Record<string, ToolpadComponent<any>> = {};

Expand Down Expand Up @@ -77,7 +86,7 @@ export default function ComponentsContext({ dom, children }: ComponentsContextPr
}

return result;
}, [dom, modules]);
}, [componentsCatalog, dom, modules]);

return <ComponentsContextProvider value={components}>{children}</ComponentsContextProvider>;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/toolpad-app/src/runtime/ToolpadApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ const queryClient = new QueryClient({

export interface ToolpadAppProps {
rootRef?: React.Ref<HTMLDivElement>;
catalog?: Record<string, ToolpadComponent>;
hidePreviewBanner?: boolean;
basename: string;
version: VersionOrPreview;
Expand All @@ -938,6 +939,7 @@ export interface ToolpadAppProps {

export default function ToolpadApp({
rootRef,
catalog,
basename,
version,
hidePreviewBanner,
Expand Down Expand Up @@ -969,7 +971,7 @@ export default function ToolpadApp({
<ResetNodeErrorsKeyProvider value={resetNodeErrorsKey}>
<React.Suspense fallback={<AppLoading />}>
<AppModulesProvider modules={state.modules}>
<ComponentsContext dom={dom}>
<ComponentsContext catalog={catalog} dom={dom}>
<AppContextProvider value={appContext}>
<QueryClientProvider client={queryClient}>
<BrowserRouter basename={basename}>
Expand Down
6 changes: 1 addition & 5 deletions packages/toolpad-app/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type BasicAuthConfig =
};

export type ServerConfig = {
databaseUrl: string;
databaseUrl?: string;
googleSheetsClientId?: string;
googleSheetsClientSecret?: string;
encryptionKeys: string[];
Expand All @@ -28,10 +28,6 @@ function readConfig(): ServerConfig & typeof sharedConfig {
throw new Error(`Serverside config can't be loaded on the client side`);
}

if (!process.env.TOOLPAD_DATABASE_URL) {
throw new Error(`App started without config env variable TOOLPAD_DATABASE_URL`);
}

// Whitespace separated, do not use spaces in your keys
const encryptionKeys: string[] =
process.env.TOOLPAD_ENCRYPTION_KEYS?.split(/\s+/).filter(Boolean) ?? [];
Expand Down
52 changes: 32 additions & 20 deletions packages/toolpad-app/src/server/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ const SELECT_APP_META = excludeFields(prisma.Prisma.AppScalarFieldEnum, ['dom'])

export type AppMeta = Omit<prisma.App, 'dom'>;

function getPrismaClient(): prisma.PrismaClient {
function createPrismaClient(): prisma.PrismaClient {
if (!process.env.TOOLPAD_DATABASE_URL) {
throw new Error(`App started without config env variable TOOLPAD_DATABASE_URL`);
}

if (process.env.NODE_ENV === 'production') {
return new prisma.PrismaClient();
}
Expand All @@ -37,7 +41,13 @@ function getPrismaClient(): prisma.PrismaClient {
return (globalThis as any).prisma;
}

const prismaClient = getPrismaClient();
let clientInstance: prisma.PrismaClient | undefined;
function getPrismaClient(): prisma.PrismaClient {
if (!clientInstance) {
clientInstance = createPrismaClient();
}
return clientInstance;
}

function deserializeValue(dbValue: string, type: prisma.DomNodeAttributeType): unknown {
const serialized = type === 'secret' ? decryptSecret(dbValue) : dbValue;
Expand Down Expand Up @@ -79,6 +89,7 @@ function decryptSecrets(dom: appDom.AppDom): appDom.AppDom {
}

export async function saveDom(appId: string, app: appDom.AppDom): Promise<void> {
const prismaClient = getPrismaClient();
await prismaClient.app.update({
where: {
id: appId,
Expand All @@ -89,6 +100,7 @@ export async function saveDom(appId: string, app: appDom.AppDom): Promise<void>
}

async function loadPreviewDomLegacy(appId: string): Promise<appDom.AppDom> {
const prismaClient = getPrismaClient();
const dbNodes = await prismaClient.domNode.findMany({
where: { appId },
include: { attributes: true },
Expand Down Expand Up @@ -137,6 +149,7 @@ async function loadPreviewDomLegacy(appId: string): Promise<appDom.AppDom> {
}

async function loadPreviewDom(appId: string): Promise<appDom.AppDom> {
const prismaClient = getPrismaClient();
const { dom } = await prismaClient.app.findUniqueOrThrow({
where: { id: appId },
});
Expand All @@ -153,6 +166,7 @@ async function loadPreviewDom(appId: string): Promise<appDom.AppDom> {
}

export async function getApps(): Promise<AppMeta[]> {
const prismaClient = getPrismaClient();
if (config.isDemo) {
return [];
}
Expand All @@ -166,33 +180,18 @@ export async function getApps(): Promise<AppMeta[]> {
}

export async function getActiveDeployments() {
const prismaClient = getPrismaClient();
return prismaClient.deployment.findMany({
distinct: ['appId'],
orderBy: { createdAt: 'desc' },
});
}

export async function getApp(id: string): Promise<AppMeta | null> {
const prismaClient = getPrismaClient();
return prismaClient.app.findUnique({ where: { id }, select: SELECT_APP_META });
}

function createDefaultDom(): appDom.AppDom {
let dom = appDom.createDom();
const appNode = appDom.getApp(dom);

// Create default page
const newPageNode = appDom.createNode(dom, 'page', {
name: 'Page 1',
attributes: {
title: appDom.createConst('Page 1'),
},
});

dom = appDom.addNode(dom, newPageNode, appNode, 'pages');

return dom;
}

export type CreateAppOptions = {
from?:
| {
Expand All @@ -210,6 +209,7 @@ export type CreateAppOptions = {
};

export async function createApp(name: string, opts: CreateAppOptions = {}): Promise<prisma.App> {
const prismaClient = getPrismaClient();
const { from } = opts;

if (config.recaptchaV3SecretKey) {
Expand Down Expand Up @@ -259,7 +259,7 @@ export async function createApp(name: string, opts: CreateAppOptions = {}): Prom
}

if (!dom) {
dom = createDefaultDom();
dom = appDom.createDefaultDom();
}

await saveDom(app.id, dom);
Expand All @@ -274,6 +274,7 @@ interface AppUpdates {
}

export async function updateApp(appId: string, updates: AppUpdates): Promise<void> {
const prismaClient = getPrismaClient();
await prismaClient.app.update({
where: {
id: appId,
Expand All @@ -287,6 +288,7 @@ export async function updateApp(appId: string, updates: AppUpdates): Promise<voi
}

export async function deleteApp(id: string): Promise<void> {
const prismaClient = getPrismaClient();
await prismaClient.app.delete({
where: { id },
select: {
Expand All @@ -303,13 +305,15 @@ export interface CreateReleaseParams {
export type ReleaseMeta = Pick<prisma.Release, keyof typeof SELECT_RELEASE_META>;

async function findLastReleaseInternal(appId: string) {
const prismaClient = getPrismaClient();
return prismaClient.release.findFirst({
where: { appId },
orderBy: { version: 'desc' },
});
}

export async function findLastRelease(appId: string): Promise<ReleaseMeta | null> {
const prismaClient = getPrismaClient();
return prismaClient.release.findFirst({
where: { appId },
orderBy: { version: 'desc' },
Expand All @@ -321,6 +325,7 @@ export async function createRelease(
appId: string,
{ description }: CreateReleaseParams,
): Promise<ReleaseMeta> {
const prismaClient = getPrismaClient();
const currentDom = await loadPreviewDom(appId);
const snapshot = Buffer.from(JSON.stringify(currentDom), 'utf-8');

Expand All @@ -341,6 +346,7 @@ export async function createRelease(
}

export async function getReleases(appId: string): Promise<ReleaseMeta[]> {
const prismaClient = getPrismaClient();
return prismaClient.release.findMany({
where: { appId },
select: SELECT_RELEASE_META,
Expand All @@ -351,6 +357,7 @@ export async function getReleases(appId: string): Promise<ReleaseMeta[]> {
}

export async function getRelease(appId: string, version: number): Promise<ReleaseMeta | null> {
const prismaClient = getPrismaClient();
return prismaClient.release.findUnique({
where: { release_app_constraint: { appId, version } },
select: SELECT_RELEASE_META,
Expand All @@ -362,6 +369,7 @@ export type Deployment = prisma.Deployment & {
};

export function getDeployments(appId: string): Promise<Deployment[]> {
const prismaClient = getPrismaClient();
return prismaClient.deployment.findMany({
where: { appId },
orderBy: { createdAt: 'desc' },
Expand All @@ -374,6 +382,7 @@ export function getDeployments(appId: string): Promise<Deployment[]> {
}

export async function createDeployment(appId: string, version: number): Promise<Deployment> {
const prismaClient = getPrismaClient();
return prismaClient.deployment.create({
data: {
app: {
Expand Down Expand Up @@ -401,6 +410,7 @@ export async function deploy(
}

export async function findActiveDeployment(appId: string): Promise<Deployment | null> {
const prismaClient = getPrismaClient();
return prismaClient.deployment.findFirst({
where: { appId },
orderBy: { createdAt: 'desc' },
Expand All @@ -417,6 +427,7 @@ function parseSnapshot(snapshot: Buffer): appDom.AppDom {
}

async function loadReleaseDom(appId: string, version: number): Promise<appDom.AppDom> {
const prismaClient = getPrismaClient();
const release = await prismaClient.release.findUnique({
where: { release_app_constraint: { appId, version } },
});
Expand Down Expand Up @@ -548,6 +559,7 @@ export async function loadRuntimeState(
}

export async function duplicateApp(id: string, name: string): Promise<AppMeta> {
const prismaClient = getPrismaClient();
const dom = await loadPreviewDom(id);
const appFromDom: CreateAppOptions = {
from: {
Expand Down
Loading

0 comments on commit 19a5356

Please sign in to comment.