feat: marketplace list

pull/12372/head
StyleZhang 2 years ago
parent 9c4e809799
commit e7fb92e169

@ -13,10 +13,15 @@ import { useDebounceFn } from 'ahooks'
import { PLUGIN_TYPE_SEARCH_MAP } from './plugin-type-switch' import { PLUGIN_TYPE_SEARCH_MAP } from './plugin-type-switch'
import type { Plugin } from '../types' import type { Plugin } from '../types'
import type { import type {
CollectionsAndPluginsSearchParams,
MarketplaceCollection,
PluginsSearchParams, PluginsSearchParams,
PluginsSort, PluginsSort,
} from './types' } from './types'
import { getMarketplacePlugins } from './utils' import {
getMarketplaceCollectionsAndPlugins,
getMarketplacePlugins,
} from './utils'
import { DEFAULT_SORT } from './constants' import { DEFAULT_SORT } from './constants'
export type MarketplaceContextValue = { export type MarketplaceContextValue = {
@ -29,9 +34,13 @@ export type MarketplaceContextValue = {
activePluginType: string activePluginType: string
handleActivePluginTypeChange: (type: string) => void handleActivePluginTypeChange: (type: string) => void
plugins?: Plugin[] plugins?: Plugin[]
setPlugins?: (plugins: Plugin[]) => void setPlugins: (plugins: Plugin[]) => void
sort: PluginsSort sort: PluginsSort
handleSortChange: (sort: PluginsSort) => void handleSortChange: (sort: PluginsSort) => void
marketplaceCollectionsFromClient?: MarketplaceCollection[]
setMarketplaceCollectionsFromClient: (collections: MarketplaceCollection[]) => void
marketplaceCollectionPluginsMapFromClient?: Record<string, Plugin[]>
setMarketplaceCollectionPluginsMapFromClient: (map: Record<string, Plugin[]>) => void
} }
export const MarketplaceContext = createContext<MarketplaceContextValue>({ export const MarketplaceContext = createContext<MarketplaceContextValue>({
@ -47,6 +56,10 @@ export const MarketplaceContext = createContext<MarketplaceContextValue>({
setPlugins: () => {}, setPlugins: () => {},
sort: DEFAULT_SORT, sort: DEFAULT_SORT,
handleSortChange: () => {}, handleSortChange: () => {},
marketplaceCollectionsFromClient: [],
setMarketplaceCollectionsFromClient: () => {},
marketplaceCollectionPluginsMapFromClient: {},
setMarketplaceCollectionPluginsMapFromClient: () => {},
}) })
type MarketplaceContextProviderProps = { type MarketplaceContextProviderProps = {
@ -66,11 +79,26 @@ export const MarketplaceContextProvider = ({
const [activePluginType, setActivePluginType] = useState(PLUGIN_TYPE_SEARCH_MAP.all) const [activePluginType, setActivePluginType] = useState(PLUGIN_TYPE_SEARCH_MAP.all)
const [plugins, setPlugins] = useState<Plugin[]>() const [plugins, setPlugins] = useState<Plugin[]>()
const [sort, setSort] = useState(DEFAULT_SORT) const [sort, setSort] = useState(DEFAULT_SORT)
const [marketplaceCollectionsFromClient, setMarketplaceCollectionsFromClient] = useState<MarketplaceCollection[] | undefined>(undefined)
const [marketplaceCollectionPluginsMapFromClient, setMarketplaceCollectionPluginsMapFromClient] = useState<Record<string, Plugin[]> | undefined>(undefined)
const handleUpdatePlugins = useCallback(async (query: PluginsSearchParams) => { const handleUpdatePlugins = useCallback(async (query: PluginsSearchParams) => {
const { marketplacePlugins } = await getMarketplacePlugins(query) const { marketplacePlugins } = await getMarketplacePlugins(query)
setPlugins(marketplacePlugins) setPlugins(marketplacePlugins)
setMarketplaceCollectionsFromClient(undefined)
setMarketplaceCollectionPluginsMapFromClient(undefined)
}, [])
const handleUpdateMarketplaceCollectionsAndPlugins = useCallback(async (query?: CollectionsAndPluginsSearchParams) => {
const {
marketplaceCollections,
marketplaceCollectionPluginsMap,
} = await getMarketplaceCollectionsAndPlugins(query)
setMarketplaceCollectionsFromClient(marketplaceCollections)
setMarketplaceCollectionPluginsMapFromClient(marketplaceCollectionPluginsMap)
setPlugins(undefined)
}, []) }, [])
const { run: handleUpdatePluginsWithDebounced } = useDebounceFn(handleUpdatePlugins, { const { run: handleUpdatePluginsWithDebounced } = useDebounceFn(handleUpdatePlugins, {
@ -80,20 +108,58 @@ export const MarketplaceContextProvider = ({
const handleSearchPluginTextChange = useCallback((text: string) => { const handleSearchPluginTextChange = useCallback((text: string) => {
setSearchPluginText(text) setSearchPluginText(text)
handleUpdatePluginsWithDebounced({ query: text }) handleUpdatePluginsWithDebounced({
}, [handleUpdatePluginsWithDebounced]) query: text,
category: activePluginType === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginType,
tags: filterPluginTags,
sortBy: sort.sortBy,
sortOrder: sort.sortOrder,
})
}, [handleUpdatePluginsWithDebounced, activePluginType, filterPluginTags, sort])
const handleFilterPluginTagsChange = useCallback((tags: string[]) => { const handleFilterPluginTagsChange = useCallback((tags: string[]) => {
setFilterPluginTags(tags) setFilterPluginTags(tags)
}, [])
handleUpdatePlugins({
query: searchPluginText,
category: activePluginType === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginType,
tags,
sortBy: sort.sortBy,
sortOrder: sort.sortOrder,
})
}, [handleUpdatePlugins, searchPluginText, activePluginType, sort])
const handleActivePluginTypeChange = useCallback((type: string) => { const handleActivePluginTypeChange = useCallback((type: string) => {
setActivePluginType(type) setActivePluginType(type)
}, [])
if (!searchPluginText && !filterPluginTags.length) {
handleUpdateMarketplaceCollectionsAndPlugins({
category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type,
})
return
}
handleUpdatePlugins({
query: searchPluginText,
category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type,
tags: filterPluginTags,
sortBy: sort.sortBy,
sortOrder: sort.sortOrder,
})
}, [handleUpdatePlugins, searchPluginText, filterPluginTags, sort, handleUpdateMarketplaceCollectionsAndPlugins])
const handleSortChange = useCallback((sort: PluginsSort) => { const handleSortChange = useCallback((sort: PluginsSort) => {
setSort(sort) setSort(sort)
}, [])
handleUpdatePlugins({
query: searchPluginText,
category: activePluginType === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginType,
tags: filterPluginTags,
sortBy: sort.sortBy,
sortOrder: sort.sortOrder,
})
}, [handleUpdatePlugins, searchPluginText, activePluginType, filterPluginTags])
return ( return (
<MarketplaceContext.Provider <MarketplaceContext.Provider
@ -110,6 +176,10 @@ export const MarketplaceContextProvider = ({
setPlugins, setPlugins,
sort, sort,
handleSortChange, handleSortChange,
marketplaceCollectionsFromClient,
setMarketplaceCollectionsFromClient,
marketplaceCollectionPluginsMapFromClient,
setMarketplaceCollectionPluginsMapFromClient,
}} }}
> >
{children} {children}

@ -0,0 +1,40 @@
import { Group } from '@/app/components/base/icons/src/vender/other'
import Line from './line'
const Empty = () => {
return (
<div
className='relative grid grid-cols-4 grid-rows-4 gap-3 p-2'
>
{
Array.from({ length: 16 }).map((_, index) => (
<div
key={index}
className='h-[144px] rounded-xl bg-background-section-burn'
>
</div>
))
}
<div
className='absolute inset-0 z-[1]'
style={{
backgroundImage: 'linear-gradient(180deg, rgba(255,255,255,0.01), #FCFCFD)',
}}
></div>
<div className='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[2] flex flex-col items-center'>
<div className='relative flex items-center justify-center mb-3 w-14 h-14 rounded-xl border border-divider-subtle bg-components-card-bg shadow-lg'>
<Group className='w-5 h-5' />
<Line className='absolute -right-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute -left-[1px] top-1/2 -translate-y-1/2' />
<Line className='absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 rotate-90' />
<Line className='absolute top-full left-1/2 -translate-x-1/2 -translate-y-1/2 rotate-90' />
</div>
<div className='text-center system-md-regular text-text-tertiary'>
No plugin found
</div>
</div>
</div>
)
}
export default Empty

@ -0,0 +1,21 @@
type LineProps = {
className?: string
}
const Line = ({
className,
}: LineProps) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="2" height="241" viewBox="0 0 2 241" fill="none" className={className}>
<path d="M1 0.5L1 240.5" stroke="url(#paint0_linear_1989_74474)"/>
<defs>
<linearGradient id="paint0_linear_1989_74474" x1="-7.99584" y1="240.5" x2="-7.88094" y2="0.50004" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stopOpacity="0.01"/>
<stop offset="0.503965" stopColor="#101828" stopOpacity="0.08"/>
<stop offset="1" stopColor="white" stopOpacity="0.01"/>
</linearGradient>
</defs>
</svg>
)
}
export default Line

@ -3,6 +3,7 @@ import type { Plugin } from '../../types'
import type { MarketplaceCollection } from '../types' import type { MarketplaceCollection } from '../types'
import ListWithCollection from './list-with-collection' import ListWithCollection from './list-with-collection'
import CardWrapper from './card-wrapper' import CardWrapper from './card-wrapper'
import Empty from '../empty'
type ListProps = { type ListProps = {
marketplaceCollections: MarketplaceCollection[] marketplaceCollections: MarketplaceCollection[]
@ -28,7 +29,7 @@ const List = ({
) )
} }
{ {
plugins && ( plugins && !!plugins.length && (
<div className='grid grid-cols-4 gap-3'> <div className='grid grid-cols-4 gap-3'>
{ {
plugins.map(plugin => ( plugins.map(plugin => (
@ -42,6 +43,11 @@ const List = ({
</div> </div>
) )
} }
{
plugins && !plugins.length && (
<Empty />
)
}
</> </>
) )
} }

@ -15,18 +15,24 @@ const ListWrapper = ({
marketplaceCollectionPluginsMap, marketplaceCollectionPluginsMap,
showInstallButton, showInstallButton,
}: ListWrapperProps) => { }: ListWrapperProps) => {
const plugins = useMarketplaceContext(s => s.plugins) const plugins = useMarketplaceContext(v => v.plugins)
const marketplaceCollectionsFromClient = useMarketplaceContext(v => v.marketplaceCollectionsFromClient)
const marketplaceCollectionPluginsMapFromClient = useMarketplaceContext(v => v.marketplaceCollectionPluginsMapFromClient)
return ( return (
<div className='px-12 py-2 bg-background-default-subtle'> <div className='px-12 py-2 bg-background-default-subtle'>
<div className='flex items-center'> {
<div className='title-xl-semi-bold text-text-primary'>134 results</div> plugins && (
<div className='flex items-center mb-4 pt-3'>
<div className='title-xl-semi-bold text-text-primary'>{plugins.length} results</div>
<div className='mx-3 w-[1px] h-3.5 bg-divider-regular'></div> <div className='mx-3 w-[1px] h-3.5 bg-divider-regular'></div>
<SortDropdown /> <SortDropdown />
</div> </div>
)
}
<List <List
marketplaceCollections={marketplaceCollections} marketplaceCollections={marketplaceCollectionsFromClient || marketplaceCollections}
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap} marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMapFromClient || marketplaceCollectionPluginsMap}
plugins={plugins} plugins={plugins}
showInstallButton={showInstallButton} showInstallButton={showInstallButton}
/> />

@ -25,10 +25,14 @@ export type PluginsSearchParams = {
sortBy?: string sortBy?: string
sortOrder?: string sortOrder?: string
category?: string category?: string
tag?: string tags?: string[]
} }
export type PluginsSort = { export type PluginsSort = {
sortBy: string sortBy: string
sortOrder: string sortOrder: string
} }
export type CollectionsAndPluginsSearchParams = {
category?: string
}

@ -1,11 +1,16 @@
import type { Plugin } from '@/app/components/plugins/types' import type { Plugin } from '@/app/components/plugins/types'
import type { import type {
CollectionsAndPluginsSearchParams,
MarketplaceCollection, MarketplaceCollection,
PluginsSearchParams, PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types' } from '@/app/components/plugins/marketplace/types'
import { MARKETPLACE_API_PREFIX } from '@/config' import { MARKETPLACE_API_PREFIX } from '@/config'
export const getMarketplaceCollectionsAndPlugins = async () => { export const getPluginIconInMarketplace = (plugin: Plugin) => {
return `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon`
}
export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAndPluginsSearchParams) => {
let marketplaceCollections = [] as MarketplaceCollection[] let marketplaceCollections = [] as MarketplaceCollection[]
let marketplaceCollectionPluginsMap = {} as Record<string, Plugin[]> let marketplaceCollectionPluginsMap = {} as Record<string, Plugin[]>
try { try {
@ -13,12 +18,12 @@ export const getMarketplaceCollectionsAndPlugins = async () => {
const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json() const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json()
marketplaceCollections = marketplaceCollectionsDataJson.data.collections marketplaceCollections = marketplaceCollectionsDataJson.data.collections
await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => { await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => {
const marketplaceCollectionPluginsData = await globalThis.fetch(`${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins`) const marketplaceCollectionPluginsData = await globalThis.fetch(`${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins?category=${query?.category}`)
const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json() const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json()
const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => { const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => {
return { return {
...plugin, ...plugin,
icon: `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon`, icon: getPluginIconInMarketplace(plugin),
} }
}) })
@ -54,7 +59,7 @@ export const getMarketplacePlugins = async (query: PluginsSearchParams) => {
sort_by: query.sortBy, sort_by: query.sortBy,
sort_order: query.sortOrder, sort_order: query.sortOrder,
category: query.category, category: query.category,
tag: query.tag, tags: query.tags,
}), }),
}, },
) )
@ -62,7 +67,7 @@ export const getMarketplacePlugins = async (query: PluginsSearchParams) => {
marketplacePlugins = marketplacePluginsDataJson.data.plugins.map((plugin: Plugin) => { marketplacePlugins = marketplacePluginsDataJson.data.plugins.map((plugin: Plugin) => {
return { return {
...plugin, ...plugin,
icon: `${MARKETPLACE_API_PREFIX}/plugins/${plugin.org}/${plugin.name}/icon`, icon: getPluginIconInMarketplace(plugin),
} }
}) })
} }

Loading…
Cancel
Save