Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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-
<IconRowkey={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+
consticons=searchResults.slice(index*columnNum,(index+1)*columnNum);
334+
335+
return(
336+
<IconRowkey={key}style={style}>
337+
{icons.map((icon)=>(
338+
<Popover
339+
key={icon.uuid+'-popover'}
340+
content={
341+
<divstyle={{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)

AltStyle によって変換されたページ (->オリジナル) /