Skip to content

Implement suspense #702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: production2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 54 additions & 7 deletions src/dashboard/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import AppData from './AppData.react';
import AppsIndex from './Apps/AppsIndex.react';
import AppsManager from 'lib/AppsManager';
import Browser from './Data/Browser/Browser.react';
import CloudCode from './Data/CloudCode/B4ACloudCode.react';
// import CloudCode from './Data/CloudCode/B4ACloudCode.react';
import AppOverview from './Data/AppOverview/AppOverview.react';
import Config from './Data/Config/Config.react';
import FourOhFour from 'components/FourOhFour/FourOhFour.react';
import GeneralSettings from './Settings/GeneralSettings.react';
import GraphQLConsole from './Data/ApiConsole/GraphQLConsole.react';
// import GraphQLConsole from './Data/ApiConsole/GraphQLConsole.react';
// import HostingSettings from './Settings/HostingSettings.react';
import HubConnections from './Hub/HubConnections.react';
import IndexManager from './IndexManager/IndexManager.react'
Expand All @@ -36,7 +36,7 @@ import { get } from 'lib/AJAX';
import { setBasePath } from 'lib/AJAX';
import ServerSettings from 'dashboard/ServerSettings/ServerSettings.react';
import { Helmet } from 'react-helmet';
import Playground from './Data/Playground/Playground.react';
// import Playground from './Data/Playground/Playground.react';
import axios from 'lib/axios';
// import moment from 'moment';
import B4aConnectPage from './B4aConnectPage/B4aConnectPage.react';
Expand All @@ -49,7 +49,7 @@ import PushDetails from './Push/PushDetails.react';
import PushIndex from './Push/PushIndex.react';
import PushNew from './Push/PushNew.react';
// import PushSettings from './Settings/PushSettings.react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState, Suspense, lazy } from 'react';
import RestConsole from './Data/ApiConsole/RestConsole.react';
// import SchemaOverview from './Data/Browser/SchemaOverview.react';
import SecuritySettings from './Settings/SecuritySettings.react';
Expand All @@ -66,6 +66,10 @@ import back4app2 from '../lib/back4app2';
import { initializeAmplitude } from 'lib/amplitudeEvents';
import { setUser as setSentryUser } from '@sentry/react';

const LazyGraphQLConsole = lazy(() => import('./Data/ApiConsole/GraphQLConsole.react'));
const LazyPlayground = lazy(() => import('./Data/Playground/Playground.react'));
const LazyCloudCode = lazy(() => import('./Data/CloudCode/B4ACloudCode.react'));

const ShowSchemaOverview = false; //In progress features. Change false to true to work on this feature.

// class Empty extends React.Component {
Expand Down Expand Up @@ -143,6 +147,44 @@ const waitForScriptToLoad = async conditionFn => {
throw new Error('Script not loaded yet!');
};

const preloadMap = {
cloudCode: () => import('./Data/CloudCode/B4ACloudCode.react'),
graphqlConsole: () => import('./Data/ApiConsole/GraphQLConsole.react'),
playground: () => import('./Data/Playground/Playground.react'),
};

// Preload all routes with proper error handling and logging
const preloadRoute = async (routeName, preloadFn) => {
try {
await preloadFn();
console.log(`Successfully preloaded route: ${routeName}`);
} catch (err) {
console.error(`Error preloading route ${routeName}:`, err);
}
};

// Preload all routes in parallel
const preloadAllRoutes = () => {
console.log('Preloading routes...');
return Promise.all(
Object.entries(preloadMap).map(([routeName, preloadFn]) =>
preloadRoute(routeName, preloadFn)
)
);
};

const LoadingComponent = () => (
<div className={baseStyles.center} style={{ background: '#0F1C32' }}>
<B4aLoader />
</div>
);

const LazyComponentWrapper = ({ children }) => (
<Suspense fallback={<LoadingComponent />}>
{children}
</Suspense>
);

class Dashboard extends React.Component {
constructor(props) {
super();
Expand All @@ -160,6 +202,11 @@ class Dashboard extends React.Component {
}

componentDidMount() {
// Start preloading routes immediately but don't block on it
preloadAllRoutes().finally(() => {
console.log('Route preloading complete');
});

get('/parse-dashboard-config.json').then(({ apps, newFeaturesInLatestVersion = [], user }) => {
fetchHubUser().then(userDetail => {
user.createdAt = userDetail.createdAt;
Expand Down Expand Up @@ -383,8 +430,8 @@ class Dashboard extends React.Component {
const ApiConsoleRoute = (
<Route element={<ApiConsole />}>
<Route path="rest" element={<RestConsole />} />
<Route path="graphql" element={<GraphQLConsole />} />
<Route path="js_console" element={<Playground />} />
<Route path="graphql" element={<LazyComponentWrapper><LazyGraphQLConsole /></LazyComponentWrapper>} />
<Route path="js_console" element={<LazyComponentWrapper><LazyPlayground /></LazyComponentWrapper>} />
<Route index element={<Navigate replace to="rest" />} />
</Route>
);
Expand All @@ -400,7 +447,7 @@ class Dashboard extends React.Component {
<Route path="browser/:className" element={<BrowserRoute />} />
<Route path="browser" element={<BrowserRoute />} />

<Route path="cloud_code" element={<CloudCode />} />
<Route path="cloud_code" element={<LazyComponentWrapper><LazyCloudCode /></LazyComponentWrapper>} />
<Route path="webhooks" element={<Webhooks />} />

<Route path="jobs">{JobsRoute}</Route>
Expand Down
13 changes: 10 additions & 3 deletions src/dashboard/Data/AppOverview/AppOverview.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
import React from 'react';
import React, { Suspense, lazy } from 'react';
import DashboardView from 'dashboard/DashboardView.react';
import styles from 'dashboard/Data/AppOverview/AppOverview.scss';
import { withRouter } from 'lib/withRouter';
Expand All @@ -18,11 +18,12 @@ import AppSecurityCard from './AppSecurityCard.react';
import AppPerformanceCard from './AppPerformanceCard.react';
import AppLoadingText from './AppLoadingText.react';
import B4aTooltip from 'components/Tooltip/B4aTooltip.react';
import ConnectAppModal from './ConnectAppModal.react';
// import ConnectAppModal from './ConnectAppModal.react';
import OnboardingBoxes from './OnboardingBoxes.react';
import AccountManager from 'lib/AccountManager';
import { amplitudeLogEvent } from 'lib/amplitudeEvents';

const LazyConnectAppModal = lazy(() => import('./ConnectAppModal.react'));
@withRouter
class AppOverview extends DashboardView {
constructor() {
Expand Down Expand Up @@ -73,6 +74,10 @@ class AppOverview extends DashboardView {
this.loadCardInformation();
}

componentDidMount() {
import('./ConnectAppModal.react');
}

copyText(copyText = '') {
if (navigator) {
navigator.clipboard.writeText(copyText);
Expand Down Expand Up @@ -300,7 +305,9 @@ class AppOverview extends DashboardView {
</div>

{this.state.showConnectAppModal && (
<ConnectAppModal closeModal={() => this.setState({ showConnectAppModal: false })} />
<Suspense fallback={'Loading...'}>
<LazyConnectAppModal closeModal={() => this.setState({ showConnectAppModal: false })} />
</Suspense>
)}
</div>
);
Expand Down
8 changes: 6 additions & 2 deletions src/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import './instrument';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import Immutable from 'immutable';
import installDevTools from 'immutable-devtools';
// import installDevTools from 'immutable-devtools';
import React from 'react';
import ReactDOM from 'react-dom';
import Dashboard from './Dashboard';

require('stylesheets/fonts.scss');
require('graphiql/graphiql.min.css');
installDevTools(Immutable);

if (process.env.NODE_ENV !== 'production') {
const installDevTools = require('immutable-devtools');
installDevTools(Immutable);
}

const path = window.PARSE_DASHBOARD_PATH || '/';
ReactDOM.render(<Dashboard path={path} />, document.getElementById('browser_mount'));
86 changes: 57 additions & 29 deletions webpack/homolog.config.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,69 @@
const configuration = require('./publish.config.js');
/*
* Copyright (c) 2016-present, Parse, LLC
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
const configuration = require('./base.config.js');
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

configuration.mode = 'production';
configuration.entry = {
dashboard: './dashboard/index.js',
};

configuration.output.path = path.resolve('./Parse-Dashboard/public/bundles');
configuration.output.filename = '[name].[chunkhash].js';

configuration.optimization = {
usedExports: true,
minimize: true,
concatenateModules: true,
sideEffects: true,
providedExports: true,
innerGraph: true,
splitChunks: {
chunks: 'all',
minSize: 20000,
maxAsyncRequests: 30,
maxInitialRequests: 30
}
};

configuration.plugins.push(
new HtmlWebpackPlugin({
template: '../Parse-Dashboard/index.ejs',
filename: path.resolve('./Parse-Dashboard/public/index.html')
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('homolog'),
'SENTRY_ENV': JSON.stringify('homolog')
'NODE_ENV': JSON.stringify('production'),
'SENTRY_ENV': JSON.stringify('production')
}
}),
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
include: /dashboard.*.*/
})
// new BundleAnalyzerPlugin()
);

configuration.optimization = {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
configuration.module = configuration.module || {};
configuration.module.rules = configuration.module.rules || [];

// Ensure babel-loader is configured for tree shaking
const babelRule = configuration.module.rules.find(rule => rule.use && rule.use.includes('babel-loader'));
if (babelRule) {
babelRule.use = {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false // This is important for tree shaking
}]
]
}
},
minimize: true,
minimizer: [
new TerserPlugin({
// cache: true,
parallel: true,
terserOptions: {
sourceMap: true,
mangle: false
}
})
]
};
}

module.exports = configuration;
module.exports = configuration;
Loading