From 7274d25d6ab6b0774364d5947259fdd1e01a6bf8 Mon Sep 17 00:00:00 2001 From: tconfrey Date: Tue, 20 Feb 2024 16:13:45 -0500 Subject: [PATCH] fixed a couple of bugs - race condition w tabLeftTG vs tabClosed, and card editor not activating save. Also re-indented background.js which caused a big diff --- app/bt.js | 2 +- extension/background.js | 321 +++++++++--------- versions/Release-Candidate/app/bt.js | 2 +- .../extension/_locales/en/messages.json | 2 +- .../Release-Candidate/extension/background.js | 321 +++++++++--------- versions/Release-Candidate/extension/popup.js | 2 +- 6 files changed, 328 insertions(+), 322 deletions(-) diff --git a/app/bt.js b/app/bt.js index c3dae47..383d10c 100644 --- a/app/bt.js +++ b/app/bt.js @@ -1430,7 +1430,7 @@ function editRow(e) { $(".editNode").on('input', function() { // enable update button if one of the texts is edited and title is not empty - if ($("#topicName").val()) return; + if (!$("#topicName").val()) return; $("#update").prop('disabled', false); }); diff --git a/extension/background.js b/extension/background.js index dbf5343..71d3507 100644 --- a/extension/background.js +++ b/extension/background.js @@ -1,10 +1,10 @@ /*** - * - * Main entry point for all window and tab manipulation. Listens for messages from app - * (relayed by content script) and dispatches to handler. Also listens for updates from - * browser (tabs opened etc) and relays back to app for processing. - * - ***/ +* +* Main entry point for all window and tab manipulation. Listens for messages from app +* (relayed by content script) and dispatches to handler. Also listens for updates from +* browser (tabs opened etc) and relays back to app for processing. +* +***/ 'use strict'; @@ -41,17 +41,17 @@ function check(msg='') { /* Document data kept in storage.local */ const storageKeys = ["BTFileText", // golden source of BT .org text data - "TabAction", // remember popup default action - "currentTabId", - "currentTag", // for setting badge text - "currentText", - "mruTopics", // mru items used to default mru topic in popup - "newInstall", // true/false, for popup display choice - "newVersion", // used for popup to indicate an update to user - "permissions", // perms granted - "ManagerHome", // open in Panel or Tab - "ManagerLocation", // {top, left, width, height} of panel - "tags"]; // used for popup display +"TabAction", // remember popup default action +"currentTabId", +"currentTag", // for setting badge text +"currentText", +"mruTopics", // mru items used to default mru topic in popup +"newInstall", // true/false, for popup display choice +"newVersion", // used for popup to indicate an update to user +"permissions", // perms granted +"ManagerHome", // open in Panel or Tab +"ManagerLocation", // {top, left, width, height} of panel +"tags"]; // used for popup display chrome.runtime.onUpdateAvailable.addListener(deets => { // Handle update. Store version so popup can inform and then upgrade @@ -61,7 +61,7 @@ chrome.runtime.onInstalled.addListener(deets => { // special handling for first install or new version if (deets.reason == 'install') { InitialInstall = chrome.runtime.getManifest().version; // let app know version - chrome.storage.local.set({'newInstall' : true}); + chrome.storage.local.set({'newInstall' : true}); chrome.tabs.create({'url': "https://braintool.org/support/welcome"}); } if (deets.reason == 'update') { @@ -69,7 +69,7 @@ chrome.runtime.onInstalled.addListener(deets => { chrome.storage.local.get(null, (items) => { Object.keys(items).forEach((key) => { if (!storageKeys.includes(key)) - chrome.storage.local.remove(key); + chrome.storage.local.remove(key); }); }); UpdateInstall = deets.previousVersion; @@ -77,11 +77,11 @@ chrome.runtime.onInstalled.addListener(deets => { }); /*** - * - * Message handling. Handlers dispatched based on msg.function - * NB need explicit mapping, evaluating from string is blocked for security reasons - * - ***/ +* +* Message handling. Handlers dispatched based on msg.function +* NB need explicit mapping, evaluating from string is blocked for security reasons +* +***/ const Handlers = { "initializeExtension": initializeExtension, "openTabs": openTabs, @@ -95,12 +95,12 @@ const Handlers = { "moveOpenTabsToTG": moveOpenTabsToTG, "updateGroup": updateGroup, "saveTabs": saveTabs, -// "importSession": importSession + // "importSession": importSession }; chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.from != 'btwindow' && msg.from != 'popup') return; - + // NB workaround for bug in Chrome, see https://stackoverflow.com/questions/71520198/manifestv3-new-promise-error-the-message-port-closed-before-a-response-was-rece/71520415#71520415 sendResponse(); @@ -113,7 +113,7 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.function == 'getBookmarks' || msg.function == 'exportBookmarks') { // request bookmark permission prior to bookmark operations // NB not using the dispatch cos that loses that its user triggered and Chrome prevents - + if (LocalTest) { getBookmarks(); return; } @@ -124,14 +124,14 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { } else { // send back denial btSendMessage(sender.tab.id, {'function': 'loadBookmarks', - 'result': 'denied'}); + 'result': 'denied'}); } }); - return; - } - if (msg.type == 'LOCALTEST') { - // Running under test so there is no external BT top level window - chrome.tabs.query({'url' : '*://localhost/test*'}, tabs => { + return; + } + if (msg.type == 'LOCALTEST') { + // Running under test so there is no external BT top level window + chrome.tabs.query({'url' : '*://localhost/test*'}, tabs => { check(); LocalTest = true; }); @@ -142,10 +142,10 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { /*** - * - * Event handling for browser events of interest - * - ***/ +* +* Event handling for browser events of interest +* +***/ chrome.tabs.onAttached.addListener(async (tabId, otherInfo) => { // listen for tab event @@ -165,7 +165,7 @@ chrome.tabs.onMoved.addListener(async (tabId, otherInfo) => { 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indicies, 'tab': tab}); setTimeout(function() {setBadge(tabId);}, 200); }); - + chrome.tabs.onRemoved.addListener(async (tabId, otherInfo) => { // listen for tabs being closed and let BT know const [BTTab, BTWin] = await getBTTabWin(); @@ -179,7 +179,7 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { const [BTTab, BTWin] = await getBTTabWin(); if (PauseTabEventsDuringTGMove) return; // ignore tab events for a few seconds after TG is dragged creating a new window if (!tabId || !BTTab || (tabId == BTTab)) return; // not set up yet or don't care - + if (changeInfo.status == 'complete') { // tab navigated to/from url btSendMessage( @@ -190,12 +190,15 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { } if (changeInfo.groupId && (tab.status == 'complete') && tab.url) { // tab moved to/from TG, wait til loaded so url etc is filled in - const indices = await tabIndices(); - btSendMessage( - BTTab, {'function': (tab.groupId > 0) ? 'tabJoinedTG' : 'tabLeftTG', - 'tabId': tabId, 'groupId': tab.groupId, - 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indices, - 'tab': tab}); + // Adding a delay to allow potential tab closed event to be processed first, otherwise tabLeftTG deletes BT Node + setTimeout(async () => { + const indices = await tabIndices(); + btSendMessage( + BTTab, {'function': (tab.groupId > 0) ? 'tabJoinedTG' : 'tabLeftTG', + 'tabId': tabId, 'groupId': tab.groupId, + 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indices, + 'tab': tab}); + }, 100); setTimeout(function() {setBadge(tabId);}, 200); } }); @@ -206,10 +209,10 @@ chrome.tabs.onActivated.addListener(async (info) => { const [BTTab, BTWin] = await getBTTabWin(); if (!info.tabId || !BTTab) return; console.log(`tabs.onActiviated fired, info: [${JSON.stringify(info)}]`); - chrome.tabs.get(info.tabId, tab => { + chrome.tabs.get(info.tabId, tab => { if (!tab) return; btSendMessage(BTTab, {'function': 'tabActivated', 'tabId': info.tabId, - 'windowId': tab.windowId, 'groupId': tab.groupId}); + 'windowId': tab.windowId, 'groupId': tab.groupId}); setTimeout(function() {setBadge(info.tabId);}, 250); }); }); @@ -219,13 +222,13 @@ chrome.tabGroups.onCreated.addListener(async (tg) => { const [BTTab, BTWin] = await getBTTabWin(); if (!BTTab) return; // not set up yet or don't care btSendMessage(BTTab, {'function': 'tabGroupCreated', 'tabGroupId': tg.id, - 'tabGroupColor': tg.color}); + 'tabGroupColor': tg.color}); }); var NewWindowID = 0; var PauseTabEventsDuringTGMove = false; chrome.windows.onCreated.addListener(async (win) => { - // When a TG is dragged out of its window a new one is created followed by a cascade of tab events leaving and rejoining the TG. + // When a TG is dragged out of its window a new one is created followed by a cascade of tab events leaving and rejoining the TG. // Its too complex to track all these events so we just ignore TG events for a few seconds after a new window is created. console.log('window created:', win); NewWindowID = win.id; @@ -237,15 +240,15 @@ chrome.tabGroups.onUpdated.addListener(async (tg) => { if (NewWindowID && (NewWindowID == tg.windowId)) { // ignore TG updates in btSendMessage for a few seconds cos tabs get disconnected and reconnected console.log('Pausing TG events for 5 seconds'); - PauseTabEventsDuringTGMove = true; + PauseTabEventsDuringTGMove = true; setTimeout(() => PauseTabEventsDuringTGMove = false, 5000); } const [BTTab, BTWin] = await getBTTabWin(); if (!BTTab) return; // not set up yet or don't care btSendMessage(BTTab, {'function': 'tabGroupUpdated', 'tabGroupId': tg.id, - 'tabGroupColor': tg.color, 'tabGroupName': tg.title, - 'tabGroupCollapsed': tg.collapsed, - 'tabGroupWindowId': tg.windowId}); + 'tabGroupColor': tg.color, 'tabGroupName': tg.title, + 'tabGroupCollapsed': tg.collapsed, + 'tabGroupWindowId': tg.windowId}); }); chrome.tabGroups.onRemoved.addListener(async (tg) => { @@ -261,7 +264,7 @@ chrome.windows.onFocusChanged.addListener(async (windowId) => { // don't care about special windows like dev tools or the BT win check(); const [BTTab, BTWin] = await getBTTabWin(); - if (!BTTab || windowId <= 0 || windowId == BTWin) return; + if (!BTTab || windowId <= 0 || windowId == BTWin) return; chrome.tabs.query({'active': true, 'windowId': windowId},tabs => { check(); if (!tabs.length) return; @@ -280,12 +283,12 @@ chrome.windows.onBoundsChanged.addListener(async (window) => { // listen for connect and immediate disconnect => open BT panel chrome.runtime.onConnect.addListener(async (port) => { - + const [BTTab, BTWin] = await getBTTabWin(); const connectTime = Date.now(); port.onDisconnect.addListener(() => { const disconnectTime = Date.now(); - if (!BTWin) return; // might have been closed + if (!BTWin) return; // might have been closed if ((disconnectTime - connectTime) < 500) chrome.windows.update(BTWin, {'focused': true}, () => { check(); @@ -313,28 +316,28 @@ async function tabOpened(winId, tabId, nodeId, index, tgId = 0) { const [BTTab, BTWin] = await getBTTabWin(); check(); btSendMessage(BTTab, - {'function': 'tabOpened', 'nodeId': nodeId, 'tabIndex': index, - 'tabId': tabId, 'windowId': winId, 'tabGroupId': tgId}); + {'function': 'tabOpened', 'nodeId': nodeId, 'tabIndex': index, + 'tabId': tabId, 'windowId': winId, 'tabGroupId': tgId}); setTimeout(function() {setBadge(tabId);}, 250); } function getOpenTabs() { // return an array of [{winId:, tabId:, groupId:, url:}..] via promise - + return new Promise(resolve => { - let allTabs = []; - chrome.tabs.query({}, (tabs) => { - tabs.forEach((tab) => - allTabs.push({'id': tab.id, - 'groupId': tab.groupId, - 'windowId': tab.windowId, - 'tabIndex' : tab.index, - 'title': tab.title, - 'pinned': tab.pinned, - 'faviconUrl': tab.favIconUrl, - 'url': tab.url})); - resolve(allTabs); - }); + let allTabs = []; + chrome.tabs.query({}, (tabs) => { + tabs.forEach((tab) => + allTabs.push({'id': tab.id, + 'groupId': tab.groupId, + 'windowId': tab.windowId, + 'tabIndex' : tab.index, + 'title': tab.title, + 'pinned': tab.pinned, + 'faviconUrl': tab.favIconUrl, + 'url': tab.url})); + resolve(allTabs); + }); }); } @@ -355,15 +358,15 @@ async function initializeExtension(msg, sender) { let allTabs = await getOpenTabs(); let allTGs = await getOpenTabGroups(); - + // send over gdrive app info - btSendMessage( + btSendMessage( BTTab, {'function': 'launchApp', 'client_id': Keys.CLIENT_ID, - 'api_key': Keys.API_KEY, 'fb_key': Keys.FB_KEY, - 'stripe_key': Keys.STRIPE_KEY, + 'api_key': Keys.API_KEY, 'fb_key': Keys.FB_KEY, + 'stripe_key': Keys.STRIPE_KEY, 'initial_install': InitialInstall, 'upgrade_install': UpdateInstall, - 'all_tabs': allTabs, 'all_tgs': allTGs}); + 'all_tabs': allTabs, 'all_tgs': allTGs}); // check to see if a welcome is called for. repeat popup setting on bt win for safety. if (InitialInstall || UpdateInstall) { @@ -371,11 +374,11 @@ async function initializeExtension(msg, sender) { 'https://braintool.org/support/welcome' : 'https://braintool.org/support/releaseNotes'; chrome.tabs.create({'url': welcomePage}, - () => { - chrome.windows.update(BTWin, - {'focused' : true}, - () => check()); - }); + () => { + chrome.windows.update(BTWin, + {'focused' : true}, + () => check()); + }); InitialInstall = null; UpdateInstall = null; } updateBTIcon('', 'BrainTool', '#59718C'); // was #5E954E @@ -388,15 +391,15 @@ function suspendExtension() { chrome.storage.local.set({'BTTab': 0, 'BTWin': 0}); updateBTIcon('', 'BrainTool is not running.\nClick to start', '#e57f21'); chrome.action.setIcon({'path': 'images/BrainToolGray.png'}); - + chrome.tabs.query({'currentWindow': true, 'active': true}, (tabs) => { - if (!tabs.length || !tabs[0].id) return; // sometimes theres no active tab - const tabId = tabs[0].id; - setTimeout(() => { - // wait for updateBTIcon to finish then show 'OFF' on top tab for 3 secs - chrome.action.setBadgeText({'text' : 'OFF', 'tabId': tabId}); - setTimeout(() => chrome.action.setBadgeText({'text' : '', 'tabId': tabId}), 3000); - }, 500); + if (!tabs.length || !tabs[0].id) return; // sometimes theres no active tab + const tabId = tabs[0].id; + setTimeout(() => { + // wait for updateBTIcon to finish then show 'OFF' on top tab for 3 secs + chrome.action.setBadgeText({'text' : 'OFF', 'tabId': tabId}); + setTimeout(() => chrome.action.setBadgeText({'text' : '', 'tabId': tabId}), 3000); + }, 500); }); } @@ -405,29 +408,29 @@ function updateBTIcon(text, title, color) { // set for each tab to override previous tab-specific setting chrome.tabs.query({}, (tabs) => - { - tabs.forEach((tab) => { - chrome.action.setBadgeText( - {'text' : text, 'tabId': tab.id}, () => check()); - chrome.action.setTitle( - {'title' : title, 'tabId': tab.id}); - chrome.action.setBadgeBackgroundColor( - {'color' : color, 'tabId': tab.id}); - }); - }); + { + tabs.forEach((tab) => { + chrome.action.setBadgeText( + {'text' : text, 'tabId': tab.id}, () => check()); + chrome.action.setTitle( + {'title' : title, 'tabId': tab.id}); + chrome.action.setBadgeBackgroundColor( + {'color' : color, 'tabId': tab.id}); + }); + }); // set across all tabs - chrome.action.setBadgeText( - {'text' : text}, () => check()); - chrome.action.setTitle( - {'title' : title}); - chrome.action.setBadgeBackgroundColor( - {'color' : color}); + chrome.action.setBadgeText( + {'text' : text}, () => check()); + chrome.action.setTitle( + {'title' : title}); + chrome.action.setBadgeBackgroundColor( + {'color' : color}); } function openTabs(msg, sender) { // open list of {nodeId, url} pairs, potentially in new window - + function openTabsInWin(tabInfo, winId = null) { // open [{url, nodeId}]s in tab in given window tabInfo.forEach((tabData) => { @@ -443,7 +446,7 @@ function openTabs(msg, sender) { const newWin = msg.newWin; const defaultWinId = msg.defaultWinId; // 0 or winId of siblings const [first, ...rest] = msg.tabs; - + if (newWin) // Create new win w first url, then iterate on rest chrome.windows.create({'url': first.url}, win => { @@ -465,7 +468,7 @@ function openTabs(msg, sender) { } function openTabGroups(msg, sender) { - // open tabs in specified or new tab group, potentially in new window + // open tabs in specified or new tab group, potentially in new window const tabGroups = msg.tabGroups; // [{tg, win, tgname[{id, url}]},..] const newWinNeeded = msg.newWin; @@ -484,7 +487,7 @@ function openTabGroups(msg, sender) { }); }); } - + tabGroups.forEach(tg => { // handle a {windowId, tabGroupId, groupName, 'tabGroupTabs': [{nodeId, url}]} instance const[first, ...rest] = tg.tabGroupTabs; @@ -498,7 +501,7 @@ function openTabGroups(msg, sender) { check(); tabOpened(win.id, win.tabs[0].id, first.nodeId, win.tabs[0].index, tgid); - chrome.tabGroups.update(tgid, {'title' : groupName}); + chrome.tabGroups.update(tgid, {'title' : groupName}); openTabsInTg(win.id, tgid, rest); }); }); @@ -515,7 +518,7 @@ function openTabGroups(msg, sender) { check(); tabOpened(tab.windowId, tab.id, first.nodeId, tab.index, tgid); - chrome.tabGroups.update(tgid, {'title' : groupName}); + chrome.tabGroups.update(tgid, {'title' : groupName}); openTabsInTg(tab.windowId, tgid, rest); }); }); @@ -554,11 +557,11 @@ function groupAndPositionTabs(msg, sender) { const nodeInfo = tabInfo.find(ti => ti.tabId == t.id); btSendMessage( sender.tab.id, {'function': 'tabPositioned', 'tabId': t.id, - 'nodeId': nodeInfo.nodeId, 'tabGroupId': groupId, - 'windowId': t.windowId, 'tabIndex': t.index}); + 'nodeId': nodeInfo.nodeId, 'tabGroupId': groupId, + 'windowId': t.windowId, 'tabIndex': t.index}); }); }); - }); + }); } function ungroup(msg, sender) { @@ -582,9 +585,9 @@ function moveOpenTabsToTG(msg, sender) { btSendMessage( sender.tab.id, {'tabId': tid, 'groupId': tgId, - 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndicies': indices, + 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndicies': indices, 'tab': tab}); - // was {'function': 'tabGrouped', 'tgId': tgId, 'tabId': tid, 'tabIndex': tab.index}); + // was {'function': 'tabGrouped', 'tgId': tgId, 'tabId': tid, 'tabIndex': tab.index}); }); }); }); @@ -642,9 +645,9 @@ function setBadge(tabId) { function marquee(badgeText, index) { if (badgeText.length < 6 || index >= badgeText.length - 2) { chrome.action.setBadgeText({'text' : badgeText, 'tabId': tabId}, () => check('marquee')); - } else { + } else { chrome.action.setBadgeText({'text' : badgeText.slice(index) + " ", - 'tabId': tabId}, () => check('marquee')); + 'tabId': tabId}, () => check('marquee')); MarqueeEvent = setTimeout(function() {marquee(badgeText, ++index);}, 150); } } @@ -652,7 +655,7 @@ function setBadge(tabId) { chrome.storage.local.get(['currentTag', 'currentText'], function(data) { if (!data.currentTag) { chrome.action.setBadgeText({'text' : "", 'tabId' : tabId}, - () => check('Resetting badge text:')); + () => check('Resetting badge text:')); chrome.action.setTitle({'title' : 'BrainTool'}); } else { marquee(data.currentTag, 0); @@ -672,7 +675,7 @@ function brainZoom(msg, sender, iteration = 0) { "48": "images/BrainTool48.png", "128": "images/BrainTool128.png" }; - + if (iteration == iterationArray.length) { chrome.action.setIcon({'path': default_icon, 'tabId': msg.tabId}); setTimeout(function() {setBadge(msg.tabId);}, 150); @@ -696,7 +699,7 @@ function getBookmarks() { itemTree[0].title = "Imported Bookmarks"; chrome.storage.local.set({'bookmarks': itemTree[0]}, function() { btSendMessage(BTTab, {'function': 'loadBookmarks', - 'result': 'success'}); + 'result': 'success'}); }); }); } @@ -728,43 +731,43 @@ function exportBookmarks() { } /* -async function importSession(msg, sender) { - // return hierarchy of all windows/tgs/tabs to enable a topic tree to be created in Mgr - - const [BTTab, BTWin] = await getBTTabWin(); - const allTabs = await getOpenTabs(); // array of tabs - const allTGs = await getOpenTabGroups(); // array of tgs - const allWins = {}, groups = {}; - - allTGs.forEach(tg => { // create hash - groups[tg.id] = tg; - tg.tabs = []; // and add array for tabs - }); - - // Loop thru tabs, create win objs and fill in TGs and tabs - allTabs.forEach(t => { - if ((t.id == BTTab) || t.pinned) return; - if (!allWins[t.windowId]) - allWins[t.windowId] = {'windowId': t.windowId, 'windowName': 'Window'+t.windowId, - 'tabs': [], 'tabGroups': {}}; - const win = allWins[t.windowId]; - if (t.groupId > 0) { - const tg = groups[t.groupId]; - if (!win.tabGroups[t.groupId]) - win.tabGroups[t.groupId] = tg; - tg.tabs.push(t); - } else { - win.tabs.push(t); - } - }); - btSendMessage(BTTab, {'function': 'importSession', 'windows': allWins, 'topic': msg.topic, 'close': msg.close}); - console.log('allWins = ', allWins); -} + async function importSession(msg, sender) { + // return hierarchy of all windows/tgs/tabs to enable a topic tree to be created in Mgr + + const [BTTab, BTWin] = await getBTTabWin(); + const allTabs = await getOpenTabs(); // array of tabs + const allTGs = await getOpenTabGroups(); // array of tgs + const allWins = {}, groups = {}; + + allTGs.forEach(tg => { // create hash + groups[tg.id] = tg; + tg.tabs = []; // and add array for tabs + }); + + // Loop thru tabs, create win objs and fill in TGs and tabs + allTabs.forEach(t => { + if ((t.id == BTTab) || t.pinned) return; + if (!allWins[t.windowId]) + allWins[t.windowId] = {'windowId': t.windowId, 'windowName': 'Window'+t.windowId, + 'tabs': [], 'tabGroups': {}}; + const win = allWins[t.windowId]; + if (t.groupId > 0) { + const tg = groups[t.groupId]; + if (!win.tabGroups[t.groupId]) + win.tabGroups[t.groupId] = tg; + tg.tabs.push(t); + } else { + win.tabs.push(t); + } + }); + btSendMessage(BTTab, {'function': 'importSession', 'windows': allWins, 'topic': msg.topic, 'close': msg.close}); + console.log('allWins = ', allWins); + } */ async function saveTabs(msg, sender) { - // handle save for popup. msg.type Could be Tab, TG, Window or Session. - // msg: {'close': 'topic', 'note', 'title', 'currentWindowId' } + // handle save for popup. msg.type Could be Tab, TG, Window or Session. + // msg: {'close','topic', 'note', 'title', 'currentWindowId' } // Create array of appropriate tab data and send to BT window const currentTabs = await chrome.tabs.query({'active': true, 'windowId': msg.currentWindowId}); @@ -777,13 +780,13 @@ async function saveTabs(msg, sender) { // Create a hash of TGIds to TG names const tgNames = {}; allTGs.forEach(tg => tgNames[tg.id] = tg.title); - + // Loop thru tabs, decide based on msg.type if it should be saved and if so add to array to send to BTTab const tabsToSave = []; allTabs.forEach(t => { if (t.id == BTTab || t.pinned) return; - const tab = {'tabId': t.id, 'groupId': t.groupId, 'windowId': t.windowId, 'url': t.url, - 'favIconUrl': t.faviconUrl, 'tabIndex': t.tabIndex, 'title': t.title}; + const tab = {'tabId': t.id, 'groupId': t.groupId, 'windowId': t.windowId, 'url': t.url, + 'favIconUrl': t.faviconUrl, 'tabIndex': t.tabIndex, 'title': t.title}; const tgName = tgNames[t.groupId] || ''; const [topic, todo] = msg.topic.split(':'); // topic might have trailing :TODO or :DONE. split it off if (saveType == 'Tab' && t.id == currentTab.id) { diff --git a/versions/Release-Candidate/app/bt.js b/versions/Release-Candidate/app/bt.js index c3dae47..383d10c 100644 --- a/versions/Release-Candidate/app/bt.js +++ b/versions/Release-Candidate/app/bt.js @@ -1430,7 +1430,7 @@ function editRow(e) { $(".editNode").on('input', function() { // enable update button if one of the texts is edited and title is not empty - if ($("#topicName").val()) return; + if (!$("#topicName").val()) return; $("#update").prop('disabled', false); }); diff --git a/versions/Release-Candidate/extension/_locales/en/messages.json b/versions/Release-Candidate/extension/_locales/en/messages.json index 701f2f6..f0bf275 100644 --- a/versions/Release-Candidate/extension/_locales/en/messages.json +++ b/versions/Release-Candidate/extension/_locales/en/messages.json @@ -4,7 +4,7 @@ "description": "The title of the application, displayed in the web store." }, "appDesc": { - "message": "BrainTool is the best Bookmark and Tabs Manager for your online life.", + "message": "BrainTool is the best Bookmark and Tabs Manager for your online life. This is the 1.0 Release Candidate", "description": "The description of the application, displayed in the web store." } } diff --git a/versions/Release-Candidate/extension/background.js b/versions/Release-Candidate/extension/background.js index dbf5343..71d3507 100644 --- a/versions/Release-Candidate/extension/background.js +++ b/versions/Release-Candidate/extension/background.js @@ -1,10 +1,10 @@ /*** - * - * Main entry point for all window and tab manipulation. Listens for messages from app - * (relayed by content script) and dispatches to handler. Also listens for updates from - * browser (tabs opened etc) and relays back to app for processing. - * - ***/ +* +* Main entry point for all window and tab manipulation. Listens for messages from app +* (relayed by content script) and dispatches to handler. Also listens for updates from +* browser (tabs opened etc) and relays back to app for processing. +* +***/ 'use strict'; @@ -41,17 +41,17 @@ function check(msg='') { /* Document data kept in storage.local */ const storageKeys = ["BTFileText", // golden source of BT .org text data - "TabAction", // remember popup default action - "currentTabId", - "currentTag", // for setting badge text - "currentText", - "mruTopics", // mru items used to default mru topic in popup - "newInstall", // true/false, for popup display choice - "newVersion", // used for popup to indicate an update to user - "permissions", // perms granted - "ManagerHome", // open in Panel or Tab - "ManagerLocation", // {top, left, width, height} of panel - "tags"]; // used for popup display +"TabAction", // remember popup default action +"currentTabId", +"currentTag", // for setting badge text +"currentText", +"mruTopics", // mru items used to default mru topic in popup +"newInstall", // true/false, for popup display choice +"newVersion", // used for popup to indicate an update to user +"permissions", // perms granted +"ManagerHome", // open in Panel or Tab +"ManagerLocation", // {top, left, width, height} of panel +"tags"]; // used for popup display chrome.runtime.onUpdateAvailable.addListener(deets => { // Handle update. Store version so popup can inform and then upgrade @@ -61,7 +61,7 @@ chrome.runtime.onInstalled.addListener(deets => { // special handling for first install or new version if (deets.reason == 'install') { InitialInstall = chrome.runtime.getManifest().version; // let app know version - chrome.storage.local.set({'newInstall' : true}); + chrome.storage.local.set({'newInstall' : true}); chrome.tabs.create({'url': "https://braintool.org/support/welcome"}); } if (deets.reason == 'update') { @@ -69,7 +69,7 @@ chrome.runtime.onInstalled.addListener(deets => { chrome.storage.local.get(null, (items) => { Object.keys(items).forEach((key) => { if (!storageKeys.includes(key)) - chrome.storage.local.remove(key); + chrome.storage.local.remove(key); }); }); UpdateInstall = deets.previousVersion; @@ -77,11 +77,11 @@ chrome.runtime.onInstalled.addListener(deets => { }); /*** - * - * Message handling. Handlers dispatched based on msg.function - * NB need explicit mapping, evaluating from string is blocked for security reasons - * - ***/ +* +* Message handling. Handlers dispatched based on msg.function +* NB need explicit mapping, evaluating from string is blocked for security reasons +* +***/ const Handlers = { "initializeExtension": initializeExtension, "openTabs": openTabs, @@ -95,12 +95,12 @@ const Handlers = { "moveOpenTabsToTG": moveOpenTabsToTG, "updateGroup": updateGroup, "saveTabs": saveTabs, -// "importSession": importSession + // "importSession": importSession }; chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.from != 'btwindow' && msg.from != 'popup') return; - + // NB workaround for bug in Chrome, see https://stackoverflow.com/questions/71520198/manifestv3-new-promise-error-the-message-port-closed-before-a-response-was-rece/71520415#71520415 sendResponse(); @@ -113,7 +113,7 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.function == 'getBookmarks' || msg.function == 'exportBookmarks') { // request bookmark permission prior to bookmark operations // NB not using the dispatch cos that loses that its user triggered and Chrome prevents - + if (LocalTest) { getBookmarks(); return; } @@ -124,14 +124,14 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { } else { // send back denial btSendMessage(sender.tab.id, {'function': 'loadBookmarks', - 'result': 'denied'}); + 'result': 'denied'}); } }); - return; - } - if (msg.type == 'LOCALTEST') { - // Running under test so there is no external BT top level window - chrome.tabs.query({'url' : '*://localhost/test*'}, tabs => { + return; + } + if (msg.type == 'LOCALTEST') { + // Running under test so there is no external BT top level window + chrome.tabs.query({'url' : '*://localhost/test*'}, tabs => { check(); LocalTest = true; }); @@ -142,10 +142,10 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { /*** - * - * Event handling for browser events of interest - * - ***/ +* +* Event handling for browser events of interest +* +***/ chrome.tabs.onAttached.addListener(async (tabId, otherInfo) => { // listen for tab event @@ -165,7 +165,7 @@ chrome.tabs.onMoved.addListener(async (tabId, otherInfo) => { 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indicies, 'tab': tab}); setTimeout(function() {setBadge(tabId);}, 200); }); - + chrome.tabs.onRemoved.addListener(async (tabId, otherInfo) => { // listen for tabs being closed and let BT know const [BTTab, BTWin] = await getBTTabWin(); @@ -179,7 +179,7 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { const [BTTab, BTWin] = await getBTTabWin(); if (PauseTabEventsDuringTGMove) return; // ignore tab events for a few seconds after TG is dragged creating a new window if (!tabId || !BTTab || (tabId == BTTab)) return; // not set up yet or don't care - + if (changeInfo.status == 'complete') { // tab navigated to/from url btSendMessage( @@ -190,12 +190,15 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { } if (changeInfo.groupId && (tab.status == 'complete') && tab.url) { // tab moved to/from TG, wait til loaded so url etc is filled in - const indices = await tabIndices(); - btSendMessage( - BTTab, {'function': (tab.groupId > 0) ? 'tabJoinedTG' : 'tabLeftTG', - 'tabId': tabId, 'groupId': tab.groupId, - 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indices, - 'tab': tab}); + // Adding a delay to allow potential tab closed event to be processed first, otherwise tabLeftTG deletes BT Node + setTimeout(async () => { + const indices = await tabIndices(); + btSendMessage( + BTTab, {'function': (tab.groupId > 0) ? 'tabJoinedTG' : 'tabLeftTG', + 'tabId': tabId, 'groupId': tab.groupId, + 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndices': indices, + 'tab': tab}); + }, 100); setTimeout(function() {setBadge(tabId);}, 200); } }); @@ -206,10 +209,10 @@ chrome.tabs.onActivated.addListener(async (info) => { const [BTTab, BTWin] = await getBTTabWin(); if (!info.tabId || !BTTab) return; console.log(`tabs.onActiviated fired, info: [${JSON.stringify(info)}]`); - chrome.tabs.get(info.tabId, tab => { + chrome.tabs.get(info.tabId, tab => { if (!tab) return; btSendMessage(BTTab, {'function': 'tabActivated', 'tabId': info.tabId, - 'windowId': tab.windowId, 'groupId': tab.groupId}); + 'windowId': tab.windowId, 'groupId': tab.groupId}); setTimeout(function() {setBadge(info.tabId);}, 250); }); }); @@ -219,13 +222,13 @@ chrome.tabGroups.onCreated.addListener(async (tg) => { const [BTTab, BTWin] = await getBTTabWin(); if (!BTTab) return; // not set up yet or don't care btSendMessage(BTTab, {'function': 'tabGroupCreated', 'tabGroupId': tg.id, - 'tabGroupColor': tg.color}); + 'tabGroupColor': tg.color}); }); var NewWindowID = 0; var PauseTabEventsDuringTGMove = false; chrome.windows.onCreated.addListener(async (win) => { - // When a TG is dragged out of its window a new one is created followed by a cascade of tab events leaving and rejoining the TG. + // When a TG is dragged out of its window a new one is created followed by a cascade of tab events leaving and rejoining the TG. // Its too complex to track all these events so we just ignore TG events for a few seconds after a new window is created. console.log('window created:', win); NewWindowID = win.id; @@ -237,15 +240,15 @@ chrome.tabGroups.onUpdated.addListener(async (tg) => { if (NewWindowID && (NewWindowID == tg.windowId)) { // ignore TG updates in btSendMessage for a few seconds cos tabs get disconnected and reconnected console.log('Pausing TG events for 5 seconds'); - PauseTabEventsDuringTGMove = true; + PauseTabEventsDuringTGMove = true; setTimeout(() => PauseTabEventsDuringTGMove = false, 5000); } const [BTTab, BTWin] = await getBTTabWin(); if (!BTTab) return; // not set up yet or don't care btSendMessage(BTTab, {'function': 'tabGroupUpdated', 'tabGroupId': tg.id, - 'tabGroupColor': tg.color, 'tabGroupName': tg.title, - 'tabGroupCollapsed': tg.collapsed, - 'tabGroupWindowId': tg.windowId}); + 'tabGroupColor': tg.color, 'tabGroupName': tg.title, + 'tabGroupCollapsed': tg.collapsed, + 'tabGroupWindowId': tg.windowId}); }); chrome.tabGroups.onRemoved.addListener(async (tg) => { @@ -261,7 +264,7 @@ chrome.windows.onFocusChanged.addListener(async (windowId) => { // don't care about special windows like dev tools or the BT win check(); const [BTTab, BTWin] = await getBTTabWin(); - if (!BTTab || windowId <= 0 || windowId == BTWin) return; + if (!BTTab || windowId <= 0 || windowId == BTWin) return; chrome.tabs.query({'active': true, 'windowId': windowId},tabs => { check(); if (!tabs.length) return; @@ -280,12 +283,12 @@ chrome.windows.onBoundsChanged.addListener(async (window) => { // listen for connect and immediate disconnect => open BT panel chrome.runtime.onConnect.addListener(async (port) => { - + const [BTTab, BTWin] = await getBTTabWin(); const connectTime = Date.now(); port.onDisconnect.addListener(() => { const disconnectTime = Date.now(); - if (!BTWin) return; // might have been closed + if (!BTWin) return; // might have been closed if ((disconnectTime - connectTime) < 500) chrome.windows.update(BTWin, {'focused': true}, () => { check(); @@ -313,28 +316,28 @@ async function tabOpened(winId, tabId, nodeId, index, tgId = 0) { const [BTTab, BTWin] = await getBTTabWin(); check(); btSendMessage(BTTab, - {'function': 'tabOpened', 'nodeId': nodeId, 'tabIndex': index, - 'tabId': tabId, 'windowId': winId, 'tabGroupId': tgId}); + {'function': 'tabOpened', 'nodeId': nodeId, 'tabIndex': index, + 'tabId': tabId, 'windowId': winId, 'tabGroupId': tgId}); setTimeout(function() {setBadge(tabId);}, 250); } function getOpenTabs() { // return an array of [{winId:, tabId:, groupId:, url:}..] via promise - + return new Promise(resolve => { - let allTabs = []; - chrome.tabs.query({}, (tabs) => { - tabs.forEach((tab) => - allTabs.push({'id': tab.id, - 'groupId': tab.groupId, - 'windowId': tab.windowId, - 'tabIndex' : tab.index, - 'title': tab.title, - 'pinned': tab.pinned, - 'faviconUrl': tab.favIconUrl, - 'url': tab.url})); - resolve(allTabs); - }); + let allTabs = []; + chrome.tabs.query({}, (tabs) => { + tabs.forEach((tab) => + allTabs.push({'id': tab.id, + 'groupId': tab.groupId, + 'windowId': tab.windowId, + 'tabIndex' : tab.index, + 'title': tab.title, + 'pinned': tab.pinned, + 'faviconUrl': tab.favIconUrl, + 'url': tab.url})); + resolve(allTabs); + }); }); } @@ -355,15 +358,15 @@ async function initializeExtension(msg, sender) { let allTabs = await getOpenTabs(); let allTGs = await getOpenTabGroups(); - + // send over gdrive app info - btSendMessage( + btSendMessage( BTTab, {'function': 'launchApp', 'client_id': Keys.CLIENT_ID, - 'api_key': Keys.API_KEY, 'fb_key': Keys.FB_KEY, - 'stripe_key': Keys.STRIPE_KEY, + 'api_key': Keys.API_KEY, 'fb_key': Keys.FB_KEY, + 'stripe_key': Keys.STRIPE_KEY, 'initial_install': InitialInstall, 'upgrade_install': UpdateInstall, - 'all_tabs': allTabs, 'all_tgs': allTGs}); + 'all_tabs': allTabs, 'all_tgs': allTGs}); // check to see if a welcome is called for. repeat popup setting on bt win for safety. if (InitialInstall || UpdateInstall) { @@ -371,11 +374,11 @@ async function initializeExtension(msg, sender) { 'https://braintool.org/support/welcome' : 'https://braintool.org/support/releaseNotes'; chrome.tabs.create({'url': welcomePage}, - () => { - chrome.windows.update(BTWin, - {'focused' : true}, - () => check()); - }); + () => { + chrome.windows.update(BTWin, + {'focused' : true}, + () => check()); + }); InitialInstall = null; UpdateInstall = null; } updateBTIcon('', 'BrainTool', '#59718C'); // was #5E954E @@ -388,15 +391,15 @@ function suspendExtension() { chrome.storage.local.set({'BTTab': 0, 'BTWin': 0}); updateBTIcon('', 'BrainTool is not running.\nClick to start', '#e57f21'); chrome.action.setIcon({'path': 'images/BrainToolGray.png'}); - + chrome.tabs.query({'currentWindow': true, 'active': true}, (tabs) => { - if (!tabs.length || !tabs[0].id) return; // sometimes theres no active tab - const tabId = tabs[0].id; - setTimeout(() => { - // wait for updateBTIcon to finish then show 'OFF' on top tab for 3 secs - chrome.action.setBadgeText({'text' : 'OFF', 'tabId': tabId}); - setTimeout(() => chrome.action.setBadgeText({'text' : '', 'tabId': tabId}), 3000); - }, 500); + if (!tabs.length || !tabs[0].id) return; // sometimes theres no active tab + const tabId = tabs[0].id; + setTimeout(() => { + // wait for updateBTIcon to finish then show 'OFF' on top tab for 3 secs + chrome.action.setBadgeText({'text' : 'OFF', 'tabId': tabId}); + setTimeout(() => chrome.action.setBadgeText({'text' : '', 'tabId': tabId}), 3000); + }, 500); }); } @@ -405,29 +408,29 @@ function updateBTIcon(text, title, color) { // set for each tab to override previous tab-specific setting chrome.tabs.query({}, (tabs) => - { - tabs.forEach((tab) => { - chrome.action.setBadgeText( - {'text' : text, 'tabId': tab.id}, () => check()); - chrome.action.setTitle( - {'title' : title, 'tabId': tab.id}); - chrome.action.setBadgeBackgroundColor( - {'color' : color, 'tabId': tab.id}); - }); - }); + { + tabs.forEach((tab) => { + chrome.action.setBadgeText( + {'text' : text, 'tabId': tab.id}, () => check()); + chrome.action.setTitle( + {'title' : title, 'tabId': tab.id}); + chrome.action.setBadgeBackgroundColor( + {'color' : color, 'tabId': tab.id}); + }); + }); // set across all tabs - chrome.action.setBadgeText( - {'text' : text}, () => check()); - chrome.action.setTitle( - {'title' : title}); - chrome.action.setBadgeBackgroundColor( - {'color' : color}); + chrome.action.setBadgeText( + {'text' : text}, () => check()); + chrome.action.setTitle( + {'title' : title}); + chrome.action.setBadgeBackgroundColor( + {'color' : color}); } function openTabs(msg, sender) { // open list of {nodeId, url} pairs, potentially in new window - + function openTabsInWin(tabInfo, winId = null) { // open [{url, nodeId}]s in tab in given window tabInfo.forEach((tabData) => { @@ -443,7 +446,7 @@ function openTabs(msg, sender) { const newWin = msg.newWin; const defaultWinId = msg.defaultWinId; // 0 or winId of siblings const [first, ...rest] = msg.tabs; - + if (newWin) // Create new win w first url, then iterate on rest chrome.windows.create({'url': first.url}, win => { @@ -465,7 +468,7 @@ function openTabs(msg, sender) { } function openTabGroups(msg, sender) { - // open tabs in specified or new tab group, potentially in new window + // open tabs in specified or new tab group, potentially in new window const tabGroups = msg.tabGroups; // [{tg, win, tgname[{id, url}]},..] const newWinNeeded = msg.newWin; @@ -484,7 +487,7 @@ function openTabGroups(msg, sender) { }); }); } - + tabGroups.forEach(tg => { // handle a {windowId, tabGroupId, groupName, 'tabGroupTabs': [{nodeId, url}]} instance const[first, ...rest] = tg.tabGroupTabs; @@ -498,7 +501,7 @@ function openTabGroups(msg, sender) { check(); tabOpened(win.id, win.tabs[0].id, first.nodeId, win.tabs[0].index, tgid); - chrome.tabGroups.update(tgid, {'title' : groupName}); + chrome.tabGroups.update(tgid, {'title' : groupName}); openTabsInTg(win.id, tgid, rest); }); }); @@ -515,7 +518,7 @@ function openTabGroups(msg, sender) { check(); tabOpened(tab.windowId, tab.id, first.nodeId, tab.index, tgid); - chrome.tabGroups.update(tgid, {'title' : groupName}); + chrome.tabGroups.update(tgid, {'title' : groupName}); openTabsInTg(tab.windowId, tgid, rest); }); }); @@ -554,11 +557,11 @@ function groupAndPositionTabs(msg, sender) { const nodeInfo = tabInfo.find(ti => ti.tabId == t.id); btSendMessage( sender.tab.id, {'function': 'tabPositioned', 'tabId': t.id, - 'nodeId': nodeInfo.nodeId, 'tabGroupId': groupId, - 'windowId': t.windowId, 'tabIndex': t.index}); + 'nodeId': nodeInfo.nodeId, 'tabGroupId': groupId, + 'windowId': t.windowId, 'tabIndex': t.index}); }); }); - }); + }); } function ungroup(msg, sender) { @@ -582,9 +585,9 @@ function moveOpenTabsToTG(msg, sender) { btSendMessage( sender.tab.id, {'tabId': tid, 'groupId': tgId, - 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndicies': indices, + 'tabIndex': tab.index, 'windowId': tab.windowId, 'tabIndicies': indices, 'tab': tab}); - // was {'function': 'tabGrouped', 'tgId': tgId, 'tabId': tid, 'tabIndex': tab.index}); + // was {'function': 'tabGrouped', 'tgId': tgId, 'tabId': tid, 'tabIndex': tab.index}); }); }); }); @@ -642,9 +645,9 @@ function setBadge(tabId) { function marquee(badgeText, index) { if (badgeText.length < 6 || index >= badgeText.length - 2) { chrome.action.setBadgeText({'text' : badgeText, 'tabId': tabId}, () => check('marquee')); - } else { + } else { chrome.action.setBadgeText({'text' : badgeText.slice(index) + " ", - 'tabId': tabId}, () => check('marquee')); + 'tabId': tabId}, () => check('marquee')); MarqueeEvent = setTimeout(function() {marquee(badgeText, ++index);}, 150); } } @@ -652,7 +655,7 @@ function setBadge(tabId) { chrome.storage.local.get(['currentTag', 'currentText'], function(data) { if (!data.currentTag) { chrome.action.setBadgeText({'text' : "", 'tabId' : tabId}, - () => check('Resetting badge text:')); + () => check('Resetting badge text:')); chrome.action.setTitle({'title' : 'BrainTool'}); } else { marquee(data.currentTag, 0); @@ -672,7 +675,7 @@ function brainZoom(msg, sender, iteration = 0) { "48": "images/BrainTool48.png", "128": "images/BrainTool128.png" }; - + if (iteration == iterationArray.length) { chrome.action.setIcon({'path': default_icon, 'tabId': msg.tabId}); setTimeout(function() {setBadge(msg.tabId);}, 150); @@ -696,7 +699,7 @@ function getBookmarks() { itemTree[0].title = "Imported Bookmarks"; chrome.storage.local.set({'bookmarks': itemTree[0]}, function() { btSendMessage(BTTab, {'function': 'loadBookmarks', - 'result': 'success'}); + 'result': 'success'}); }); }); } @@ -728,43 +731,43 @@ function exportBookmarks() { } /* -async function importSession(msg, sender) { - // return hierarchy of all windows/tgs/tabs to enable a topic tree to be created in Mgr - - const [BTTab, BTWin] = await getBTTabWin(); - const allTabs = await getOpenTabs(); // array of tabs - const allTGs = await getOpenTabGroups(); // array of tgs - const allWins = {}, groups = {}; - - allTGs.forEach(tg => { // create hash - groups[tg.id] = tg; - tg.tabs = []; // and add array for tabs - }); - - // Loop thru tabs, create win objs and fill in TGs and tabs - allTabs.forEach(t => { - if ((t.id == BTTab) || t.pinned) return; - if (!allWins[t.windowId]) - allWins[t.windowId] = {'windowId': t.windowId, 'windowName': 'Window'+t.windowId, - 'tabs': [], 'tabGroups': {}}; - const win = allWins[t.windowId]; - if (t.groupId > 0) { - const tg = groups[t.groupId]; - if (!win.tabGroups[t.groupId]) - win.tabGroups[t.groupId] = tg; - tg.tabs.push(t); - } else { - win.tabs.push(t); - } - }); - btSendMessage(BTTab, {'function': 'importSession', 'windows': allWins, 'topic': msg.topic, 'close': msg.close}); - console.log('allWins = ', allWins); -} + async function importSession(msg, sender) { + // return hierarchy of all windows/tgs/tabs to enable a topic tree to be created in Mgr + + const [BTTab, BTWin] = await getBTTabWin(); + const allTabs = await getOpenTabs(); // array of tabs + const allTGs = await getOpenTabGroups(); // array of tgs + const allWins = {}, groups = {}; + + allTGs.forEach(tg => { // create hash + groups[tg.id] = tg; + tg.tabs = []; // and add array for tabs + }); + + // Loop thru tabs, create win objs and fill in TGs and tabs + allTabs.forEach(t => { + if ((t.id == BTTab) || t.pinned) return; + if (!allWins[t.windowId]) + allWins[t.windowId] = {'windowId': t.windowId, 'windowName': 'Window'+t.windowId, + 'tabs': [], 'tabGroups': {}}; + const win = allWins[t.windowId]; + if (t.groupId > 0) { + const tg = groups[t.groupId]; + if (!win.tabGroups[t.groupId]) + win.tabGroups[t.groupId] = tg; + tg.tabs.push(t); + } else { + win.tabs.push(t); + } + }); + btSendMessage(BTTab, {'function': 'importSession', 'windows': allWins, 'topic': msg.topic, 'close': msg.close}); + console.log('allWins = ', allWins); + } */ async function saveTabs(msg, sender) { - // handle save for popup. msg.type Could be Tab, TG, Window or Session. - // msg: {'close': 'topic', 'note', 'title', 'currentWindowId' } + // handle save for popup. msg.type Could be Tab, TG, Window or Session. + // msg: {'close','topic', 'note', 'title', 'currentWindowId' } // Create array of appropriate tab data and send to BT window const currentTabs = await chrome.tabs.query({'active': true, 'windowId': msg.currentWindowId}); @@ -777,13 +780,13 @@ async function saveTabs(msg, sender) { // Create a hash of TGIds to TG names const tgNames = {}; allTGs.forEach(tg => tgNames[tg.id] = tg.title); - + // Loop thru tabs, decide based on msg.type if it should be saved and if so add to array to send to BTTab const tabsToSave = []; allTabs.forEach(t => { if (t.id == BTTab || t.pinned) return; - const tab = {'tabId': t.id, 'groupId': t.groupId, 'windowId': t.windowId, 'url': t.url, - 'favIconUrl': t.faviconUrl, 'tabIndex': t.tabIndex, 'title': t.title}; + const tab = {'tabId': t.id, 'groupId': t.groupId, 'windowId': t.windowId, 'url': t.url, + 'favIconUrl': t.faviconUrl, 'tabIndex': t.tabIndex, 'title': t.title}; const tgName = tgNames[t.groupId] || ''; const [topic, todo] = msg.topic.split(':'); // topic might have trailing :TODO or :DONE. split it off if (saveType == 'Tab' && t.id == currentTab.id) { diff --git a/versions/Release-Candidate/extension/popup.js b/versions/Release-Candidate/extension/popup.js index e06df8c..9fdc430 100644 --- a/versions/Release-Candidate/extension/popup.js +++ b/versions/Release-Candidate/extension/popup.js @@ -110,7 +110,7 @@ function windowOpen(home = 'PANEL', location) { // const url = "https://BrainTool.org/versions/"+version+'/app/'; // // Disgregard App store version id as used above since it must be monotonically increasing number format - const url = "https://BrainTool.org/versions/Release-Candidate/app/'; + const url = "https://BrainTool.org/versions/Release-Candidate/app/"; console.log('loading from ', url); // Default open in side panel