@@ -5,19 +5,23 @@ import { BudDrawerLayout } from "@/components/ui/bud/dataEntry/BudDrawerLayout";
55import { BudForm } from "@/components/ui/bud/dataEntry/BudForm" ;
66import DeployModelSelect from "@/components/ui/bud/deploymentDrawer/DeployModelSelect" ;
77import ModelFilter from "@/components/ui/bud/deploymentDrawer/ModelFilter" ;
8- import React , { useContext , useEffect } from "react" ;
8+ import React , { useContext , useEffect , useRef , useCallback } from "react" ;
99import { useDrawer } from "src/hooks/useDrawer" ;
1010import { useModels } from "src/hooks/useModels" ;
1111import { useDeployModel } from "src/stores/useDeployModel" ;
1212
13+ const PAGE_SIZE = 50 ;
14+
1315export default function ModelList ( ) {
1416 const { openDrawerWithStep } = useDrawer ( ) ;
1517
16- const [ page , setPage ] = React . useState ( 1 ) ;
17- const [ limit , setLimit ] = React . useState ( 100 ) ;
18+ const pageRef = useRef ( 1 ) ;
1819
1920 const { selectedProvider, modalityType } = useDeployModel ( ) ;
2021 const [ models , setModels ] = React . useState ( [ ] ) ;
22+ const [ hasMore , setHasMore ] = React . useState ( true ) ;
23+ const [ isLoadingMore , setIsLoadingMore ] = React . useState ( false ) ;
24+ const lastScrollTop = useRef ( 0 ) ;
2125 const { isExpandedViewOpen } = useContext ( BudFormContext ) ;
2226
2327 const { loading, fetchModels } = useModels ( ) ;
@@ -27,36 +31,105 @@ export default function ModelList() {
2731 const { selectedModel, setSelectedModel, currentWorkflow, updateCloudModel } =
2832 useDeployModel ( ) ;
2933
34+ const getFilterParams = useCallback ( ( ) => {
35+ const endpoints =
36+ modalityType ?. endpoints && modalityType . endpoints . length > 0
37+ ? modalityType . endpoints
38+ : undefined ;
39+ const modalities =
40+ ! endpoints &&
41+ modalityType ?. modalities &&
42+ modalityType . modalities . length > 0
43+ ? modalityType . modalities
44+ : undefined ;
45+
46+ return {
47+ table_source : "cloud_model" as const ,
48+ source : selectedProvider ?. type ,
49+ modality : modalities ,
50+ supported_endpoints : endpoints ,
51+ } ;
52+ } , [
53+ selectedProvider ?. type ,
54+ modalityType ?. modalities ,
55+ modalityType ?. endpoints ,
56+ ] ) ;
57+
3058 useEffect ( ( ) => {
3159 if ( currentWorkflow ?. workflow_steps ?. model ) {
3260 setSelectedModel ( currentWorkflow . workflow_steps . model ) ;
3361 }
3462 } , [ currentWorkflow ] ) ;
3563
3664 useEffect ( ( ) => {
37- const endpoints = modalityType ?. endpoints && modalityType . endpoints . length > 0
38- ? modalityType . endpoints
39- : undefined ;
40- const modalities = ! endpoints && modalityType ?. modalities && modalityType . modalities . length > 0
41- ? modalityType . modalities
42- : undefined ;
43-
4465 if ( ! selectedProvider ?. type ) {
4566 setModels ( [ ] ) ;
67+ setHasMore ( false ) ;
4668 return ;
4769 }
4870
71+ pageRef . current = 1 ;
72+ setHasMore ( true ) ;
73+
4974 fetchModels ( {
50- page,
51- limit,
52- table_source : "cloud_model" ,
53- source : selectedProvider ?. type ,
54- modality : modalities ,
55- supported_endpoints : endpoints ,
75+ page : 1 ,
76+ limit : PAGE_SIZE ,
77+ ...getFilterParams ( ) ,
5678 } ) . then ( ( data ) => {
57- setModels ( data ) ;
79+ const items = data || [ ] ;
80+ setModels ( items ) ;
81+ setHasMore ( items . length >= PAGE_SIZE ) ;
5882 } ) ;
59- } , [ selectedProvider ?. type , modalityType ?. modalities , modalityType ?. endpoints , page , limit , fetchModels ] ) ;
83+ } , [
84+ selectedProvider ?. type ,
85+ modalityType ?. modalities ,
86+ modalityType ?. endpoints ,
87+ fetchModels ,
88+ getFilterParams ,
89+ ] ) ;
90+
91+ const loadMore = useCallback ( ( ) => {
92+ if ( isLoadingMore || ! hasMore || loading ) return ;
93+
94+ setIsLoadingMore ( true ) ;
95+ const nextPage = pageRef . current + 1 ;
96+
97+ fetchModels ( {
98+ page : nextPage ,
99+ limit : PAGE_SIZE ,
100+ ...getFilterParams ( ) ,
101+ } )
102+ . then ( ( data ) => {
103+ const items = data || [ ] ;
104+ if ( items . length > 0 ) {
105+ setModels ( ( prev ) => [ ...prev , ...items ] ) ;
106+ pageRef . current = nextPage ;
107+ }
108+ setHasMore ( items . length >= PAGE_SIZE ) ;
109+ } )
110+ . finally ( ( ) => {
111+ setIsLoadingMore ( false ) ;
112+ } ) ;
113+ } , [ isLoadingMore , hasMore , loading , fetchModels , getFilterParams ] ) ;
114+
115+ const handleScroll = useCallback (
116+ ( e : React . UIEvent < HTMLDivElement > ) => {
117+ const target = e . currentTarget ;
118+ const { scrollTop, scrollHeight, clientHeight } = target ;
119+
120+ if ( scrollTop <= lastScrollTop . current ) {
121+ lastScrollTop . current = scrollTop ;
122+ return ;
123+ }
124+ lastScrollTop . current = scrollTop ;
125+
126+ const isNearBottom = scrollTop + clientHeight >= scrollHeight - 100 ;
127+ if ( isNearBottom ) {
128+ loadMore ( ) ;
129+ }
130+ } ,
131+ [ loadMore ] ,
132+ ) ;
60133
61134 useEffect ( ( ) => {
62135 if ( ! selectedModel ) {
@@ -96,7 +169,7 @@ export default function ModelList() {
96169 }
97170 } }
98171 >
99- < BudWraperBox >
172+ < BudWraperBox onScroll = { handleScroll } >
100173 < BudDrawerLayout >
101174 < DrawerTitleCard
102175 title = { `Select a Model from ${ selectedProvider ?. name } ` }
@@ -108,6 +181,7 @@ export default function ModelList() {
108181 setSelectedModel = { setSelectedModel }
109182 selectedModel = { selectedModel }
110183 hideSeeMore
184+ isLoadingMore = { isLoadingMore }
111185 >
112186 < ModelFilter
113187 search = { search }
0 commit comments