Skip to content

Commit

Permalink
Move more APIs to utils package (#2352)
Browse files Browse the repository at this point in the history
  • Loading branch information
Janpot authored Jul 23, 2023
1 parent 3d8e95f commit 3907e2b
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 125 deletions.
3 changes: 2 additions & 1 deletion packages/toolpad-app/cli/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import { createServer as createViteServer } from 'vite';
import * as fs from 'fs/promises';
import serializeJavascript from 'serialize-javascript';
import { WebSocket, WebSocketServer } from 'ws';
import { listen } from '@mui/toolpad-utils/http';
import { asyncHandler } from '../src/utils/express';
import { createProdHandler } from '../src/server/toolpadAppServer';
import { getUserProjectRoot } from '../src/server/localMode';
import { asyncHandler, listen } from '../src/utils/http';
import { getProject } from '../src/server/liveProject';
import { Command as AppDevServerCommand, Event as AppDevServerEvent } from './appServer';
import { createRpcHandler, rpcServer } from '../src/server/rpc';
Expand Down
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/server/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import serverDataSources from '../toolpadDataSources/server';
import * as appDom from '../appDom';
import applyTransform from '../toolpadDataSources/applyTransform';
import { loadDom, saveDom } from './liveProject';
import { asyncHandler } from '../utils/http';
import { asyncHandler } from '../utils/express';

export async function getConnectionParams<P = unknown>(
connectionId: string | null,
Expand Down
4 changes: 2 additions & 2 deletions packages/toolpad-app/src/server/har.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { streamToString } from '../utils/streams';

describe('har', () => {
test('headers in array form', async () => {
const { port, stopServer } = await listen(async (req, res) => {
const { port, close } = await listen(async (req, res) => {
res.write(
JSON.stringify({
body: await streamToString(req),
Expand Down Expand Up @@ -40,7 +40,7 @@ describe('har', () => {
}),
);
} finally {
await stopServer();
await close();
}
});
});
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/server/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { execQuery, dataSourceFetchPrivate, dataSourceExecPrivate } from './data
import { getVersionInfo } from './versionInfo';
import { createComponent, deletePage } from './localMode';
import { loadDom, saveDom, applyDomDiff, openCodeEditor } from './liveProject';
import { asyncHandler } from '../utils/http';
import { asyncHandler } from '../utils/express';

export interface Method<P extends any[] = any[], R = any> {
(...params: P): Promise<R>;
Expand Down
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/server/toolpadAppServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import config from '../config';
import { postProcessHtml } from './toolpadAppBuilder';
import { loadDom } from './liveProject';
import { getAppOutputFolder } from './localMode';
import { asyncHandler } from '../utils/http';
import { asyncHandler } from '../utils/express';
import { createDataHandler } from './data';
import { basicAuthUnauthorized, checkBasicAuthHeader } from './basicAuth';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ import {
} from '@mui/toolpad-core';
import { createProvidedContext } from '@mui/toolpad-utils/react';
import { TabContext, TabList } from '@mui/lab';
import useDebounced from '@mui/toolpad-utils/hooks/useDebounced';
import { JsExpressionEditor } from './PageEditor/JsExpressionEditor';
import JsonView from '../../components/JsonView';
import useLatest from '../../utils/useLatest';
import useDebounced from '../../utils/useDebounced';
import { useEvaluateLiveBinding } from './useEvaluateLiveBinding';
import GlobalScopeExplorer from './GlobalScopeExplorer';
import { WithControlledProp, Maybe } from '../../utils/types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { styled } from '@mui/material';
import { NodeId } from '@mui/toolpad-core';
import useDebouncedHandler from '@mui/toolpad-utils/hooks/useDebouncedHandler';
import SplitPane from '../../../components/SplitPane';
import RenderPanel from './RenderPanel';
import ComponentPanel from './ComponentPanel';
Expand All @@ -11,7 +12,6 @@ import ComponentCatalog from './ComponentCatalog';
import NotFoundEditor from '../NotFoundEditor';
import usePageTitle from '../../../utils/usePageTitle';
import useLocalStorageState from '../../../utils/useLocalStorageState';
import useDebouncedHandler from '../../../utils/useDebouncedHandler';
import useUndoRedo from '../../hooks/useUndoRedo';

const classes = {
Expand Down Expand Up @@ -41,7 +41,10 @@ function PageEditorContent({ node }: PageEditorContentProps) {
300,
);

const handleSplitChange = useDebouncedHandler((newSize) => setSplitDefaultSize(newSize), 100);
const handleSplitChange = useDebouncedHandler(
(newSize: number) => setSplitDefaultSize(newSize),
100,
);

return (
<PageEditorProvider key={node.id} nodeId={node.id}>
Expand Down
2 changes: 1 addition & 1 deletion packages/toolpad-app/src/toolpad/AppState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { debounce, DebouncedFunc } from 'lodash-es';

import { useLocation } from 'react-router-dom';
import { mapValues } from '@mui/toolpad-utils/collections';
import useDebouncedHandler from '@mui/toolpad-utils/hooks/useDebouncedHandler';
import * as appDom from '../appDom';
import { omit, update } from '../utils/immutability';
import client from '../api';
import useShortcut from '../utils/useShortcut';
import useDebouncedHandler from '../utils/useDebouncedHandler';
import insecureHash from '../utils/insecureHash';
import useEvent from '../utils/useEvent';
import { NodeHashes } from '../types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { inferColumns, parseColumns } from '@mui/toolpad-components';
import { DataGridPro, GridColDef } from '@mui/x-data-grid-pro';
import { UseQueryResult } from '@tanstack/react-query';
import { getObjectKey } from '@mui/toolpad-utils/objectKey';
import useDebounced from '@mui/toolpad-utils/hooks/useDebounced';
import { ClientDataSource, ConnectionEditorProps, QueryEditorProps } from '../../types';
import {
GoogleSheetsConnectionParams,
Expand All @@ -26,7 +27,6 @@ import {
GoogleSheetsPrivateQuery,
GoogleSheetsResult,
} from './types';
import useDebounced from '../../utils/useDebounced';
import { usePrivateQuery } from '../context';
import QueryInputPanel from '../QueryInputPanel';
import SplitPane from '../../components/SplitPane';
Expand Down
14 changes: 14 additions & 0 deletions packages/toolpad-app/src/utils/express.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as express from 'express';
import { Awaitable } from './types';

export function asyncHandler(
handler: (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => Awaitable<void>,
): express.RequestHandler {
return (req, res, next) => {
Promise.resolve(handler(req, res, next)).catch(next);
};
}
30 changes: 0 additions & 30 deletions packages/toolpad-app/src/utils/http.ts

This file was deleted.

83 changes: 11 additions & 72 deletions packages/toolpad-app/src/utils/useLocalStorageState.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,5 @@
import * as React from 'react';
import { Emitter } from '@mui/toolpad-utils/events';

// storage events only work across windows, we'll use an event emitter to announce within the window
const emitter = new Emitter<Record<string, null>>();
// local cache, needed for getSnapshot
const cache = new Map<string, any>();

function subscribe(key: string, cb: () => void): () => void {
const onKeyChange = () => {
// invalidate local cache
cache.delete(key);
cb();
};
const storageHandler = (event: StorageEvent) => {
if (event.storageArea === window.localStorage && event.key === key) {
onKeyChange();
}
};
window.addEventListener('storage', storageHandler);
emitter.on(key, onKeyChange);
return () => {
window.removeEventListener('storage', storageHandler);
emitter.off(key, onKeyChange);
};
}

function getSnapshot<T = unknown>(key: string): T | undefined {
try {
let value = cache.get(key);
if (!value) {
const item = window.localStorage.getItem(key);
value = item ? JSON.parse(item) : undefined;
cache.set(key, value);
}
return value;
} catch (error) {
console.error(error);
return undefined;
}
}

function setValue<T = unknown>(key: string, value: T) {
try {
if (typeof window !== 'undefined') {
cache.set(key, value);
window.localStorage.setItem(key, JSON.stringify(value));
emitter.emit(key, null);
}
} catch (error) {
console.error(error);
}
}
import useStorageState from '@mui/toolpad-utils/hooks/useStorageState';

/**
* Sync state to local storage so that it persists through a page refresh. Usage is
Expand All @@ -70,26 +19,16 @@ export default function useLocalStorageState<V>(
key: string,
initialValue: V,
): [V, React.Dispatch<React.SetStateAction<V>>] {
const subscribeKey = React.useCallback((cb: () => void) => subscribe(key, cb), [key]);
const getKeySnapshot = React.useCallback(
() => getSnapshot<V>(key) ?? initialValue,
[initialValue, key],
);
const getKeyServerSnapshot = React.useCallback(() => initialValue, [initialValue]);

const storedValue: V = React.useSyncExternalStore(
subscribeKey,
getKeySnapshot,
getKeyServerSnapshot,
);

const setStoredValue = React.useCallback(
(value: React.SetStateAction<V>) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setValue(key, valueToStore);
},
[key, storedValue],
const [input, setInput] = useStorageState('local', key, () => JSON.stringify(initialValue));

const value: V = React.useMemo(() => JSON.parse(input), [input]);
const handleChange: React.Dispatch<React.SetStateAction<V>> = React.useCallback(
(newValue) =>
setInput(
JSON.stringify(typeof newValue === 'function' ? (newValue as Function)(value) : newValue),
),
[setInput, value],
);

return [storedValue, setStoredValue];
return [value, handleChange];
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import * as React from 'react';

interface Handler<P extends any[]> {
interface Handler<P extends unknown[]> {
(...params: P): void;
}

interface DelayedInvocation<P extends any[]> {
interface DelayedInvocation<P extends unknown[]> {
startTime: number;
timeout: NodeJS.Timeout;
params: P;
}

function clear<P extends any[]>(
function clear<P extends unknown[]>(
delayedInvocation: React.MutableRefObject<DelayedInvocation<P> | null>,
) {
if (delayedInvocation.current) {
Expand All @@ -19,7 +19,11 @@ function clear<P extends any[]>(
}
}

function defer<P extends any[]>(fn: React.MutableRefObject<Handler<P>>, params: P, delay: number) {
function defer<P extends unknown[]>(
fn: React.MutableRefObject<Handler<P>>,
params: P,
delay: number,
) {
const timeout = setTimeout(() => {
fn.current(...params);
}, delay);
Expand All @@ -34,7 +38,7 @@ function defer<P extends any[]>(fn: React.MutableRefObject<Handler<P>>, params:
* This implementation adds on the lodash implementation in that it handles updates to the
* delay value.
*/
export default function useDebouncedHandler<P extends any[]>(
export default function useDebouncedHandler<P extends unknown[]>(
fn: Handler<P>,
delay: number,
): Handler<P> {
Expand Down
Loading

0 comments on commit 3907e2b

Please sign in to comment.