javascriptreactjsgoogle-chrome-extensionbrowser

How to run code before closing side-panel?


I'm building a browser extension using react, vite, typescript. The extension has side-panel and fullscreen tabs. Only on fullscreeen tabs, I need to create a button/icon that appears only when side-panel is closed. So, when I open side-panel it hides the button and when I close side-panel it shows the button. I found a way to check if the side-panel is closed or open using this code.

const contexts: chrome.runtime.ExtensionContext[] = await chrome.runtime.getContexts({ contextTypes: ["SIDE_PANEL"] });

if contexts.length > 0 it means that side-panel is open, otherwise, it is closed. I added this logic in useEffect hook, and it works fine. The problem is that I need to run this code again every time side-panel opens or closes so it can check side-panel status and show/hide it if needed. I tried using focus and blur events but unfortunately it doesn't work in Chrome browser although it works in Edge. I come up with an idea that I can use chrome.runtime.sendMessage to trigger the code and run function again. In my main component index.tsx in useEffect I can send the message and it will trigger the code and hide the button. But then, I need to send the same message when side-panel closes. I tried to do it in useEffect return function but it didn't work for me. Is there any way to sendMessage when side-panel closes? Or maybe, there is better options to implement the logic of showing and hiding the icon? Full code of the button component is below

import { useEffect, useState } from "react";
import { logosHooks } from "@/hooks/logos-hooks";
import { cn } from "@/lib/utils";

const SidePanelTrigger = () => {
  const logo = logosHooks.useLogoIcon();
  const [show, setShow] = useState(false);

  useEffect(() => {
    const checkPanelState = async() => {
      const contexts: chrome.runtime.ExtensionContext[] = await chrome.runtime.getContexts({ contextTypes: ["SIDE_PANEL"] });
      setShow(contexts.length !== 0);
    }

    checkPanelState();
  }, []);

  const handleOpenSidepanel = () => {
    chrome.runtime.sendMessage({ action: "openSidePanel" });
  }
  
  return (
    <button 
      className={cn(
        "flex fixed top-[200px] z-10 -right-[10px] bg-primary rounded-l-sm justify-center items-center transition-all duration-300 hover:right-0 p-3 shadow-lg cursor-pointer",
        show && "hidden"
      )}
      onClick={handleOpenSidepanel}
    >
      Button
    </button>
  );
};

export default SidePanelTrigger;

Solution

  • I changed a little bit the code and managed to fix that.

    The solution that worked for me is using ResizeObserver. Every time side-panel opens or closes it changes the width of the active tab. I attached my checkPanelState function that checks if side-panel is closed or open to resizing event using ResizeObserver. I also added debounce to optimize the code. Full code of the component is below

    import { useEffect, useState } from "react";
    import { logosHooks } from "@/hooks/logos-hooks";
    import { cn, debounce } from "@/lib/utils";
    
    const SidePanelTrigger = () => {
      const logo = logosHooks.useLogoIcon();
      const [show, setShow] = useState(false);
    
      useEffect(() => {
        const checkPanelState = async() => {
          // @ts-ignore
          const contexts: chrome.runtime.ExtensionContext[] = await chrome.runtime.getContexts({ contextTypes: ["SIDE_PANEL"] });
          setShow(contexts.length === 0);
        }
    
        const debouncedCheck = debounce(checkPanelState, 50);
        const resizeObserver = new ResizeObserver(debouncedCheck);
    
        resizeObserver.observe(document.body);
    
        return () => resizeObserver.disconnect();
      }, []);
    
      const handleOpenSidepanel = () => {
        setShow(false)
        chrome.runtime.sendMessage({ action: "openSidePanel" });
      }
      
      return (
        <button 
          className={cn(
            "hidden fixed top-[200px] z-10 -right-[10px] bg-primary rounded-l-sm justify-center items-center transition-all duration-300 hover:right-0 p-3 shadow-lg cursor-pointer",
            show && "flex"
          )}
          onClick={handleOpenSidepanel}
        >
          Button
        </button>
      );
    };
    
    export default SidePanelTrigger;