Skip to content

feat: start archiving new pages as the user starts to visit them with archiving enabled#19

Merged
nikitalokhmachev-ai merged 2 commits intomainfrom
nikita-sidepanel-archiving-sync
May 9, 2025
Merged

feat: start archiving new pages as the user starts to visit them with archiving enabled#19
nikitalokhmachev-ai merged 2 commits intomainfrom
nikita-sidepanel-archiving-sync

Conversation

@nikitalokhmachev-ai
Copy link
Collaborator

@nikitalokhmachev-ai nikitalokhmachev-ai commented May 8, 2025

User description

This PR solves issue #14 and the bug mentioned by @Shrinks99 .
Now, the "Resume Archiving" and "Pause Archiving" buttons are always visible due to the new behavior, allowing the user to switch between tabs and record them without closing the side panel.


PR Type

Bug fix


Description

  • Fix sidepanel archiving sync across browser tabs

  • Enable recording on newly activated tabs

  • Properly handle tab navigation with recording enabled

  • Maintain recording state across tab changes


Changes walkthrough 📝

Relevant files
Bug fix
bg.ts
Implement cross-tab archiving synchronization                       

src/ext/bg.ts

  • Added isRecordingEnabled flag to track recording state globally
  • Modified startRecording to work with active tabs instead of just the
    sidepanel tab
  • Refactored stopRecording to stop recording on all tabs
  • Added tab activation listener to start recording on newly activated
    tabs
  • Enhanced URL change handling to start recording on new valid URLs
  • +67/-12 

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @pr-agent-monadical
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    🎫 Ticket compliance analysis 🔶

    14 - Partially compliant

    Compliant requirements:

    • Fix bug where "Resume Archiving" targets the wrong tab
    • Improve UX by removing the need to manually reopen the side panel to start a new recording session

    Non-compliant requirements:

    • Fix unresponsive "Resume Archiving" or "Pause Archiving" button when a tab being archived is closed

    Requires further human verification:

    • The fix for unresponsive buttons when a tab is closed may be implemented but needs manual testing to verify

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Error Handling

    The code doesn't handle potential errors when starting recorders on tabs, especially in the onActivated and onUpdated listeners. Consider adding try/catch blocks.

    chrome.tabs.onActivated.addListener(async ({ tabId }) => {
      if (!isRecordingEnabled) return;
    
      // @ts-expect-error - chrome doesn't have type definitions
      const tab = await new Promise<chrome.tabs.Tab>((resolve) => chrome.tabs.get(tabId, resolve));
    
      if (!isValidUrl(tab.url)) return;
    
      if (!self.recorders[tabId]) {
        // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
        startRecorder(tabId, { collId: defaultCollId, port: null, autorun }, tab.url);
      }
    });
    Race Condition

    Potential race condition when multiple tabs are active and the startRecording message is received, as it iterates through tabs sequentially with async operations.

    chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
      for (const tab of tabs) {
        if (!isValidUrl(tab.url)) continue;
    
        // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
        await startRecorder(tab.id, { collId: defaultCollId, port: null, autorun }, tab.url);
      }

    Comment on lines 162 to +175
    case "startRecording": {
    const { collId, autorun } = message;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, port, autorun }, message.url);
    isRecordingEnabled = true;
    defaultCollId = message.collId;
    autorun = message.autorun;

    // @ts-expect-error - tabs doesn't have type definitions
    chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
    for (const tab of tabs) {
    if (!isValidUrl(tab.url)) continue;

    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    await startRecorder(tab.id, { collId: defaultCollId, port: null, autorun }, tab.url);
    }

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Suggestion: The startRecorder function is being called with port: null, but the original code passed the actual port. This could break communication with the recorder. Ensure the port is properly passed to maintain functionality. [possible issue, importance: 8]

    Suggested change
    case "startRecording": {
    const { collId, autorun } = message;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, port, autorun }, message.url);
    isRecordingEnabled = true;
    defaultCollId = message.collId;
    autorun = message.autorun;
    // @ts-expect-error - tabs doesn't have type definitions
    chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
    for (const tab of tabs) {
    if (!isValidUrl(tab.url)) continue;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    await startRecorder(tab.id, { collId: defaultCollId, port: null, autorun }, tab.url);
    }
    case "startRecording": {
    isRecordingEnabled = true;
    defaultCollId = message.collId;
    autorun = message.autorun;
    // @ts-expect-error - tabs doesn't have type definitions
    chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
    for (const tab of tabs) {
    if (!isValidUrl(tab.url)) continue;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    await startRecorder(tab.id, { collId: defaultCollId, port, autorun }, tab.url);
    }

    Comment on lines +240 to +252
    // @ts-expect-error - TS7006 - Parameter 'tab' implicitly has an 'any' type.
    chrome.tabs.onActivated.addListener(async ({ tabId }) => {
    if (!isRecordingEnabled) return;

    // @ts-expect-error - chrome doesn't have type definitions
    const tab = await new Promise<chrome.tabs.Tab>((resolve) => chrome.tabs.get(tabId, resolve));

    if (!isValidUrl(tab.url)) return;

    if (!self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, port: null, autorun }, tab.url);
    }

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Suggestion: The startRecorder function is called with port: null when a tab is activated. This could lead to communication issues. Consider handling the case where the recorder needs to communicate back to the UI. [possible issue, importance: 3]

    Suggested change
    // @ts-expect-error - TS7006 - Parameter 'tab' implicitly has an 'any' type.
    chrome.tabs.onActivated.addListener(async ({ tabId }) => {
    if (!isRecordingEnabled) return;
    // @ts-expect-error - chrome doesn't have type definitions
    const tab = await new Promise<chrome.tabs.Tab>((resolve) => chrome.tabs.get(tabId, resolve));
    if (!isValidUrl(tab.url)) return;
    if (!self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, port: null, autorun }, tab.url);
    }
    // @ts-expect-error - TS7006 - Parameter 'tab' implicitly has an 'any' type.
    chrome.tabs.onActivated.addListener(async ({ tabId }) => {
    if (!isRecordingEnabled) return;
    // @ts-expect-error - chrome doesn't have type definitions
    const tab = await new Promise<chrome.tabs.Tab>((resolve) => chrome.tabs.get(tabId, resolve));
    if (!isValidUrl(tab.url)) return;
    if (!self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, port: null, autorun }, tab.url);
    // Notify any connected ports about the new recorder if needed
    }
    });

    Comment on lines +330 to 344
    } else if (changeInfo.url) {
    if (isRecordingEnabled && isValidUrl(changeInfo.url) && !self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, autorun }, changeInfo.url);
    return;
    }
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, autorun }, changeInfo.url);
    if (openWinMap.has(changeInfo.url)) {
    const collId = openWinMap.get(changeInfo.url);
    openWinMap.delete(changeInfo.url);
    if (!tabId || !isValidUrl(changeInfo.url)) return;

    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, autorun }, changeInfo.url);
    }
    }

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Suggestion: The port parameter is missing in both startRecorder calls in the URL change handler. This is inconsistent with the original implementation and might cause issues with recorder communication. [possible issue, importance: 7]

    Suggested change
    } else if (changeInfo.url) {
    if (isRecordingEnabled && isValidUrl(changeInfo.url) && !self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, autorun }, changeInfo.url);
    return;
    }
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, autorun }, changeInfo.url);
    if (openWinMap.has(changeInfo.url)) {
    const collId = openWinMap.get(changeInfo.url);
    openWinMap.delete(changeInfo.url);
    if (!tabId || !isValidUrl(changeInfo.url)) return;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, autorun }, changeInfo.url);
    }
    }
    } else if (changeInfo.url) {
    if (isRecordingEnabled && isValidUrl(changeInfo.url) && !self.recorders[tabId]) {
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId: defaultCollId, port: null, autorun }, changeInfo.url);
    return;
    }
    if (openWinMap.has(changeInfo.url)) {
    const collId = openWinMap.get(changeInfo.url);
    openWinMap.delete(changeInfo.url);
    if (!tabId || !isValidUrl(changeInfo.url)) return;
    // @ts-expect-error - TS2554 - Expected 2 arguments, but got 3.
    startRecorder(tabId, { collId, port: null, autorun }, changeInfo.url);
    }
    }

    @Shrinks99 Shrinks99 linked an issue May 9, 2025 that may be closed by this pull request
    Copy link
    Collaborator

    @Shrinks99 Shrinks99 left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Great! Looks good :)

    @nikitalokhmachev-ai nikitalokhmachev-ai merged commit 7ad3e34 into main May 9, 2025
    1 of 4 checks passed
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    Sidepanel Archiving Sync Bug Across Tabs

    3 participants