Skip to content

Commit 96b90e8

Browse files
added public app editor
1 parent 1a3d073 commit 96b90e8

File tree

9 files changed

+404
-8
lines changed

9 files changed

+404
-8
lines changed

client/packages/lowcoder/src/app.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ORG_AUTH_FORGOT_PASSWORD_URL,
3030
ORG_AUTH_RESET_PASSWORD_URL,
3131
ADMIN_AUTH_URL,
32+
PUBLIC_APP_EDITOR_URL,
3233
} from "constants/routesURL";
3334
import React from "react";
3435
import { createRoot } from "react-dom/client";
@@ -65,6 +66,7 @@ const LazyInviteLanding = React.lazy(() => import("pages/common/inviteLanding"))
6566
const LazyComponentDoc = React.lazy(() => import("pages/ComponentDoc"));
6667
const LazyComponentPlayground = React.lazy(() => import("pages/ComponentPlayground"));
6768
const LazyAppEditor = React.lazy(() => import("pages/editor/AppEditor"));
69+
const LazyPublicAppEditor = React.lazy(() => import("pages/editor/AppEditorPublic"));
6870
const LazyAppFromTemplate = React.lazy(() => import("pages/ApplicationV2/AppFromTemplate"));
6971
const LazyApplicationHome = React.lazy(() => import("pages/ApplicationV2"));
7072
const LazyDebugComp = React.lazy(() => import("./debug"));
@@ -301,6 +303,14 @@ class AppIndex extends React.Component<AppIndexProps, any> {
301303
path={IMPORT_APP_FROM_TEMPLATE_URL}
302304
component={LazyAppFromTemplate}
303305
/>
306+
307+
<LazyRoute
308+
exact
309+
fallback="layout"
310+
path={PUBLIC_APP_EDITOR_URL}
311+
component={LazyPublicAppEditor}
312+
/>
313+
304314
<LazyRoute
305315
fallback="layout"
306316
path={APP_EDITOR_URL}

client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ async function npmLoader(
2424
: NPM_PLUGIN_ASSETS_BASE_URL;
2525

2626
const entry = `${pluginBaseUrl}/${appId || 'none'}/${packageName}@${localPackageVersion}/index.js`;
27+
// const entry = `../../../../../public/package/index.js`;
2728

2829
try {
2930
const module = await import(

client/packages/lowcoder/src/constants/routesURL.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const FOLDERS_URL = `/folders`;
4949
export const TRASH_URL = `/trash`;
5050
export const IMPORT_APP_FROM_TEMPLATE_URL = `${ALL_APPLICATIONS_URL}/template-import/:templateId`;
5151
export const APP_EDITOR_URL = `${ALL_APPLICATIONS_URL}/:applicationId/:viewMode/:appPageId?`;
52+
export const PUBLIC_APP_EDITOR_URL = `${ALL_APPLICATIONS_URL}/public/edit`;
5253

5354
export const AUTH_BIND_URL = `${USER_AUTH_URL}/bind`;
5455
export const AUTH_LOGIN_URL = `${USER_AUTH_URL}/login`;

client/packages/lowcoder/src/pages/common/header.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ export default function Header(props: HeaderProps) {
383383

384384
const isModule = appType === AppTypeEnum.Module;
385385

386+
console.log('header', applicationId);
387+
386388
useEffect(() => {
387389
if(blockEditing && application && Boolean(application?.editingUserId)) {
388390
UserApi.getUserDetail(application.editingUserId!)
@@ -536,7 +538,7 @@ export default function Header(props: HeaderProps) {
536538
) : (
537539
<>
538540
{/* Display a hint about who is editing the app */}
539-
{blockEditing && (
541+
{blockEditing && Boolean(applicationId) && (
540542
<>
541543
<Popover
542544
style={{ width: 200 }}
@@ -587,7 +589,7 @@ export default function Header(props: HeaderProps) {
587589
</>
588590
)}
589591

590-
{applicationId && (
592+
{Boolean(applicationId) && applicationId !== 'public' && (
591593
<AppPermissionDialog
592594
applicationId={applicationId}
593595
visible={permissionDialogVisible}

client/packages/lowcoder/src/pages/editor/AppEditor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ const AppEditor = React.memo(() => {
191191
},
192192
});
193193
setAppInfo(info);
194+
console.log(info);
194195
fetchJSDataSourceByApp();
195196
setFetchingAppDetails(false);
196197
},
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import { AppPathParams, AppTypeEnum } from "constants/applicationConstants";
2+
import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
3+
import { useDispatch, useSelector } from "react-redux";
4+
import { useParams } from "react-router";
5+
import { AppSummaryInfo, fetchApplicationInfo } from "redux/reduxActions/applicationActions";
6+
import { fetchDataSourceTypes } from "redux/reduxActions/datasourceActions";
7+
import { getUser } from "redux/selectors/usersSelectors";
8+
import { useUserViewMode } from "util/hooks";
9+
import "comps/uiCompRegistry";
10+
import { showAppSnapshotSelector } from "redux/selectors/appSnapshotSelector";
11+
import { setShowAppSnapshot } from "redux/reduxActions/appSnapshotActions";
12+
import { fetchGroupsAction } from "redux/reduxActions/orgActions";
13+
import { getFetchOrgGroupsFinished } from "redux/selectors/orgSelectors";
14+
import { getIsCommonSettingFetching } from "redux/selectors/commonSettingSelectors";
15+
import {
16+
MarkAppDSLLoaded,
17+
MarkAppEditorFirstRender,
18+
MarkAppEditorMounted,
19+
perfClear,
20+
perfMark,
21+
} from "util/perfUtils";
22+
import { useMount, useUnmount } from "react-use";
23+
import { clearGlobalSettings, setGlobalSettings } from "comps/utils/globalSettings";
24+
import { fetchFolderElements } from "redux/reduxActions/folderActions";
25+
import { registryDataSourcePlugin } from "constants/queryConstants";
26+
import { useRootCompInstance } from "./useRootCompInstance";
27+
import EditorSkeletonView from "./editorSkeletonView";
28+
import {ErrorBoundary} from 'react-error-boundary';
29+
import { ALL_APPLICATIONS_URL } from "@lowcoder-ee/constants/routesURL";
30+
import history from "util/history";
31+
import Flex from "antd/es/flex";
32+
import React from "react";
33+
import { currentApplication } from "@lowcoder-ee/redux/selectors/applicationSelector";
34+
import { AppState } from "@lowcoder-ee/redux/reducers";
35+
import { resetIconDictionary } from "@lowcoder-ee/constants/iconConstants";
36+
import {fetchJsDSPaginationByApp} from "@lowcoder-ee/util/pagination/axios";
37+
38+
const AppEditorInternalView = lazy(
39+
() => import("pages/editor/appEditorInternal")
40+
.then(moduleExports => ({default: moduleExports.AppEditorInternalView}))
41+
);
42+
43+
const AppEditorPublic = React.memo(() => {
44+
const dispatch = useDispatch();
45+
const params = useParams<AppPathParams>();
46+
const isUserViewModeCheck = useUserViewMode();
47+
const showAppSnapshot = useSelector(showAppSnapshotSelector);
48+
const currentUser = useSelector(getUser);
49+
const fetchOrgGroupsFinished = useSelector(getFetchOrgGroupsFinished);
50+
const isCommonSettingsFetching = useSelector(getIsCommonSettingFetching);
51+
const application = useSelector(currentApplication);
52+
const [currentPage, setCurrentPage] = useState(1);
53+
const [pageSize, setPageSize] = useState(10);
54+
const [elements, setElements] = useState({ elements: [], total: 1 })
55+
const isLowcoderCompLoading = useSelector((state: AppState) => state.npmPlugin.loading.lowcoderComps);
56+
57+
const isUserViewMode = useMemo(
58+
() => params.viewMode ? isUserViewModeCheck : true,
59+
[params.viewMode, isUserViewModeCheck]
60+
);
61+
const applicationId = useMemo(
62+
() => params.applicationId || window.location.pathname.split("/")[2],
63+
[params.applicationId, window.location.pathname]
64+
);
65+
console.log('viewMode', applicationId);
66+
const paramViewMode = useMemo(
67+
() => params.viewMode || window.location.pathname.split("/")[3],
68+
[params.viewMode, window.location.pathname]
69+
);
70+
const viewMode = useMemo(
71+
() => (paramViewMode === "view" || paramViewMode === "admin")
72+
? "published"
73+
: paramViewMode === "view_marketplace" ? "view_marketplace" : "editing",
74+
[paramViewMode]
75+
);
76+
77+
const firstRendered = useRef(false);
78+
const orgId = useMemo(() => currentUser.currentOrgId, [currentUser.currentOrgId]);
79+
const [isDataSourcePluginRegistered, setIsDataSourcePluginRegistered] = useState(false);
80+
const [appError, setAppError] = useState('');
81+
const [blockEditing, setBlockEditing] = useState<boolean>(true);
82+
const [fetchingAppDetails, setFetchingAppDetails] = useState<boolean>(false);
83+
84+
setGlobalSettings({ applicationId, isViewMode: paramViewMode === "view" });
85+
86+
if (!firstRendered.current) {
87+
perfClear();
88+
perfMark(MarkAppEditorFirstRender);
89+
firstRendered.current = true;
90+
}
91+
92+
useMount(() => {
93+
perfMark(MarkAppEditorMounted);
94+
});
95+
96+
useUnmount(() => {
97+
clearGlobalSettings();
98+
});
99+
100+
// fetch dsl
101+
const [appInfo, setAppInfo] = useState<AppSummaryInfo>({
102+
id: "",
103+
appType: AppTypeEnum.Application,
104+
});
105+
106+
const readOnly = applicationId === 'public' ? false : isUserViewMode;
107+
console.log('readOnly', readOnly)
108+
const compInstance = useRootCompInstance(
109+
appInfo,
110+
readOnly,
111+
isDataSourcePluginRegistered,
112+
blockEditing,
113+
);
114+
115+
// fetch dataSource and plugin
116+
useEffect(() => {
117+
if (!orgId || paramViewMode !== "edit") {
118+
return;
119+
}
120+
dispatch(fetchDataSourceTypes({ organizationId: orgId }));
121+
dispatch(fetchFolderElements({}));
122+
}, [dispatch, orgId, paramViewMode]);
123+
124+
125+
const fetchJSDataSourceByApp = useCallback(() => {
126+
fetchJsDSPaginationByApp({
127+
appId: applicationId,
128+
pageNum: currentPage,
129+
pageSize: pageSize
130+
}).then((res) => {
131+
setElements({elements: [], total: res.total || 1})
132+
res.data!.forEach((i: any) => {
133+
registryDataSourcePlugin(i.type, i.id, i.pluginDefinition);
134+
});
135+
setIsDataSourcePluginRegistered(true);
136+
});
137+
dispatch(setShowAppSnapshot(false));
138+
}, [
139+
applicationId,
140+
registryDataSourcePlugin,
141+
setIsDataSourcePluginRegistered,
142+
setShowAppSnapshot,
143+
dispatch,
144+
currentPage,
145+
pageSize
146+
]);
147+
148+
useEffect(() => {
149+
if (!fetchOrgGroupsFinished) {
150+
dispatch(fetchGroupsAction(orgId));
151+
}
152+
}, [dispatch, fetchOrgGroupsFinished, orgId]);
153+
154+
const fetchApplication = useCallback(() => {
155+
setFetchingAppDetails(true);
156+
dispatch(
157+
fetchApplicationInfo({
158+
type: viewMode,
159+
applicationId: applicationId,
160+
onSuccess: (info) => {
161+
perfMark(MarkAppDSLLoaded);
162+
const runJsInHost =
163+
info.orgCommonSettings?.runJavaScriptInHost ?? !!REACT_APP_DISABLE_JS_SANDBOX;
164+
setGlobalSettings({
165+
orgCommonSettings: {
166+
...info.orgCommonSettings,
167+
runJavaScriptInHost: runJsInHost,
168+
},
169+
});
170+
console.log(info);
171+
setAppInfo(info);
172+
fetchJSDataSourceByApp();
173+
setFetchingAppDetails(false);
174+
},
175+
onError: (errorMessage) => {
176+
setAppError(errorMessage);
177+
setFetchingAppDetails(false);
178+
}
179+
})
180+
);
181+
}, [viewMode, applicationId, dispatch, fetchJSDataSourceByApp]);
182+
183+
useEffect(() => {
184+
if(!isLowcoderCompLoading) {
185+
fetchApplication();
186+
resetIconDictionary();
187+
}
188+
}, [isLowcoderCompLoading, fetchApplication]);
189+
190+
const fallbackUI = useMemo(() => (
191+
<Flex align="center" justify="center" vertical style={{
192+
height: '300px',
193+
width: '400px',
194+
margin: '0 auto',
195+
}}>
196+
<h4 style={{margin: 0}}>Something went wrong while displaying this webpage</h4>
197+
<button onClick={() => history.push(ALL_APPLICATIONS_URL)} style={{background: '#4965f2',border: '1px solid #4965f2', color: '#ffffff',borderRadius:'6px'}}>Go to Apps</button>
198+
</Flex>
199+
), []);
200+
201+
if (Boolean(appError)) {
202+
return (
203+
<Flex align="center" justify="center" vertical style={{
204+
height: '300px',
205+
width: '400px',
206+
margin: '0 auto',
207+
}}>
208+
<h4>{appError}</h4>
209+
<button onClick={() => history.push(ALL_APPLICATIONS_URL)} style={{background: '#4965f2',border: '1px solid #4965f2', color: '#ffffff',borderRadius:'6px'}}>Back to Home</button>
210+
</Flex>
211+
)
212+
}
213+
214+
return (
215+
<ErrorBoundary fallback={fallbackUI}>
216+
<Suspense fallback={<EditorSkeletonView />}>
217+
{fetchingAppDetails
218+
? <EditorSkeletonView />
219+
: (
220+
<AppEditorInternalView
221+
appInfo={appInfo}
222+
readOnly={readOnly}
223+
blockEditing={blockEditing}
224+
loading={
225+
!fetchOrgGroupsFinished || !isDataSourcePluginRegistered || isCommonSettingsFetching
226+
}
227+
compInstance={compInstance}
228+
fetchApplication={fetchApplication}
229+
/>
230+
)
231+
}
232+
</Suspense>
233+
</ErrorBoundary>
234+
);
235+
});
236+
237+
export default AppEditorPublic;

client/packages/lowcoder/src/pages/editor/appEditorInternal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ export const AppEditorInternalView = React.memo((props: AppEditorInternalViewPro
143143
const loading =
144144
!compInstance || !compInstance.comp || !compInstance.comp.preloaded || props.loading;
145145

146+
console.log('loading', loading);
146147
const currentUser = useSelector(getCurrentUser);
147148

148149
return loading ? (

0 commit comments

Comments
 (0)