Improve lazy loading (#12186)
WIP, using preview app to test performance (which was very bad)
This commit is contained in:
@ -3,9 +3,9 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "VITE_DISABLE_TYPESCRIPT_CHECKER=true VITE_DISABLE_ESLINT_CHECKER=true NODE_OPTIONS=--max-old-space-size=4500 npx vite build && sh ./scripts/inject-runtime-env.sh",
|
||||
"build:sourcemaps": "VITE_BUILD_SOURCEMAP=true VITE_DISABLE_TYPESCRIPT_CHECKER=true VITE_DISABLE_ESLINT_CHECKER=true NODE_OPTIONS=--max-old-space-size=7000 npx vite build && sh ./scripts/inject-runtime-env.sh",
|
||||
"start:prod": "NODE_ENV=production npx vite --host",
|
||||
"build": "VITE_DISABLE_TYPESCRIPT_CHECKER=true VITE_DISABLE_ESLINT_CHECKER=true NODE_OPTIONS=--max-old-space-size=8192 npx vite build && sh ./scripts/inject-runtime-env.sh",
|
||||
"build:sourcemaps": "VITE_BUILD_SOURCEMAP=true VITE_DISABLE_TYPESCRIPT_CHECKER=true VITE_DISABLE_ESLINT_CHECKER=true NODE_OPTIONS=--max-old-space-size=8192 npx vite build && sh ./scripts/inject-runtime-env.sh",
|
||||
"start:prod": "NODE_ENV=production npx serve -s build",
|
||||
"tsup": "npx tsup"
|
||||
},
|
||||
"engines": {
|
||||
@ -82,6 +82,7 @@
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"eslint-plugin-unicorn": "^51.0.1",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"optionator": "^0.9.1"
|
||||
"optionator": "^0.9.1",
|
||||
"rollup-plugin-visualizer": "^5.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
PDFExporter,
|
||||
pdfDefaultSchemaMappings,
|
||||
} from '@blocknote/xl-pdf-exporter';
|
||||
import * as ReactPDF from '@react-pdf/renderer';
|
||||
import { pdf } from '@react-pdf/renderer';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
export const exportBlockNoteEditorToPdf = async (
|
||||
@ -14,6 +14,6 @@ export const exportBlockNoteEditorToPdf = async (
|
||||
|
||||
const pdfDocument = await exporter.toReactPDFDocument(editor.document);
|
||||
|
||||
const blob = await ReactPDF.pdf(pdfDocument).toBlob();
|
||||
const blob = await pdf(pdfDocument).toBlob();
|
||||
saveAs(blob, `${filename}.pdf`);
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { ActivityRow } from '@/activities/components/ActivityRow';
|
||||
import { AttachmentDropdown } from '@/activities/files/components/AttachmentDropdown';
|
||||
import { AttachmentIcon } from '@/activities/files/components/AttachmentIcon';
|
||||
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/components/DocumentViewer';
|
||||
import { Attachment } from '@/activities/files/types/Attachment';
|
||||
import { downloadFile } from '@/activities/files/utils/downloadFile';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -17,10 +16,11 @@ import styled from '@emotion/styled';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { formatToHumanReadableDate } from '~/utils/date-utils';
|
||||
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
|
||||
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
|
||||
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
import { isModifiedEvent } from 'twenty-ui/utilities';
|
||||
import { formatToHumanReadableDate } from '~/utils/date-utils';
|
||||
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
|
||||
|
||||
const StyledLeftContent = styled.div`
|
||||
align-items: center;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
|
||||
import { fetchCsvPreview } from '@/activities/files/utils/fetchCsvPreview';
|
||||
import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
|
||||
import '@cyntler/react-doc-viewer/dist/index.css';
|
||||
@ -41,29 +42,6 @@ type DocumentViewerProps = {
|
||||
documentUrl: string;
|
||||
};
|
||||
|
||||
export const PREVIEWABLE_EXTENSIONS = [
|
||||
'bmp',
|
||||
'csv',
|
||||
'odt',
|
||||
'doc',
|
||||
'docx',
|
||||
'gif',
|
||||
'htm',
|
||||
'html',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'pdf',
|
||||
'png',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'tiff',
|
||||
'txt',
|
||||
'xls',
|
||||
'xlsx',
|
||||
'mp4',
|
||||
'webp',
|
||||
];
|
||||
|
||||
const MIME_TYPE_MAPPING: Record<
|
||||
(typeof PREVIEWABLE_EXTENSIONS)[number],
|
||||
string
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
export const PREVIEWABLE_EXTENSIONS = [
|
||||
'bmp',
|
||||
'csv',
|
||||
'odt',
|
||||
'doc',
|
||||
'docx',
|
||||
'gif',
|
||||
'htm',
|
||||
'html',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'pdf',
|
||||
'png',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'tiff',
|
||||
'txt',
|
||||
'xls',
|
||||
'xlsx',
|
||||
'mp4',
|
||||
'webp',
|
||||
];
|
||||
@ -1,5 +1,5 @@
|
||||
import { AppErrorBoundaryEffect } from '@/error-handler/components/internal/AppErrorBoundaryEffect';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { captureException } from '@sentry/react';
|
||||
import { ErrorInfo, ReactNode } from 'react';
|
||||
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
|
||||
|
||||
@ -15,7 +15,7 @@ export const AppErrorBoundary = ({
|
||||
resetOnLocationChange = true,
|
||||
}: AppErrorBoundaryProps) => {
|
||||
const handleError = (error: Error, info: ErrorInfo) => {
|
||||
Sentry.captureException(error, (scope) => {
|
||||
captureException(error, (scope) => {
|
||||
scope.setExtras({ info });
|
||||
return scope;
|
||||
});
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import {
|
||||
browserTracingIntegration,
|
||||
init,
|
||||
replayIntegration,
|
||||
setUser,
|
||||
} from '@sentry/react';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@ -7,8 +12,8 @@ import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { sentryConfigState } from '@/client-config/states/sentryConfigState';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
|
||||
export const SentryInitEffect = () => {
|
||||
const sentryConfig = useRecoilValue(sentryConfigState);
|
||||
@ -21,14 +26,11 @@ export const SentryInitEffect = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isNonEmptyString(sentryConfig?.dsn) && !isSentryInitialized) {
|
||||
Sentry.init({
|
||||
init({
|
||||
environment: sentryConfig?.environment ?? undefined,
|
||||
release: sentryConfig?.release ?? undefined,
|
||||
dsn: sentryConfig?.dsn,
|
||||
integrations: [
|
||||
Sentry.browserTracingIntegration({}),
|
||||
Sentry.replayIntegration(),
|
||||
],
|
||||
integrations: [browserTracingIntegration({}), replayIntegration()],
|
||||
tracePropagationTargets: ['localhost:3001', REACT_APP_SERVER_BASE_URL],
|
||||
tracesSampleRate: 1.0,
|
||||
replaysSessionSampleRate: 0.1,
|
||||
@ -39,14 +41,14 @@ export const SentryInitEffect = () => {
|
||||
}
|
||||
|
||||
if (isDefined(currentUser)) {
|
||||
Sentry.setUser({
|
||||
setUser({
|
||||
email: currentUser?.email,
|
||||
id: currentUser?.id,
|
||||
workspaceId: currentWorkspace?.id,
|
||||
workspaceMemberId: currentWorkspaceMember?.id,
|
||||
});
|
||||
} else {
|
||||
Sentry.setUser(null);
|
||||
setUser(null);
|
||||
}
|
||||
}, [
|
||||
sentryConfig,
|
||||
|
||||
@ -3,7 +3,7 @@ import { ZodHelperLiteral } from '@/object-record/record-field/types/ZodHelperLi
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { ConnectedAccountProvider } from 'twenty-shared/types';
|
||||
import { ThemeColor } from 'twenty-ui/theme';
|
||||
import * as z from 'zod';
|
||||
import { z } from 'zod';
|
||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||
import { CurrencyCode } from './CurrencyCode';
|
||||
|
||||
|
||||
@ -31,29 +31,24 @@ const StyledBaseContainer = styled.div<{
|
||||
}
|
||||
|
||||
&:hover {
|
||||
${({
|
||||
isReadOnly,
|
||||
fontColorMedium,
|
||||
backgroundColorSecondary,
|
||||
fontColorSecondary,
|
||||
}) =>
|
||||
isReadOnly
|
||||
? `
|
||||
outline: 1px solid ${fontColorMedium};
|
||||
border-radius: 0px;
|
||||
background-color: ${backgroundColorSecondary};
|
||||
|
||||
color: ${fontColorSecondary};
|
||||
|
||||
svg {
|
||||
color: ${fontColorSecondary};
|
||||
}
|
||||
|
||||
img {
|
||||
opacity: 0.64;
|
||||
}
|
||||
`
|
||||
: ''}
|
||||
${(props) => {
|
||||
if (!props.isReadOnly) return '';
|
||||
|
||||
return `
|
||||
outline: 1px solid ${props.fontColorMedium};
|
||||
border-radius: 0px;
|
||||
background-color: ${props.backgroundColorSecondary};
|
||||
color: ${props.fontColorSecondary};
|
||||
|
||||
svg {
|
||||
color: ${props.fontColorSecondary};
|
||||
}
|
||||
|
||||
img {
|
||||
opacity: 0.64;
|
||||
}
|
||||
`;
|
||||
}}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Point } from '@nivo/line';
|
||||
import type { Point } from '@nivo/line';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
const StyledTooltipContainer = styled.div`
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useState } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import * as XLSX from 'xlsx-ugnis';
|
||||
import { read, WorkBook } from 'xlsx-ugnis';
|
||||
|
||||
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
|
||||
import { readFileAsync } from '@/spreadsheet-import/utils/readFilesAsync';
|
||||
@ -84,7 +84,7 @@ const StyledText = styled.span`
|
||||
`;
|
||||
|
||||
type DropZoneProps = {
|
||||
onContinue: (data: XLSX.WorkBook, file: File) => void;
|
||||
onContinue: (data: WorkBook, file: File) => void;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
@ -119,7 +119,7 @@ export const DropZone = ({ onContinue, isLoading }: DropZoneProps) => {
|
||||
onDropAccepted: async ([file]) => {
|
||||
setLoading(true);
|
||||
const arrayBuffer = await readFileAsync(file);
|
||||
const workbook = XLSX.read(arrayBuffer, {
|
||||
const workbook = read(arrayBuffer, {
|
||||
cellDates: true,
|
||||
codepage: 65001, // UTF-8 codepage
|
||||
dateNF: dateFormat,
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import * as XLSX from 'xlsx-ugnis';
|
||||
import { utils } from 'xlsx-ugnis';
|
||||
|
||||
import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
|
||||
|
||||
describe('mapWorkbook', () => {
|
||||
it('should map the workbook to a 2D array of strings', () => {
|
||||
const inputWorkbook = XLSX.utils.book_new();
|
||||
const inputWorkbook = utils.book_new();
|
||||
const inputSheetData = [
|
||||
['Name', 'Age'],
|
||||
['John', '30'],
|
||||
@ -12,8 +12,8 @@ describe('mapWorkbook', () => {
|
||||
];
|
||||
const expectedOutput = inputSheetData;
|
||||
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(inputSheetData);
|
||||
XLSX.utils.book_append_sheet(inputWorkbook, worksheet, 'Sheet1');
|
||||
const worksheet = utils.aoa_to_sheet(inputSheetData);
|
||||
utils.book_append_sheet(inputWorkbook, worksheet, 'Sheet1');
|
||||
|
||||
const result = mapWorkbook(inputWorkbook);
|
||||
|
||||
@ -21,7 +21,7 @@ describe('mapWorkbook', () => {
|
||||
});
|
||||
|
||||
it('should map the specified sheet of the workbook to a 2D array of strings', () => {
|
||||
const inputWorkbook = XLSX.utils.book_new();
|
||||
const inputWorkbook = utils.book_new();
|
||||
const inputSheet1Data = [
|
||||
['Name', 'Age'],
|
||||
['John', '30'],
|
||||
@ -34,10 +34,10 @@ describe('mapWorkbook', () => {
|
||||
];
|
||||
const expectedOutput = inputSheet2Data;
|
||||
|
||||
const worksheet1 = XLSX.utils.aoa_to_sheet(inputSheet1Data);
|
||||
const worksheet2 = XLSX.utils.aoa_to_sheet(inputSheet2Data);
|
||||
XLSX.utils.book_append_sheet(inputWorkbook, worksheet1, 'Sheet1');
|
||||
XLSX.utils.book_append_sheet(inputWorkbook, worksheet2, 'Sheet2');
|
||||
const worksheet1 = utils.aoa_to_sheet(inputSheet1Data);
|
||||
const worksheet2 = utils.aoa_to_sheet(inputSheet2Data);
|
||||
utils.book_append_sheet(inputWorkbook, worksheet1, 'Sheet1');
|
||||
utils.book_append_sheet(inputWorkbook, worksheet2, 'Sheet2');
|
||||
|
||||
const result = mapWorkbook(inputWorkbook, 'Sheet2');
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import * as XLSX from 'xlsx-ugnis';
|
||||
import { utils, WorkBook } from 'xlsx-ugnis';
|
||||
|
||||
export const mapWorkbook = (workbook: XLSX.WorkBook, sheetName?: string) => {
|
||||
export const mapWorkbook = (workbook: WorkBook, sheetName?: string) => {
|
||||
const worksheet = workbook.Sheets[sheetName || workbook.SheetNames[0]];
|
||||
const data = XLSX.utils.sheet_to_json(worksheet, {
|
||||
const data = utils.sheet_to_json(worksheet, {
|
||||
header: 1,
|
||||
blankrows: false,
|
||||
raw: false,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import { hasFlag } from 'country-flag-icons';
|
||||
import * as Flags from 'country-flag-icons/react/3x2';
|
||||
import { getCountries, getCountryCallingCode } from 'libphonenumber-js';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { Country } from '@/ui/input/components/internal/types/Country';
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import * as Flags from 'country-flag-icons/react/3x2';
|
||||
import { FlagComponent } from 'country-flag-icons/react/3x2';
|
||||
import { CountryCallingCode, CountryCode } from 'libphonenumber-js';
|
||||
|
||||
export type Country = {
|
||||
countryCode: CountryCode;
|
||||
countryName: string;
|
||||
callingCode: CountryCallingCode;
|
||||
Flag: Flags.FlagComponent;
|
||||
Flag: FlagComponent;
|
||||
};
|
||||
|
||||
@ -5,8 +5,7 @@ import { LayoutCard } from '@/ui/layout/tab/types/LayoutCard';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { IconComponent } from 'twenty-ui/display';
|
||||
import { Tab } from './Tab';
|
||||
|
||||
|
||||
@ -5,10 +5,10 @@ import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/Wo
|
||||
import { fn } from '@storybook/test';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { ComponentProps } from 'react';
|
||||
import { CatalogDecorator, CatalogStory } from 'twenty-ui/testing';
|
||||
import { ReactflowDecorator } from '~/testing/decorators/ReactflowDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { WorkflowDiagramStepNodeEditableContent } from '../WorkflowDiagramStepNodeEditableContent';
|
||||
import { CatalogDecorator, CatalogStory } from 'twenty-ui/testing';
|
||||
|
||||
type ComponentState = 'default' | 'hover' | 'selected';
|
||||
|
||||
|
||||
@ -8,9 +8,9 @@ import {
|
||||
StepOutputSchema,
|
||||
} from '@/workflow/workflow-variables/types/StepOutputSchema';
|
||||
import { filterOutputSchema } from '@/workflow/workflow-variables/utils/filterOutputSchema';
|
||||
import { isEmptyObject } from '@tiptap/core';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isEmptyObject } from '~/utils/isEmptyObject';
|
||||
|
||||
export const useAvailableVariablesInWorkflowStep = ({
|
||||
objectNameSingularToSelect,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { JSONContent } from '@tiptap/react';
|
||||
import type { JSONContent } from '@tiptap/react';
|
||||
import { parseEditorContent } from '../parseEditorContent';
|
||||
|
||||
describe('parseEditorContent', () => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { JSONContent } from '@tiptap/react';
|
||||
import type { JSONContent } from '@tiptap/react';
|
||||
|
||||
export const CAPTURE_VARIABLE_TAG_REGEX = /({{[^{}]+}})/;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { JSONContent } from '@tiptap/react';
|
||||
import type { JSONContent } from '@tiptap/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const parseEditorContent = (json: JSONContent): string => {
|
||||
|
||||
@ -5,11 +5,11 @@ import react from '@vitejs/plugin-react-swc';
|
||||
import wyw from '@wyw-in-js/vite';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { defineConfig, loadEnv, searchForWorkspaceRoot } from 'vite';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { defineConfig, loadEnv, PluginOption, searchForWorkspaceRoot } from 'vite';
|
||||
import checker from 'vite-plugin-checker';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
type Checkers = Parameters<typeof checker>[0];
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
@ -145,6 +145,12 @@ export default defineConfig(({ command, mode }) => {
|
||||
presets: ['@babel/preset-typescript', '@babel/preset-react'],
|
||||
},
|
||||
}),
|
||||
visualizer({
|
||||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
filename: 'dist/stats.html',
|
||||
}) as PluginOption, // https://github.com/btd/rollup-plugin-visualizer/issues/162#issuecomment-1538265997,
|
||||
],
|
||||
|
||||
optimizeDeps: {
|
||||
@ -156,7 +162,7 @@ export default defineConfig(({ command, mode }) => {
|
||||
},
|
||||
|
||||
build: {
|
||||
minify: false,
|
||||
minify: 'esbuild',
|
||||
outDir: 'build',
|
||||
sourcemap: VITE_BUILD_SOURCEMAP === 'true',
|
||||
rollupOptions: {
|
||||
|
||||
@ -98,11 +98,18 @@ export type CatalogOptions = {
|
||||
|
||||
export const CatalogDecorator: Decorator = (Story, context) => {
|
||||
const {
|
||||
catalog: { dimensions, options },
|
||||
} = context.parameters;
|
||||
catalog: { dimensions = [], options = {} } = {
|
||||
dimensions: [],
|
||||
options: {},
|
||||
},
|
||||
} = context.parameters || {};
|
||||
|
||||
if (!dimensions || !Array.isArray(dimensions)) {
|
||||
return <Story />;
|
||||
}
|
||||
|
||||
const [
|
||||
dimension1,
|
||||
dimension1 = emptyDimension,
|
||||
dimension2 = emptyDimension,
|
||||
dimension3 = emptyDimension,
|
||||
dimension4 = emptyDimension,
|
||||
|
||||
@ -100,7 +100,7 @@ export default defineConfig(({ command }) => {
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
minify: false,
|
||||
minify: 'esbuild',
|
||||
sourcemap: false,
|
||||
outDir: './dist',
|
||||
reportCompressedSize: true,
|
||||
|
||||
Reference in New Issue
Block a user