mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-08 03:08:21 +00:00
fix(extension): keep X import status on initiating tab
This commit is contained in:
parent
e651045ac5
commit
63e0d1f705
3 changed files with 138 additions and 36 deletions
|
|
@ -15,6 +15,7 @@ import {
|
|||
type TwitterImportConfig,
|
||||
TwitterImporter,
|
||||
} from "../utils/twitter-import"
|
||||
import { createTwitterImportNotifications } from "../utils/twitter-import-notifications"
|
||||
import type {
|
||||
ExtensionMessage,
|
||||
MemoryData,
|
||||
|
|
@ -90,36 +91,6 @@ export default defineBackground(() => {
|
|||
["requestHeaders", "extraHeaders"],
|
||||
)
|
||||
|
||||
// Send message to current active tab.
|
||||
const sendMessageToCurrentTab = async (message: string) => {
|
||||
const tabs = await browser.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true,
|
||||
})
|
||||
if (tabs.length > 0 && tabs[0].id) {
|
||||
await browser.tabs.sendMessage(tabs[0].id, {
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send import completion message
|
||||
*/
|
||||
const sendImportDoneMessage = async (totalImported: number) => {
|
||||
const tabs = await browser.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true,
|
||||
})
|
||||
if (tabs.length > 0 && tabs[0].id) {
|
||||
await browser.tabs.sendMessage(tabs[0].id, {
|
||||
type: MESSAGE_TYPES.IMPORT_DONE,
|
||||
totalImported,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save memory to supermemory API
|
||||
*/
|
||||
|
|
@ -246,18 +217,21 @@ export default defineBackground(() => {
|
|||
* Handle extension messages
|
||||
*/
|
||||
browser.runtime.onMessage.addListener(
|
||||
(message: ExtensionMessage, _sender, sendResponse) => {
|
||||
(message: ExtensionMessage, sender, sendResponse) => {
|
||||
// Handle Twitter import request
|
||||
if (message.type === MESSAGE_TYPES.BATCH_IMPORT_ALL) {
|
||||
const notifications = createTwitterImportNotifications(
|
||||
sender.tab?.id,
|
||||
(tabId, notification) =>
|
||||
browser.tabs.sendMessage(tabId, notification),
|
||||
)
|
||||
const importConfig: TwitterImportConfig = {
|
||||
isFolderImport: message.isFolderImport,
|
||||
bookmarkCollectionId: message.bookmarkCollectionId,
|
||||
selectedProject: message.selectedProject,
|
||||
onProgress: sendMessageToCurrentTab,
|
||||
onComplete: sendImportDoneMessage,
|
||||
onError: async (error: Error) => {
|
||||
await sendMessageToCurrentTab(`Error: ${error.message}`)
|
||||
},
|
||||
onProgress: notifications.onProgress,
|
||||
onComplete: notifications.onComplete,
|
||||
onError: notifications.onError,
|
||||
}
|
||||
|
||||
twitterImporter = new TwitterImporter(importConfig)
|
||||
|
|
@ -351,3 +325,4 @@ export default defineBackground(() => {
|
|||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import { describe, expect, mock, test } from "bun:test"
|
||||
import { MESSAGE_TYPES } from "./constants"
|
||||
import { createTwitterImportNotifications } from "./twitter-import-notifications"
|
||||
|
||||
describe("Twitter import notifications", () => {
|
||||
test("keeps progress, errors, and completion on the initiating tab", async () => {
|
||||
let activeTabId = 7
|
||||
const sendMessage = mock(async () => {})
|
||||
const notifications = createTwitterImportNotifications(
|
||||
activeTabId,
|
||||
sendMessage,
|
||||
)
|
||||
|
||||
activeTabId = 42
|
||||
await notifications.onProgress("Imported 10 bookmarks")
|
||||
await notifications.onError(new Error("rate limited"))
|
||||
await notifications.onComplete(10)
|
||||
|
||||
expect(activeTabId).toBe(42)
|
||||
expect(sendMessage.mock.calls).toEqual([
|
||||
[
|
||||
7,
|
||||
{
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: "Imported 10 bookmarks",
|
||||
},
|
||||
],
|
||||
[
|
||||
7,
|
||||
{
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: "Error: rate limited",
|
||||
},
|
||||
],
|
||||
[7, { type: MESSAGE_TYPES.IMPORT_DONE, totalImported: 10 }],
|
||||
])
|
||||
})
|
||||
|
||||
test("does not let tab closure interrupt import callbacks", async () => {
|
||||
const sendMessage = mock(async () => {
|
||||
throw new Error("Receiving end does not exist")
|
||||
})
|
||||
const notifications = createTwitterImportNotifications(7, sendMessage)
|
||||
|
||||
await notifications.onProgress("Retrying")
|
||||
await notifications.onError(new Error("failed"))
|
||||
await notifications.onComplete(0)
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
test("accepts tab id zero", async () => {
|
||||
const sendMessage = mock(async () => {})
|
||||
const notifications = createTwitterImportNotifications(0, sendMessage)
|
||||
|
||||
await notifications.onProgress("Starting")
|
||||
|
||||
expect(sendMessage).toHaveBeenCalledWith(0, {
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: "Starting",
|
||||
})
|
||||
})
|
||||
|
||||
test("skips notifications when the request has no sender tab", async () => {
|
||||
const sendMessage = mock(async () => {})
|
||||
const notifications = createTwitterImportNotifications(
|
||||
undefined,
|
||||
sendMessage,
|
||||
)
|
||||
|
||||
await notifications.onProgress("Starting")
|
||||
await notifications.onComplete(0)
|
||||
|
||||
expect(sendMessage).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
50
apps/browser-extension/utils/twitter-import-notifications.ts
Normal file
50
apps/browser-extension/utils/twitter-import-notifications.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { MESSAGE_TYPES } from "./constants"
|
||||
|
||||
type TwitterImportNotification =
|
||||
| {
|
||||
type: typeof MESSAGE_TYPES.IMPORT_UPDATE
|
||||
importedMessage: string
|
||||
}
|
||||
| {
|
||||
type: typeof MESSAGE_TYPES.IMPORT_DONE
|
||||
totalImported: number
|
||||
}
|
||||
|
||||
type SendTabMessage = (
|
||||
tabId: number,
|
||||
message: TwitterImportNotification,
|
||||
) => Promise<unknown>
|
||||
|
||||
export function createTwitterImportNotifications(
|
||||
tabId: number | undefined,
|
||||
sendMessage: SendTabMessage,
|
||||
) {
|
||||
const deliver = async (message: TwitterImportNotification): Promise<void> => {
|
||||
if (tabId === undefined) return
|
||||
try {
|
||||
await sendMessage(tabId, message)
|
||||
} catch {
|
||||
// The initiating tab can be closed or navigated while the import keeps
|
||||
// running. Notification delivery must not cancel the import itself.
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
onProgress: (message: string) =>
|
||||
deliver({
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: message,
|
||||
}),
|
||||
onComplete: (totalImported: number) =>
|
||||
deliver({
|
||||
type: MESSAGE_TYPES.IMPORT_DONE,
|
||||
totalImported,
|
||||
}),
|
||||
onError: (error: Error) =>
|
||||
deliver({
|
||||
type: MESSAGE_TYPES.IMPORT_UPDATE,
|
||||
importedMessage: `Error: ${error.message}`,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue