Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
Lots of changes to get TabCloser working in Chrome, Edge, Arc, and.... SAFARI!
  • Loading branch information
sethcottle committed Jul 14, 2024
1 parent 9b33bf2 commit 3e3962a
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 142 deletions.
105 changes: 29 additions & 76 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
// TabCloser is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please see the
// GNU General Public License for more details.
// GNU General Public License for more details.

const debug = true; // Set to true for debugging
const debug = false; // Set to true for debugging

const predefinedUrlPatterns = [
{ label: 'Asana', pattern: '^https?://app\\.asana\\.com/-/desktop_app_link\\?.*' },
Expand All @@ -34,99 +34,52 @@ async function shouldCloseTab(url) {
const { disabledUrls = [], customUrls = [] } = await chrome.storage.sync.get(['disabledUrls', 'customUrls']);

// Check predefined patterns
let matchedPattern = null;
const shouldCloseDefault = predefinedUrlPatterns.some(({ pattern, label }) => {
const regex = new RegExp(pattern, 'i');
if (regex.test(url) && !disabledUrls.includes(pattern)) {
matchedPattern = label;
if (debug) console.log(`Should close (default): true (matched: ${label})`);
return true;
}
return false;
});

// Check custom URLs (exact literal match)
const matchedCustomUrl = customUrls.find(({ url: customUrl, enabled }) => {
if (!enabled) return false;
return url === customUrl; // Exact, case-sensitive match
const shouldCloseCustom = customUrls.some(({ url: customUrl, enabled }) => {
if (enabled && url === customUrl) {
if (debug) console.log(`Should close (custom): true (matched: ${customUrl})`);
return true;
}
return false;
});

const shouldCloseCustom = !!matchedCustomUrl;

if (debug) {
console.log(`Checking URL: ${url}`);
if (shouldCloseDefault) {
console.log(`Should close (default): true (matched: ${matchedPattern})`);
} else if (shouldCloseCustom) {
console.log(`Should close (custom): true (matched: ${matchedCustomUrl.url})`);
} else {
console.log(`URL does not match any closing patterns`);
}
if (debug && !shouldCloseDefault && !shouldCloseCustom) {
console.log(`URL does not match any closing patterns: ${url}`);
}

return shouldCloseDefault || shouldCloseCustom;
}

async function checkAndCloseTabs() {
const tabs = await chrome.tabs.query({});
const { interval = 15 } = await chrome.storage.sync.get(['interval']);

if (debug) {
console.log(`Checking ${tabs.length} tabs`);
console.log(`Closure interval: ${interval} seconds`);
}

for (const tab of tabs) {
const shouldClose = await shouldCloseTab(tab.url);
if (shouldClose) {
if (debug) {
console.log(`Attempting to close tab: ${tab.url}`);
}
try {
await new Promise((resolve) => {
setTimeout(async () => {
try {
// Check if the tab still exists before attempting to close it
const tabExists = await chrome.tabs.get(tab.id).catch(() => null);
if (tabExists) {
await chrome.tabs.remove(tab.id);
if (debug) {
console.log(`Successfully closed tab with id ${tab.id}`);
}
} else if (debug) {
console.log(`Tab with id ${tab.id} no longer exists`);
}
} catch (error) {
console.error(`Error closing tab with id ${tab.id}:`, error.message);
}
resolve();
}, interval * 1000);
});
} catch (error) {
console.error(`Unexpected error handling tab with id ${tab.id}:`, error.message);
}
async function checkAndCloseTab(tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
if (debug) console.log(`Tab updated: ${tab.url}`);
const { interval = 15 } = await chrome.storage.sync.get(['interval']);

if (await shouldCloseTab(tab.url)) {
if (debug) console.log(`Scheduling tab for closure: ${tab.url}`);
setTimeout(async () => {
try {
await chrome.tabs.remove(tabId);
if (debug) console.log(`Closed tab: ${tab.url}`);
} catch (error) {
if (debug) console.error(`Error closing tab ${tab.url}: ${error.message}`);
}
}, interval * 1000);
}
}
}

// Run tab closer periodically
chrome.alarms.create('runTabCloser', { periodInMinutes: 5 });

chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'runTabCloser') {
if (debug) {
console.log('Running tab closer alarm');
}
checkAndCloseTabs();
}
});

// Listen for new tab creation
chrome.tabs.onCreated.addListener(() => {
if (debug) {
console.log('New tab created, scheduling check');
}
setTimeout(() => checkAndCloseTabs(), 10000);
});
// Listen for tab updates
chrome.tabs.onUpdated.addListener(checkAndCloseTab);

// Service Worker initialization
chrome.runtime.onInstalled.addListener(() => {
Expand All @@ -140,4 +93,4 @@ if (debug) {
console.log('Storage changes:', changes);
}
});
}
}
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "TabCloser",
"version": "3.0.0",
"version": "3.0.1",
"description": "Automatically close leftover tabs from services like Figma, Spotify, Zoom and other commonly redirected URLs.",
"action": {
"default_popup": "popup.html",
Expand Down
89 changes: 58 additions & 31 deletions options.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

<!--
<!--
Copyright (C) 2023-2024 Seth Cottle
Expand All @@ -12,7 +12,7 @@
TabCloser is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please see the
GNU General Public License for more details.
GNU General Public License for more details.
-->

Expand All @@ -34,6 +34,7 @@
--button-text: #ffffff;
--checkbox-bg: #e0e0e0;
--checkbox-border: #b0b0b0;
--svg-color: #333333;
}

@media (prefers-color-scheme: dark) {
Expand All @@ -44,6 +45,7 @@
--input-bg: #2a2a2a;
--checkbox-bg: #444444;
--checkbox-border: #666666;
--svg-color: #f0f0f0;
}
}

