Skip to content

Commit af9e431

Browse files
committed
Merge branch 'dev' into clean_json_schema_form
2 parents 9954a49 + cda5657 commit af9e431

File tree

3 files changed

+138
-126
lines changed

3 files changed

+138
-126
lines changed

client/packages/lowcoder/src/app.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ import {
3131
ADMIN_AUTH_URL,
3232
PUBLIC_APP_EDITOR_URL,
3333
} from "constants/routesURL";
34-
import React, { useMemo } from "react";
34+
import React, { useEffect, useMemo } from "react";
3535
import { createRoot } from "react-dom/client";
3636
import { Helmet } from "react-helmet";
37-
import { connect, Provider } from "react-redux";
37+
import { connect, Provider, useDispatch, useSelector } from "react-redux";
3838
import { Redirect, Route, Router, Switch } from "react-router-dom";
3939
import type { AppState } from "redux/reducers";
40-
import { fetchConfigAction } from "redux/reduxActions/configActions";
40+
import { fetchConfigAction, fetchDeploymentIdAction } from "redux/reduxActions/configActions";
4141
import { fetchUserAction } from "redux/reduxActions/userActions";
4242
import { reduxStore } from "redux/store/store";
4343
import { developEnv } from "util/envUtils";
@@ -50,10 +50,10 @@ import { loadComps } from "comps";
5050
import { initApp } from "util/commonUtils";
5151
import { favicon } from "assets/images";
5252
import { hasQueryParam } from "util/urlUtils";
53-
import { isFetchUserFinished } from "redux/selectors/usersSelectors"; // getCurrentUser,
53+
import { getUser, isFetchUserFinished } from "redux/selectors/usersSelectors"; // getCurrentUser,
5454
import { getIsCommonSettingFetched } from "redux/selectors/commonSettingSelectors";
5555
import { SystemWarning } from "./components/SystemWarning";
56-
import { getBrandingConfig } from "./redux/selectors/configSelectors";
56+
import { getBrandingConfig, getDeploymentId } from "./redux/selectors/configSelectors";
5757
import { buildMaterialPreviewURL } from "./util/materialUtils";
5858
import GlobalInstances from 'components/GlobalInstances';
5959
// import posthog from 'posthog-js'
@@ -64,6 +64,7 @@ import { fetchBrandingSetting } from "./redux/reduxActions/enterpriseActions";
6464
import { EnterpriseProvider } from "./util/context/EnterpriseContext";
6565
import { SimpleSubscriptionContextProvider } from "./util/context/SimpleSubscriptionContext";
6666
import { getBrandingSetting } from "./redux/selectors/enterpriseSelectors";
67+
import { fetchSubscriptionsAction } from "./redux/reduxActions/subscriptionActions";
6768

