mirror of
https://github.com/pionxzh/chatgpt-exporter.git
synced 2026-07-23 09:00:51 -05:00
fix: stream conversation pages progressively to unblock Export All UI
With 400+ conversations the old code made 4+ sequential requests and only rendered anything after all of them completed, freezing the dialog for 10+ seconds with no interactable elements. - Add optional `onBatch` callback to `fetchAllConversations` that fires after each page arrives, so callers can react incrementally - Wire `onBatch` in ExportDialog to append each batch to state as it lands — first 100 conversations appear almost instantly - Remove `loading` from the `disabled` guard so the user can select and export already-loaded conversations while remaining pages still fetch - Show "Loading…" only when the list is empty (initial blank state); replace with a subtle italic sentinel at the bottom of the list while subsequent pages are still in flight - Allow the project selector to be changed at any time during loading Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -548,7 +548,7 @@ async function fetchProjectConversations(project: string, cursor: string | numbe
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchAllConversations(project: string | null = null, maxConversations = 1000): Promise<ApiConversationItem[]> {
|
||||
export async function fetchAllConversations(project: string | null = null, maxConversations = 1000, onBatch?: (batch: ApiConversationItem[]) => void): Promise<ApiConversationItem[]> {
|
||||
const conversations: ApiConversationItem[] = []
|
||||
const limit = project === null ? 100 : 50 // gizmos api uses a smaller limit
|
||||
let offset = 0
|
||||
@@ -565,6 +565,7 @@ export async function fetchAllConversations(project: string | null = null, maxCo
|
||||
}
|
||||
conversations.push(...result.items)
|
||||
if (result.items.length === 0) break
|
||||
onBatch?.(result.items)
|
||||
// Stop if the API signals no more pages (no total count and no next cursor)
|
||||
if (result.total == null && result.cursor == null) break
|
||||
// Stop if we've reached the total reported by the API OR the user-defined limit
|
||||
|
||||
@@ -77,10 +77,9 @@ const ConversationSelect: FC<ConversationSelectProps> = ({
|
||||
/>
|
||||
</div>
|
||||
<ul className="SelectList">
|
||||
{loading && <li className="SelectItem">{t('Loading')}...</li>}
|
||||
{loading && conversations.length === 0 && <li className="SelectItem">{t('Loading')}...</li>}
|
||||
{error && <li className="SelectItem">{t('Error')}: {error}</li>}
|
||||
{!loading && !error
|
||||
&& conversations.map(c => (
|
||||
{conversations.map(c => (
|
||||
<li className="SelectItem" key={c.id}>
|
||||
<CheckBox
|
||||
label={c.title}
|
||||
@@ -95,6 +94,9 @@ const ConversationSelect: FC<ConversationSelectProps> = ({
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
{loading && conversations.length > 0 && (
|
||||
<li className="SelectItem" style={{ opacity: 0.5, fontStyle: 'italic' }}>{t('Loading')}...</li>
|
||||
)}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
@@ -131,7 +133,7 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
|
||||
|
||||
const [selected, setSelected] = useState<ApiConversationItem[]>([])
|
||||
const [exportType, setExportType] = useState(exportAllOptions[0].label)
|
||||
const disabled = loading || processing || !!error || selected.length === 0
|
||||
const disabled = processing || !!error || selected.length === 0
|
||||
|
||||
const requestQueue = useMemo(() => new RequestQueue<ApiConversationWithId>(200, 1600), [])
|
||||
const archiveQueue = useMemo(() => new RequestQueue<boolean>(200, 1600), [])
|
||||
@@ -295,9 +297,13 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setApiConversations([])
|
||||
setLoading(true)
|
||||
fetchAllConversations(selectedProject?.id, exportAllLimit)
|
||||
.then(setApiConversations)
|
||||
fetchAllConversations(
|
||||
selectedProject?.id,
|
||||
exportAllLimit,
|
||||
batch => setApiConversations(prev => [...prev, ...batch]),
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error('Error fetching conversations:', err)
|
||||
setError(err.message || 'Failed to load conversations')
|
||||
@@ -328,7 +334,7 @@ const DialogContent: FC<DialogContentProps> = ({ format }) => {
|
||||
{t('Export from API')}
|
||||
</div>
|
||||
)}
|
||||
<ProjectSelect projects={projects} selected={selectedProject} setSelected={setSelectedProject} disabled={processing || loading} />
|
||||
<ProjectSelect projects={projects} selected={selectedProject} setSelected={setSelectedProject} disabled={processing} />
|
||||
<ConversationSelect
|
||||
conversations={conversations}
|
||||
selected={selected}
|
||||
|
||||
Reference in New Issue
Block a user