feat: enhance indexing state management and inbox count formatting

- Improved indexing state management by refining the logic for handling notifications, ensuring accurate updates for in-progress, completed, and failed states.
- Introduced a new utility function to format inbox counts, displaying numbers up to 999 and using "k+" for larger counts, enhancing user interface clarity.
- Updated sidebar components to utilize the new inbox count formatting, improving the overall user experience.
This commit is contained in:
Anish Sarkar 2026-01-24 01:20:51 +05:30
parent c48ba36fa4
commit 6d14b49d3f
4 changed files with 75 additions and 44 deletions

View file

@ -10,8 +10,9 @@ import { isConnectorIndexingMetadata } from "@/contracts/types/inbox.types";
*
* This provides a better UX than polling by:
* 1. Setting indexing state immediately when user triggers indexing (optimistic)
* 2. Clearing indexing state when Electric SQL detects last_indexed_at changed
* 3. Clearing indexing state when a failed notification is detected
* 2. Detecting in_progress notifications from Electric SQL to restore state after remounts
* 3. Clearing indexing state when notifications become completed or failed
* 4. Clearing indexing state when Electric SQL detects last_indexed_at changed
*
* The actual `last_indexed_at` value comes from Electric SQL/PGlite, not local state.
*/
@ -28,65 +29,73 @@ export function useIndexingConnectors(
// Detect when last_indexed_at changes (indexing completed) via Electric SQL
useEffect(() => {
const previousValues = previousLastIndexedAtRef.current;
const newIndexingIds = new Set(indexingConnectorIds);
let hasChanges = false;
for (const connector of connectors) {
const previousValue = previousValues.get(connector.id);
const currentValue = connector.last_indexed_at;
// If last_indexed_at changed and connector was in indexing state, clear it
// If last_indexed_at changed, clear it from indexing state
if (
previousValue !== undefined && // We've seen this connector before
previousValue !== currentValue && // Value changed
indexingConnectorIds.has(connector.id) // It was marked as indexing
previousValue !== currentValue // Value changed
) {
newIndexingIds.delete(connector.id);
hasChanges = true;
// Use functional update to access current state
setIndexingConnectorIds((prev) => {
if (prev.has(connector.id)) {
const next = new Set(prev);
next.delete(connector.id);
return next;
}
return prev;
});
}
// Update previous value tracking
previousValues.set(connector.id, currentValue);
}
}, [connectors]);
if (hasChanges) {
setIndexingConnectorIds(newIndexingIds);
}
}, [connectors, indexingConnectorIds]);
// Detect failed notifications and stop indexing state
// Detect notification status changes and update indexing state accordingly
// This restores spinner state after component remounts and handles all status transitions
useEffect(() => {
if (!inboxItems || inboxItems.length === 0) return;
const newIndexingIds = new Set(indexingConnectorIds);
let hasChanges = false;
setIndexingConnectorIds((prev) => {
const newIndexingIds = new Set(prev);
let hasChanges = false;
for (const item of inboxItems) {
// Only check connector_indexing notifications
if (item.type !== "connector_indexing") continue;
for (const item of inboxItems) {
// Only check connector_indexing notifications
if (item.type !== "connector_indexing") continue;
// Check if this notification indicates a failure
const metadata = isConnectorIndexingMetadata(item.metadata)
? item.metadata
: null;
if (!metadata) continue;
const metadata = isConnectorIndexingMetadata(item.metadata)
? item.metadata
: null;
if (!metadata) continue;
// Check if status is "failed" or if there's an error_message
const isFailed =
metadata.status === "failed" ||
(metadata.error_message && metadata.error_message.trim().length > 0);
// If failed and connector is in indexing state, clear it
if (isFailed && indexingConnectorIds.has(metadata.connector_id)) {
newIndexingIds.delete(metadata.connector_id);
hasChanges = true;
// If status is "in_progress", add connector to indexing set
if (metadata.status === "in_progress") {
if (!newIndexingIds.has(metadata.connector_id)) {
newIndexingIds.add(metadata.connector_id);
hasChanges = true;
}
}
// If status is "completed" or "failed", remove connector from indexing set
else if (
metadata.status === "completed" ||
metadata.status === "failed" ||
(metadata.error_message && metadata.error_message.trim().length > 0)
) {
if (newIndexingIds.has(metadata.connector_id)) {
newIndexingIds.delete(metadata.connector_id);
hasChanges = true;
}
}
}
}
if (hasChanges) {
setIndexingConnectorIds(newIndexingIds);
}
}, [inboxItems, indexingConnectorIds]);
return hasChanges ? newIndexingIds : prev;
});
}, [inboxItems]);
// Add a connector to the indexing set (called when indexing starts)
const startIndexing = useCallback((connectorId: number) => {