6869
const LazyUserAuthComp = React.lazy(() => import("pages/userAuth"));
6970
const LazyInviteLanding = React.lazy(() => import("pages/common/inviteLanding"));
@@ -81,6 +82,22 @@ const Wrapper = React.memo((props: {
8182
language: string,
8283
fontFamily?: string
8384
}) => {
85+
const deploymentId = useSelector(getDeploymentId);
86+
const user = useSelector(getUser);
87+
const dispatch = useDispatch();
88+
89+
useEffect(() => {
90+
if (user.currentOrgId) {
91+
dispatch(fetchDeploymentIdAction());
92+
}
93+
}, [user.currentOrgId]);
94+
95+
useEffect(() => {
96+
if(Boolean(deploymentId)) {
97+
dispatch(fetchSubscriptionsAction())
98+
}
99+
}, [deploymentId]);
100+
84101
const theme = useMemo(() => {
85102
return {
86103
hashed: false,

client/packages/lowcoder/src/comps/controls/iconscoutControl.tsx

Lines changed: 116 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
useIcon,
1313
wrapperToControlItem,
1414
} from "lowcoder-design";
15-
import { memo, ReactNode, useCallback, useMemo, useRef, useState } from "react";
15+
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
1616
import styled from "styled-components";
1717
import Popover from "antd/es/popover";
1818
import { CloseIcon, SearchIcon } from "icons";
@@ -225,62 +225,85 @@ export const IconPicker = (props: {
225225
IconType?: "OnlyAntd" | "All" | "default" | undefined;
226226
}) => {
227227
const draggableRef = useRef<HTMLDivElement>(null);
228-
const [ visible, setVisible ] = useState(false)
229-
const [ loading, setLoading ] = useState(false)
230-
const [ downloading, setDownloading ] = useState(false)
231-
const [ searchText, setSearchText ] = useState<string>('')
232-
const [ searchResults, setSearchResults ] = useState<Array<any>>([]);
233-
const { subscriptions } = useSimpleSubscriptionContext();
234-
228+
const [visible, setVisible] = useState(false);
229+
const [loading, setLoading] = useState(false);
230+
const [downloading, setDownloading] = useState(false);
231+
const [searchText, setSearchText] = useState<string>('');
232+
const [searchResults, setSearchResults] = useState<Array<any>>([]);
235233
const [page, setPage] = useState(1);
236234
const [hasMore, setHasMore] = useState(true);
235+
const abortControllerRef = useRef<AbortController | null>(null);
236+
const { subscriptions } = useSimpleSubscriptionContext();
237237

238-
239-
const mediaPackSubscription = subscriptions.find(
240-
sub => sub.product === SubscriptionProductsEnum.MEDIAPACKAGE && sub.status === 'active'
238+
const mediaPackSubscription = useMemo(() =>
239+
subscriptions.find(
240+
sub => sub.product === SubscriptionProductsEnum.MEDIAPACKAGE && sub.status === 'active'
241+
),
242+
[subscriptions]
241243
);
242244

243245
const onChangeRef = useRef(props.onChange);
244246
onChangeRef.current = props.onChange;
245247

248+
// Cleanup function for async operations
249+
useEffect(() => {
250+
return () => {
251+
if (abortControllerRef.current) {
252+
abortControllerRef.current.abort();
253+
}
254+
};
255+
}, []);
256+
246257
const onChangeIcon = useCallback(
247258
(key: string, value: string, url: string) => {
248259
onChangeRef.current(key, value, url);
249260
setVisible(false);
250-
}, []
261+
},
262+
[]
251263
);
252264

253-
const fetchResults = async (query: string, pageNum: number = 1) => {
265+
const fetchResults = useCallback(async (query: string, pageNum: number = 1) => {
266+
if (abortControllerRef.current) {
267+
abortControllerRef.current.abort();
268+
}
269+
abortControllerRef.current = new AbortController();
270+
254271
setLoading(true);
272+
try {
273+
const [freeResult, premiumResult] = await Promise.all([
274+
searchAssets({
275+
...IconScoutSearchParams,
276+
asset: props.assetType,
277+
price: 'free',
278+
query,
279+
page: pageNum,
280+
}),
281+
searchAssets({
282+
...IconScoutSearchParams,
283+
asset: props.assetType,
284+
price: 'premium',
285+
query,
286+
page: pageNum,
287+
})
288+
]);
255289

256-
const freeResult = await searchAssets({
257-
...IconScoutSearchParams,
258-
asset: props.assetType,
259-
price: 'free',
260-
query,
261-
page: pageNum,
262-
});
263-
264-
const premiumResult = await searchAssets({
265-
...IconScoutSearchParams,
266-
asset: props.assetType,
267-
price: 'premium',
268-
query,
269-
page: pageNum,
270-
});
271-
272-
const combined = [...freeResult.data, ...premiumResult.data];
273-
const isLastPage = combined.length < IconScoutSearchParams.per_page * 2;
274-
275-
setSearchResults(prev =>
276-
pageNum === 1 ? combined : [...prev, ...combined]
277-
);
278-
setHasMore(!isLastPage);
279-
setLoading(false);
280-
};
281-
290+
const combined = [...freeResult.data, ...premiumResult.data];
291+
const isLastPage = combined.length < IconScoutSearchParams.per_page * 2;
292+
293+
setSearchResults(prev =>
294+
pageNum === 1 ? combined : [...prev, ...combined]
295+
);
296+
setHasMore(!isLastPage);
297+
} catch (error: any) {
298+
if (error.name !== 'AbortError') {
299+
console.error('Error fetching results:', error);
300+
}
301+
} finally {
302+
setLoading(false);
303+
}
304+
}, [props.assetType]);
282305

283-
const downloadAsset = async (
306+
const downloadAsset = useCallback(async (
284307
uuid: string,
285308
downloadUrl: string,
286309
callback: (assetUrl: string) => void,
@@ -293,29 +316,29 @@ export const IconPicker = (props: {
293316
});
294317
}
295318
} catch(error) {
296-
console.error(error);
319+
console.error('Error downloading asset:', error);
297320
setDownloading(false);
298321
}
299-
}
322+
}, []);
300323

301-
const fetchDownloadUrl = async (uuid: string, preview: string) => {
324+
const fetchDownloadUrl = useCallback(async (uuid: string, preview: string) => {
302325
try {
303326
setDownloading(true);
304327
const result = await getAssetLinks(uuid, {
305328
format: props.assetType === AssetType.LOTTIE ? 'lottie' : 'svg',
306329
});
307330

308-
downloadAsset(uuid, result.download_url, (assetUrl: string) => {
331+
await downloadAsset(uuid, result.download_url, (assetUrl: string) => {
309332
setDownloading(false);
310333
onChangeIcon(uuid, assetUrl, preview);
311334
});
312335
} catch (error) {
313-
console.error(error);
336+
console.error('Error fetching download URL:', error);
314337
setDownloading(false);
315338
}
316-
}
339+
}, [props.assetType, downloadAsset, onChangeIcon]);
317340

318-
const handleChange = (e: { target: { value: any; }; }) => {
341+
const handleChange = useCallback((e: { target: { value: any; }; }) => {
319342
const query = e.target.value;
320343
setSearchText(query); // Update search text immediately
321344

@@ -324,9 +347,15 @@ export const IconPicker = (props: {
324347
} else {
325348
setSearchResults([]); // Clear results if input is too short
326349
}
327-
};
328-
329-
const debouncedFetchResults = useMemo(() => debounce(fetchResults, 700), []);
350+
}, []);
351+
352+
const debouncedFetchResults = useMemo(
353+
() => debounce((query: string) => {
354+
setPage(1);
355+
fetchResults(query, 1);
356+
}, 700),
357+
[fetchResults]
358+
);
330359

331360
const rowRenderer = useCallback(
332361
({ index, key, style }: ListRowProps) => {
@@ -408,39 +437,41 @@ export const IconPicker = (props: {
408437
</IconRow>
409438
);
410439
},
411-
[columnNum, mediaPackSubscription, props.assetType, fetchDownloadUrl]
440+
[columnNum, mediaPackSubscription, props.assetType, fetchDownloadUrl, searchResults]
412441
);
413-
414442

415443
const popupTitle = useMemo(() => {
416444
if (props.assetType === AssetType.ILLUSTRATION) return trans("iconScout.searchImage");
417445
if (props.assetType === AssetType.LOTTIE) return trans("iconScout.searchAnimation");
418446
return trans("iconScout.searchIcon");
419447
}, [props.assetType]);
420448

421-
const MemoizedIconList = memo(({
422-
searchResults,
423-
rowRenderer,
424-
onScroll,
425-
columnNum,
449+
const handleScroll = useCallback(({
450+
clientHeight,
451+
scrollHeight,
452+
scrollTop,
426453
}: {
427-
searchResults: any[];
428-
rowRenderer: (props: ListRowProps) => React.ReactNode;
429-
onScroll: (params: { clientHeight: number; scrollHeight: number; scrollTop: number }) => void;
430-
columnNum: number;
454+
clientHeight: number;
455+
scrollHeight: number;
456+
scrollTop: number;
431457
}) => {
432-
return (
433-
<IconList
434-
width={550}
435-
height={400}
436-
rowHeight={140}
437-
rowCount={Math.ceil(searchResults.length / columnNum)}
438-
rowRenderer={rowRenderer}
439-
onScroll={onScroll}
440-
/>
441-
);
442-
});
443-
458+
if (hasMore && !loading && scrollHeight - scrollTop <= clientHeight + 10) {
459+
const nextPage = page + 1;
460+
setPage(nextPage);
461+
fetchResults(searchText, nextPage);
462+
}
463+
}, [hasMore, loading, page, searchText, fetchResults]);
464+
465+
const memoizedIconListElement = useMemo(() => (
466+
<IconList
467+
width={550}
468+
height={400}
469+
rowHeight={140}
470+
rowCount={Math.ceil(searchResults.length / columnNum)}
471+
rowRenderer={rowRenderer}
472+
onScroll={handleScroll}
473+
/>
474+
), [searchResults.length, rowRenderer, handleScroll, columnNum]);
444475

445476
return (
446477
<Popover
@@ -471,11 +502,6 @@ export const IconPicker = (props: {
471502
/>
472503
<StyledSearchIcon />
473504
</SearchDiv>
474-
{loading && (
475-
<Flex align="center" justify="center" style={{flex: 1}}>
476-
<Spin indicator={<LoadingOutlined style={{ fontSize: 25 }} spin />} />
477-
</Flex>
478-
)}
479505
<Spin spinning={downloading} indicator={<LoadingOutlined style={{ fontSize: 25 }} />} >
480506
{!loading && Boolean(searchText) && !Boolean(searchResults?.length) && (
481507
<Flex align="center" justify="center" style={{flex: 1}}>
@@ -484,33 +510,16 @@ export const IconPicker = (props: {
484510
</Typography.Text>
485511
</Flex>
486512
)}
487-
{!loading && Boolean(searchText) && Boolean(searchResults?.length) && (
513+
{Boolean(searchText) && Boolean(searchResults?.length) && (
488514
<IconListWrapper>
489-
490-
<IconList
491-
width={550}
492-
height={400}
493-
rowHeight={140}
494-
rowCount={Math.ceil(searchResults.length / columnNum)}
495-
rowRenderer={rowRenderer}
496-
onScroll={({
497-
clientHeight,
498-
scrollHeight,
499-
scrollTop,
500-
}: {
501-
clientHeight: number;
502-
scrollHeight: number;
503-
scrollTop: number;
504-
}) => {
505-
if (hasMore && !loading && scrollHeight - scrollTop <= clientHeight + 10) {
506-
const nextPage = page + 1;
507-
setPage(nextPage);
508-
fetchResults(searchText, nextPage);
509-
}
510-
}}
511-
/>
515+
{memoizedIconListElement}
512516
</IconListWrapper>
513517
)}
518+
{loading && (
519+
<Flex align="center" justify="center" style={{flex: 1}}>
520+
<Spin indicator={<LoadingOutlined style={{ fontSize: 25 }} spin />} />
521+
</Flex>
522+
)}
514523
</Spin>
515524
</PopupContainer>
516525
</Draggable>
@@ -557,11 +566,12 @@ export function IconscoutControl(
557566
) {
558567
return class IconscoutControl extends SimpleComp<IconScoutAsset> {
559568
readonly IGNORABLE_DEFAULT_VALUE = false;
569+
560570
protected getDefaultValue(): IconScoutAsset {
561571
return {
562-
uuid: '',
563-
value: '',
564-
preview: '',
572+
uuid: "",
573+
value: "",
574+
preview: "",
565575
};
566576
}
567577

@@ -586,5 +596,5 @@ export function IconscoutControl(
586596
</ControlPropertyViewWrapper>
587597
);
588598
}
589-
}
599+
};
590600
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy