Skip to content

Pls work #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 67 additions & 48 deletions src/app/containers/ErrorContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,31 @@ function ErrorContainer(props: ErrorContainerProps): JSX.Element {
(state: RootState) => state.main,
);

// Helper function to check if a URL is localhost
const isLocalhost = (url: string): boolean => {
return url.startsWith('http://localhost:') || url.startsWith('https://localhost:');
};

// Add effect to initialize currentTab if not set
useEffect(() => {
const initializeCurrentTab = async () => {
if (!currentTab) {
try {
// Query for the active tab
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (activeTab?.id) {
dispatch(setTab(activeTab.id));
// Query specifically for localhost tabs first
const tabs = await chrome.tabs.query({ currentWindow: true });
const localhostTab = tabs.find(tab => tab.url && isLocalhost(tab.url));

if (localhostTab?.id) {
dispatch(setTab(localhostTab.id));
} else {
// Fallback to active tab if no localhost found
const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (activeTab?.id) {
dispatch(setTab(activeTab.id));
}
}
} catch (error) {
console.error('Error getting active tab:', error);
console.error('Error getting tab:', error);
}
}
};
Expand All @@ -30,52 +43,58 @@ function ErrorContainer(props: ErrorContainerProps): JSX.Element {
}, [currentTab, dispatch]);

// function that launches the main app and refreshes the page
function launch(): void {
// Add validation to ensure we have valid data
if (!currentTab) {
console.warn('No current tab available - attempting to get active tab');
// Try to get the active tab when launching
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0]?.id) {
const activeTabId = tabs[0].id;
dispatch(setTab(activeTabId));
// Create default payload and launch
const defaultPayload = {
status: {
contentScriptLaunched: false,
reactDevToolsInstalled: false,
targetPageisaReactApp: false,
},
};
dispatch(launchContentScript(defaultPayload));
// Allow the dispatch to complete before refreshing
setTimeout(() => {
chrome.tabs.reload(activeTabId);
}, 100);
async function launch(): Promise<void> {
try {
// If no current tab, try to find localhost tab first
if (!currentTab) {
const tabs = await chrome.tabs.query({ currentWindow: true });
const localhostTab = tabs.find(tab => tab.url && isLocalhost(tab.url));

if (localhostTab?.id) {
dispatch(setTab(localhostTab.id));
await initializeLaunch(localhostTab.id);
} else {
console.warn('No localhost tab found');
}
});
return;
}
return;
}

if (!tabs || !tabs[currentTab]) {
// If no tab data exists, create a minimal valid payload
const defaultPayload = {
status: {
contentScriptLaunched: false,
reactDevToolsInstalled: false,
targetPageisaReactApp: false,
},
};
dispatch(launchContentScript(defaultPayload));
} else {
dispatch(launchContentScript(tabs[currentTab]));
// Verify current tab is still localhost
const tab = await chrome.tabs.get(currentTab);
if (!tab.url || !isLocalhost(tab.url)) {
// Try to find a localhost tab
const tabs = await chrome.tabs.query({ currentWindow: true });
const localhostTab = tabs.find(tab => tab.url && isLocalhost(tab.url));

if (localhostTab?.id) {
dispatch(setTab(localhostTab.id));
await initializeLaunch(localhostTab.id);
} else {
console.warn('No localhost tab found');
}
return;
}

await initializeLaunch(currentTab);
} catch (error) {
console.error('Error during launch:', error);
}
}

async function initializeLaunch(tabId: number): Promise<void> {
const defaultPayload = {
status: {
contentScriptLaunched: false,
reactDevToolsInstalled: false,
targetPageisaReactApp: false,
},
};

dispatch(launchContentScript(defaultPayload));

// Allow the dispatch to complete before refreshing
setTimeout(() => {
if (currentTab) {
chrome.tabs.reload(currentTab);
}
chrome.tabs.reload(tabId);
}, 100);
}

Expand All @@ -96,8 +115,8 @@ function ErrorContainer(props: ErrorContainerProps): JSX.Element {
connect with your app and start monitoring state changes.
</p>
<p className='error-description'>
Important: Reactime requires React Developer Tools to be installed. If you haven't
already, please{' '}
Important: Reactime requires React Developer Tools to be installed and will only track state
changes on localhost development servers. If you haven't already, please{' '}
<a
href='https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en'
target='_blank'
Expand Down Expand Up @@ -134,4 +153,4 @@ function ErrorContainer(props: ErrorContainerProps): JSX.Element {
);
}

export default ErrorContainer;
export default ErrorContainer;
150 changes: 118 additions & 32 deletions src/extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,65 @@ let activeTab;
const tabsObj = {};
// Will store Chrome web vital metrics and their corresponding values.
const metrics = {};

// Helper function to check if a URL is localhost
function isLocalhost(url) {
return url?.startsWith('http://localhost:') || url?.startsWith('https://localhost:');
}

// Helper function to find localhost tab
async function findLocalhostTab() {
const tabs = await chrome.tabs.query({ currentWindow: true });
return tabs.find((tab) => tab.url && isLocalhost(tab.url));
}

//keep alive functionality to address port disconnection issues
function setupKeepAlive() {
//ellie
// Create an alarm that triggers every 4.9 minutes (under the 5-minute limit)
// Clear any existing keep-alive alarms to prevent duplicates
chrome.alarms.clear('keepAlive', (wasCleared) => {
if (wasCleared) {
console.log('Cleared existing keep-alive alarm.');
}
});

// Create a new keep-alive alarm, we found .5 min to resolve the idle time port disconnection
chrome.alarms.create('keepAlive', { periodInMinutes: 0.5 });

// Log active alarms for debugging
chrome.alarms.getAll((alarms) => {
console.log(
'Active alarms:',
alarms.map((alarm) => alarm.name),
);
});

// Listen for the keep-alive alarm
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'keepAlive') {
console.log('Keep-alive alarm triggered.');
pingServiceWorker();
}
});
}