Expand Down Expand Up @@ -165,27 +167,55 @@
}

.remove-btn {
background: none;
border: none;
cursor: pointer;
padding: 5px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.7;
transition: opacity 0.3s ease;
}

.remove-btn:hover {
opacity: 1;
background: none;
}

.remove-btn svg {
width: 18px;
height: 18px;
fill: currentColor;
}
background: none !important;
border: none !important;
cursor: pointer;
font-size: 18px;
padding: 5px;
opacity: 0.7;
transition: all 0.3s ease;
color: inherit !important;
box-shadow: none !important;
}

.custom-url-remove-btn.remove-btn {
background: none !important;
border: none !important;
cursor: pointer;
font-size: 18px;
padding: 5px;
opacity: 0.7;
transition: all 0.3s ease;
color: inherit !important;
box-shadow: none !important;
}

.custom-url-remove-btn.remove-btn:hover {
opacity: 1;
color: #ff3333 !important;
background: none !important;
}

/* Ensure contrast in light theme */
@media (prefers-color-scheme: light) {
.custom-url-remove-btn.remove-btn {
filter: contrast(0.5);
}
.custom-url-remove-btn.remove-btn:hover {
filter: none;
}
}

/* Ensure contrast in dark theme */
@media (prefers-color-scheme: dark) {
.custom-url-remove-btn.remove-btn {
filter: brightness(1.5);
}
.custom-url-remove-btn.remove-btn:hover {
filter: none;
}
}


#check-interval {
margin-bottom: 20px;
Expand Down Expand Up @@ -262,7 +292,7 @@
<body class="popup-body">
<h1>TabCloser</h1>

<h2>Default Options</h2>
<h2>Default Services</h2>
<div id="default-options"></div>

<h2>Custom URLs</h2>
Expand All @@ -272,22 +302,19 @@ <h2>Custom URLs</h2>
</form>
<ul id="custom-url-list"></ul>

<h2>Closure Settings</h2>
<h2>Close Time</h2>
<div>
<label for="check-interval">Close Time (seconds):</label>
<input type="number" id="check-interval" min="1" value="15">
<br>
<a href="https://cottle.notion.site/Getting-Started-2959666010e64deabad75efa0aaeb814?pvs=25" target="_blank">Learn About Closure Settings</a>
<input type="number" id="check-interval" min="1" value="15"> <label for="check-interval"> Seconds
</div>

<h2>Support TabCloser</h2>
<p>
<a href="https://www.buymeacoffee.com/seth" target="_blank">Buy Me A Coffee</a> |
<a href="https://www.buymeacoffee.com/seth" target="_blank">Buy Me A Coffee</a> |
<a href="https://tabcloser.com" target="_blank">TabCloser.com</a>
</p>
<p>Made By <a href="https://seth.social" target="_blank">Seth Cottle</a></p>

<script src="options.js"></script>
</body>

</html>
</html>
8 changes: 2 additions & 6 deletions options.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,8 @@ function renderCustomUrls() {
li.appendChild(toggleSwitch);

const removeBtn = document.createElement('button');
removeBtn.className = 'remove-btn';
removeBtn.innerHTML = `
<svg viewBox="0 0 24 24">
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/>
</svg>
`;
removeBtn.className = 'remove-btn custom-url-remove-btn'; // Updated class
removeBtn.textContent = '🗑️'; // Unicode trash can symbol
removeBtn.setAttribute('aria-label', 'Remove URL');
removeBtn.onclick = () => removeCustomUrl(index);
li.appendChild(removeBtn);
Expand Down
Loading

0 comments on commit 3e3962a

Please sign in to comment.