Skip to content

Commit 91cde6a

Browse files
author
FalkWolsky
committed
Updated IconScout Integration
1 parent d90330b commit 91cde6a

File tree

6 files changed

+155
-70
lines changed

6 files changed

+155
-70
lines changed

client/packages/lowcoder/src/api/iconFlowApi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export interface SearchParams {
66
query: string;
77
asset: string;
88
per_page: number;
9-
page: 1;
9+
page: number;
1010
sort: string;
1111
formats?: string;
1212
price?: string;

client/packages/lowcoder/src/comps/comps/imageComp.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,7 @@ let ImageBasicComp = new UICompBuilder(childrenMap, (props) => {
252252
{children.sourceMode.getView() === 'standard' && children.src.propertyView({
253253
label: trans("image.src"),
254254
})}
255-
{children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({
256-
label: trans("image.src"),
257-
})}
255+
{children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({})}
258256
</Section>
259257

260258
{["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (

client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,7 @@ let JsonLottieTmpComp = (function () {
281281
{children.sourceMode.getView() === 'standard' && children.value.propertyView({
282282
label: trans("jsonLottie.lottieJson"),
283283
})}
284-
{children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({
285-
label: "Lottie Source",
286-
})}
284+
{children.sourceMode.getView() === 'asset-library' && children.iconScoutAsset.propertyView({})}
287285
</Section>
288286

289287
{(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (

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

Lines changed: 142 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
useIcon,
1313
wrapperToControlItem,
1414
} from "lowcoder-design";
15-
import { ReactNode, useCallback, useMemo, useRef, useState } from "react";
15+
import { memo, ReactNode, useCallback, useMemo, useRef, useState } from "react";
1616
import styled from "styled-components";
1717
import Popover from "antd/es/popover";
1818
import { CloseIcon, SearchIcon } from "icons";
@@ -149,8 +149,8 @@ const IconRow = styled.div`
149149
`;
150150

151151
const IconItemContainer = styled.div`
152-
width: 60px;
153-
height: 60px;
152+
width: 120px;
153+
height: 120px;
154154
display: flex;
155155
flex-direction: column;
156156
align-items: center;
@@ -213,7 +213,7 @@ const IconScoutSearchParams: SearchParams = {
213213
sort: 'relevant',
214214
};
215215

216-
const columnNum = 8;
216+
const columnNum = 4;
217217

218218
export const IconPicker = (props: {
219219
assetType: string;
@@ -232,6 +232,10 @@ export const IconPicker = (props: {
232232
const [ searchResults, setSearchResults ] = useState<Array<any>>([]);
233233
const { subscriptions } = useSimpleSubscriptionContext();
234234

235+
const [page, setPage] = useState(1);
236+
const [hasMore, setHasMore] = useState(true);
237+
238+
235239
const mediaPackSubscription = subscriptions.find(
236240
sub => sub.product === SubscriptionProductsEnum.MEDIAPACKAGE && sub.status === 'active'
237241
);
@@ -246,26 +250,35 @@ export const IconPicker = (props: {
246250
}, []
247251
);
248252

249-
const fetchResults = async (query: string) => {
253+
const fetchResults = async (query: string, pageNum: number = 1) => {
250254
setLoading(true);
255+
251256
const freeResult = await searchAssets({
252257
...IconScoutSearchParams,
253258
asset: props.assetType,
254259
price: 'free',
255260
query,
261+
page: pageNum,
256262
});
263+
257264
const premiumResult = await searchAssets({
258265
...IconScoutSearchParams,
259266
asset: props.assetType,
260267
price: 'premium',
261268
query,
269+
page: pageNum,
262270
});
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);
263279
setLoading(false);
264-
265-
console.log("freeResult", freeResult, "premiumResult", premiumResult)
266-
267-
setSearchResults([...freeResult.data, ...premiumResult.data]);
268280
};
281+
269282

270283
const downloadAsset = async (
271284
uuid: string,
@@ -316,64 +329,119 @@ export const IconPicker = (props: {
316329
const debouncedFetchResults = useMemo(() => debounce(fetchResults, 700), []);
317330

318331
const rowRenderer = useCallback(
319-
(p: ListRowProps) => (
320-
<IconRow key={p.key} style={p.style}>
321-
{searchResults
322-
.slice(p.index * columnNum, (p.index + 1) * columnNum)
323-
.map((icon) => (
324-
<IconItemContainer
325-
key={icon.uuid}
326-
tabIndex={0}
327-
onClick={() => {
328-
// check if premium content then show subscription popup
329-
// TODO: if user has subscription then skip this if block
330-
if (!mediaPackSubscription) {
331-
CustomModal.confirm({
332-
title: trans("iconScout.buySubscriptionTitle"),
333-
content: trans("iconScout.buySubscriptionContent"),
334-
onConfirm: () =>{
335-
window.open(SUBSCRIPTION_SETTING, "_blank");
336-
},
337-
confirmBtnType: "primary",
338-
okText: trans("iconScout.buySubscriptionButton"),
339-
})
340-
return;
341-
}
342-
343-
fetchDownloadUrl(
344-
icon.uuid,
345-
props.assetType === AssetType.ICON ? icon.urls.png_64 : icon.urls.thumb,
346-
);
347-
}}
332+
({ index, key, style }: ListRowProps) => {
333+
const icons = searchResults.slice(index * columnNum, (index + 1) * columnNum);
334+
335+
return (
336+
<IconRow key={key} style={style}>
337+
{icons.map((icon) => (
338+
<Popover
339+
key={icon.uuid + '-popover'}
340+
content={
341+
<div style={{ maxWidth: 400 }}>
342+
{props.assetType === AssetType.LOTTIE ? (
343+
<video
344+
src={icon.urls.thumb}
345+
autoPlay
346+
loop
347+
muted
348+
style={{ width: "100%", height: "auto", borderRadius: 6 }}
349+
/>
350+
) : (
351+
<img
352+
src={props.assetType === AssetType.ICON ? icon.urls.png_128 : icon.urls.thumb}
353+
alt=""
354+
style={{ width: "100%", height: "auto", borderRadius: 6 }}
355+
/>
356+
)}
357+
</div>
358+
}
359+
placement="right"
360+
mouseEnterDelay={0.2}
348361
>
349-
<Badge
350-
count={icon.price !== 0 ? <CrownFilled style={{color: "#e7b549"}} /> : undefined}
351-
size='small'
362+
<IconItemContainer
363+
key={icon.uuid}
364+
tabIndex={0}
365+
onClick={() => {
366+
if (!mediaPackSubscription) {
367+
CustomModal.confirm({
368+
title: trans("iconScout.buySubscriptionTitle"),
369+
content: trans("iconScout.buySubscriptionContent"),
370+
onConfirm: () => {
371+
window.open(SUBSCRIPTION_SETTING, "_blank");
372+
},
373+
confirmBtnType: "primary",
374+
okText: trans("iconScout.buySubscriptionButton"),
375+
});
376+
return;
377+
}
378+
379+
fetchDownloadUrl(
380+
icon.uuid,
381+
props.assetType === AssetType.ICON ? icon.urls.png_64 : icon.urls.thumb,
382+
);
383+
}}
352384
>
353-
<IconWrapper $isPremium={icon.price !== 0}>
354-
{props.assetType === AssetType.ICON && (
355-
<StyledPreviewIcon src={icon.urls.png_64} />
356-
)}
357-
{props.assetType === AssetType.ILLUSTRATION && (
358-
<StyledPreviewIcon src={icon.urls.thumb} />
359-
)}
360-
{props.assetType === AssetType.LOTTIE && (
361-
<StyledPreviewLotte src={icon.urls.thumb} autoPlay />
362-
)}
363-
</IconWrapper>
364-
</Badge>
365-
</IconItemContainer>
385+
<Badge
386+
count={
387+
icon.price !== 0 ? (
388+
<CrownFilled style={{ color: "#e7b549" }} />
389+
) : undefined
390+
}
391+
size="small"
392+
>
393+
<IconWrapper $isPremium={icon.price !== 0}>
394+
{props.assetType === AssetType.ICON && (
395+
<StyledPreviewIcon src={icon.urls.png_64} />
396+
)}
397+
{props.assetType === AssetType.ILLUSTRATION && (
398+
<StyledPreviewIcon src={icon.urls.thumb} />
399+
)}
400+
{props.assetType === AssetType.LOTTIE && (
401+
<StyledPreviewLotte src={icon.urls.thumb} autoPlay />
402+
)}
403+
</IconWrapper>
404+
</Badge>
405+
</IconItemContainer>
406+
</Popover>
366407
))}
367-
</IconRow>
368-
),[searchResults]
408+
</IconRow>
409+
);
410+
},
411+
[columnNum, mediaPackSubscription, props.assetType, fetchDownloadUrl]
369412
);
413+
370414

371415
const popupTitle = useMemo(() => {
372416
if (props.assetType === AssetType.ILLUSTRATION) return trans("iconScout.searchImage");
373417
if (props.assetType === AssetType.LOTTIE) return trans("iconScout.searchAnimation");
374418
return trans("iconScout.searchIcon");
375419
}, [props.assetType]);
376420

421+
const MemoizedIconList = memo(({
422+
searchResults,
423+
rowRenderer,
424+
onScroll,
425+
columnNum,
426+
}: {
427+
searchResults: any[];
428+
rowRenderer: (props: ListRowProps) => React.ReactNode;
429+
onScroll: (params: { clientHeight: number; scrollHeight: number; scrollTop: number }) => void;
430+
columnNum: number;
431+
}) => {
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+
444+
377445
return (
378446
<Popover
379447
trigger={'click'}
@@ -418,12 +486,28 @@ export const IconPicker = (props: {
418486
)}
419487
{!loading && Boolean(searchText) && Boolean(searchResults?.length) && (
420488
<IconListWrapper>
489+
421490
<IconList
422491
width={550}
423492
height={400}
424-
rowHeight={80}
493+
rowHeight={140}
425494
rowCount={Math.ceil(searchResults.length / columnNum)}
426495
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+
}}
427511
/>
428512
</IconListWrapper>
429513
)}
@@ -451,7 +535,7 @@ export const IconPicker = (props: {
451535
/>
452536
</ButtonWrapper>
453537
) : (
454-
<BlockGrayLabel label={trans("iconControl.selectIcon")} />
538+
<BlockGrayLabel label={props.assetType === AssetType.LOTTIE ? trans("iconControl.searchAnimation") : props.assetType === AssetType.ILLUSTRATION ? trans("iconControl.searchIllustration") : trans("iconControl.searchIcon")} />
455539
)}
456540
</TacoButton>
457541
</Popover>

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
export enum SubscriptionProductsEnum {
33
// PROD
44
SUPPORT = "QYGsTWZYyJYzMg",
5+
MEDIAPACKAGE = 'SOz085DH7CmNHG',
56

67
// DEV
78
// SUPPORT = "QW8L3WPMiNjQjI",
8-
MEDIAPACKAGE = 'QW8MpIBHxieKXd',
9+
// MEDIAPACKAGE = 'QW8MpIBHxieKXd',
910
AZUREAPIS = 'QlQ7cdOh8Lv4dy',
1011
GOOGLEAPIS = 'enterprise',
1112
AWSAPIS = 'enterprise-global',
@@ -35,18 +36,18 @@ export const InitSubscriptionProducts = [
3536
type: "org",
3637
quantity_entity: "orgUser",
3738
},
38-
/* {
39+
{
3940
pricingType: "For you in this Workspace, monthly",
4041
activeSubscription: false,
41-
accessLink: "1Pf65wDDlQgecLSf6OFlbsD5",
42+
accessLink: "1RUB2XDDlQgecLSfRsBDx14y",
4243
product: SubscriptionProductsEnum.MEDIAPACKAGE,
4344
checkoutLink: "",
4445
checkoutLinkDataLoaded: false,
4546
subscriptionId: "",
4647
type: "user",
4748
quantity_entity: "singleItem",
4849
},
49-
{
50+
/* {
5051
pricingType: "For all in this Workspace, monthly",
5152
activeSubscription: false,
5253
accessLink: "1PttHIDDlQgecLSf0XP27tXt",

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2277,6 +2277,9 @@ export const en = {
22772277
},
22782278
"iconControl": {
22792279
"selectIcon": "Select an Icon",
2280+
"searchIcon" : "Search an Icon",
2281+
"searchAnimation" : "Search an Animation",
2282+
"searchIllustration" : "Search an Illustration",
22802283
"insertIcon": "Insert an Icon",
22812284
"insertImage": "Insert an Image or "
22822285
},
@@ -4008,6 +4011,7 @@ export const en = {
40084011
},
40094012
"jsonLottie": {
40104013
"lottieJson": "Lottie JSON",
4014+
"searchAnimation" : "Search Animation",
40114015
"speed": "Speed",
40124016
"width": "Width",
40134017
"height": "Height",
@@ -4333,7 +4337,7 @@ export const en = {
43334337
discord: "https://discord.com/invite/qMG9uTmAx2",
43344338
},
43354339

4336-
iconScout: {
4340+
"iconScout": {
43374341
"searchImage": "Search Image",
43384342
"searchAnimation": "Search Animation",
43394343
"searchIcon": "Search Icon",

0 commit comments

Comments
 (0)