// Ping the service worker to keep it alive
function pingServiceWorker() {
// Use a lightweight API call to keep the service worker active
chrome.runtime.getPlatformInfo(() => {
console.log('Service worker pinged successfully');
});
try {
chrome.runtime.getPlatformInfo(() => {
console.log('Service worker pinged successfully.');
});
} catch (error) {
console.error('Failed to ping service worker:', error);

// Fallback: Trigger an empty event to wake up the service worker
chrome.runtime.sendMessage({ type: 'ping' }, (response) => {
if (chrome.runtime.lastError) {
console.error('Fallback message failed:', chrome.runtime.lastError.message);
} else {
console.log('Fallback message sent successfully:', response);
}
});
}
}

// function pruning the chrome ax tree and pulling the relevant properties
Expand Down Expand Up @@ -281,15 +322,24 @@ function changeCurrLocation(tabObj, rootNode, index, name) {
}

async function getActiveTab() {
return new Promise((resolve, reject) => {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
if (tabs.length > 0) {
resolve(tabs[0].id);
} else {
reject(new Error('No active tab'));
}
});
});
try {
// First try to find a localhost tab
const localhostTab = await findLocalhostTab();
if (localhostTab) {
return localhostTab.id;
}

// Fallback to current active tab
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (tabs.length > 0) {
return tabs[0].id;
}

throw new Error('No active tab');
} catch (error) {
console.error('Error in getActiveTab:', error);
throw error;
}
}

/*
Expand Down Expand Up @@ -366,7 +416,7 @@ chrome.runtime.onConnect.addListener(async (port) => {
});
}

// Handles port disconnection by removing the disconnected port -ellie
// Handles port disconnection by removing the disconnected port
port.onDisconnect.addListener(() => {
const index = portsArr.indexOf(port);
if (index !== -1) {
Expand Down Expand Up @@ -656,7 +706,7 @@ chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
break;
}

// DUPLICATE SNAPSHOT CHECK -ellie
// DUPLICATE SNAPSHOT CHECK
const isDuplicateSnapshot = (previous, incoming) => {
if (!previous || !incoming) return false;
const prevData = previous?.componentData;
Expand Down Expand Up @@ -772,26 +822,62 @@ chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
});

// When tab view is changed, put the tabId as the current tab
chrome.tabs.onActivated.addListener((info) => {
chrome.tabs.onActivated.addListener(async (info) => {
// Get info about the tab information from tabId
chrome.tabs.get(info.tabId, (tab) => {
// Never set a reactime instance to the active tab
if (!tab.pendingUrl?.match('^chrome-extension')) {
activeTab = tab;

// Send messages to active ports about the tab change
if (portsArr.length > 0) {
portsArr.forEach((bg) =>
bg.postMessage({
action: 'changeTab',
payload: { tabId: tab.id, title: tab.title },
}),
);
try {
const tab = await chrome.tabs.get(info.tabId);

// Only update activeTab if:
// 1. It's not a Reactime extension tab
// 2. We don't already have a localhost tab being tracked
// 3. Or if it is a localhost tab (prioritize localhost)
if (!tab.url?.match('^chrome-extension')) {
if (isLocalhost(tab.url)) {
// Always prioritize localhost tabs
activeTab = tab;
if (portsArr.length > 0) {
portsArr.forEach((bg) =>
bg.postMessage({
action: 'changeTab',
payload: { tabId: tab.id, title: tab.title },
}),
);
}
} else if (!activeTab || !isLocalhost(activeTab.url)) {
// Only set non-localhost tab as active if we don't have a localhost tab
activeTab = tab;
if (portsArr.length > 0) {
portsArr.forEach((bg) =>
bg.postMessage({
action: 'changeTab',
payload: { tabId: tab.id, title: tab.title },
}),
);
}
}
}
});
} catch (error) {
console.error('Error in tab activation handler:', error);
}
});

// Ensure keep-alive is set up when the extension is installed
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed. Setting up keep-alive...');
setupKeepAlive();
});

// Ensure keep-alive is set up when the browser starts
chrome.runtime.onStartup.addListener(() => {
console.log('Browser started. Setting up keep-alive...');
setupKeepAlive();
});

// Optional: Reset keep-alive when a message is received (to cover edge cases)
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message === 'resetKeepAlive') {
console.log('Resetting keep-alive as requested.');
setupKeepAlive();
sendResponse({ success: true });
}
});