mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
feat(web): show import status on X bookmarks integration card (#1130)
## What
The X bookmarks ("Import X bookmarks") card on the integrations page never reflected any state, even after importing tweets — unlike connectors and plugins, which show "Connected"/"Active".
After this change, once you've imported at least one tweet the card shows an **"Imported · {last import time}"** pill (mirroring the plugin "Active · {time}" style) and is included in the **Connected** filter.
Since importing X bookmarks is a one-time/occasional action rather than a live connection, it intentionally says **"Imported"** with the last-import timestamp, not "Connected".
## Why
The page's status logic (`isItemConnected` + `renderStatus`) only handled the `plugin` and `connector` item kinds. The X bookmarks card is an `import` kind, so it always rendered just "Connect". This was a regression from #979 (the integrations overhaul), which rebuilt the page around item kinds and dropped the previous "{N} tweets imported" indicator.
## How
- Add a single documents query (`@post/documents/documents`, `categories: ["tweet"]`, `limit: 1`, newest first) that yields both the org-wide tweet count (`pagination.totalItems`) and the latest tweet's `createdAt`.
- `isItemConnected` returns `true` for `import` when the count is `> 0`.
- `renderStatus` renders a new `ImportedPill` ("Imported · {relative time}") for `import` when there's ≥1 tweet.
## Testing
- `tsc` and Biome pass; no new errors introduced by this change.
- Verified the status source of truth against a local backend by seeding `type='tweet'` documents — the count/latest-timestamp the pill renders reflect them.
> Note: `tsc` reports two **pre-existing**, unrelated `granola` errors in this file (`CONNECTOR_META` + an icon record missing the `granola` provider). They exist on `main` and are not touched by this PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
parent
9017a1b5d4
commit
313a8df6a6
1 changed files with 60 additions and 1 deletions
|
|
@ -1059,6 +1059,29 @@ function ConnectionsCountPill({ count }: { count: number }) {
|
|||
)
|
||||
}
|
||||
|
||||
function ImportedPill({
|
||||
lastImportedAt,
|
||||
}: {
|
||||
lastImportedAt: string | number | Date | null
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
dmSans125ClassName(),
|
||||
"flex shrink-0 items-center gap-1.5 text-[12px] font-medium text-[#00AC3F] sm:text-[13px]",
|
||||
)}
|
||||
>
|
||||
<span className="size-[7px] rounded-full bg-[#00AC3F]" />
|
||||
Imported
|
||||
{lastImportedAt && (
|
||||
<span className="text-[11px] font-normal text-[#737373]">
|
||||
· {formatRelativeTime(lastImportedAt)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const CONNECTOR_META: Record<
|
||||
ConnectorProvider,
|
||||
{ name: string; icon: ReactNode; documentLabel: string }
|
||||
|
|
@ -2463,6 +2486,35 @@ export function IntegrationsView({
|
|||
staleTime: 30 * 1000,
|
||||
})
|
||||
|
||||
const { data: xBookmarksImport } = useQuery({
|
||||
queryKey: ["x-bookmarks-import-status"],
|
||||
queryFn: async () => {
|
||||
const response = await $fetch("@post/documents/documents", {
|
||||
body: {
|
||||
page: 1,
|
||||
limit: 1,
|
||||
sort: "createdAt",
|
||||
order: "desc",
|
||||
categories: ["tweet"],
|
||||
},
|
||||
disableValidation: true,
|
||||
})
|
||||
if (response.error)
|
||||
throw new Error(
|
||||
response.error?.message || "Failed to load X bookmarks status",
|
||||
)
|
||||
return {
|
||||
count: response.data?.pagination?.totalItems ?? 0,
|
||||
lastImportedAt: response.data?.documents?.[0]?.createdAt ?? null,
|
||||
}
|
||||
},
|
||||
staleTime: 5 * 60 * 1000,
|
||||
enabled: !publicMode,
|
||||
})
|
||||
|
||||
const tweetCount = xBookmarksImport?.count ?? 0
|
||||
const lastTweetImportAt = xBookmarksImport?.lastImportedAt ?? null
|
||||
|
||||
const keyPrefix = useCallback((key: ListedApiKey): string | null => {
|
||||
return key.start ?? (key.name?.startsWith("sm_") ? key.name : null)
|
||||
}, [])
|
||||
|
|
@ -2675,9 +2727,12 @@ export function IntegrationsView({
|
|||
if (item.kind === "connector") {
|
||||
return connectionsByProvider[item.provider].length > 0
|
||||
}
|
||||
if (item.kind === "import") {
|
||||
return tweetCount > 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
[activePluginById, connectionsByProvider, publicMode],
|
||||
[activePluginById, connectionsByProvider, publicMode, tweetCount],
|
||||
)
|
||||
|
||||
const counts = useMemo<Record<CategoryFilter, number>>(
|
||||
|
|
@ -3286,6 +3341,10 @@ export function IntegrationsView({
|
|||
if (count <= 0) return null
|
||||
return <ConnectionsCountPill count={count} />
|
||||
}
|
||||
case "import": {
|
||||
if (tweetCount <= 0) return null
|
||||
return <ImportedPill lastImportedAt={lastTweetImportAt} />
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue