diff --git a/CHANGELOG.md b/CHANGELOG.md index 235d548..8f56b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### v1.3.10 10/3/2018 + + - feat: allow store tabs into a specific list by clicking the items in context menu or a button in the simple list + - feat: right clicking an item in the list will show a menu allows moving the tab to another list + ### v1.3.9 9/29/2018 - feat: add an option to allow open tabs at the ending diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 912554b..7caf9d3 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -29,6 +29,12 @@ "menu_STORE_TWOSIDE_TABS": { "message": "store unselected tabs" }, + "menu_STORE": { + "message": "store" + }, + "menu_STORE_TO_TITLED_LIST": { + "message": "store into a titled list" + }, "cmd_store_selected_tabs": { "message": "Store selected tabs" }, @@ -355,5 +361,11 @@ }, "ui_click_view_changelog": { "message": "click to view detail changelog" + }, + "ui_move_to": { + "message": "Move to" + }, + "ui_a_new_list": { + "message": "(a new list)" } } diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 97a2564..34bcfa0 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -29,6 +29,12 @@ "menu_STORE_TWOSIDE_TABS": { "message": "储存未选中的标签" }, + "menu_STORE": { + "message": "储存" + }, + "menu_STORE_TO_TITLED_LIST": { + "message": "储存至" + }, "cmd_store_selected_tabs": { "message": "储存选中的标签" }, @@ -346,5 +352,11 @@ }, "ui_click_view_changelog": { "message": "点击查看详细更新日志" + }, + "ui_move_to": { + "message": "移动至" + }, + "ui_a_new_list": { + "message": "(一个新列表)" } } diff --git a/src/background.js b/src/background.js index ba4547c..5f55285 100644 --- a/src/background.js +++ b/src/background.js @@ -59,7 +59,56 @@ const updateBrowserAction = (action, tmp = false) => { } } -const setupContextMenus = async ({pageContext, allContext}) => { +const dynamicDisableMenu = async () => { + const groupedTabs = await tabs.groupTabsInCurrentWindow() + const windows = await browser.windows.getAll() + browser.contextMenus.update('STORE.STORE_LEFT_TABS', { + enabled: groupedTabs.left.length !== 0, + title: __('menu_STORE_LEFT_TABS') + ` (${groupedTabs.left.length})`, + }) + browser.contextMenus.update('STORE.STORE_RIGHT_TABS', { + enabled: groupedTabs.right.length !== 0, + title: __('menu_STORE_RIGHT_TABS') + ` (${groupedTabs.right.length})`, + }) + browser.contextMenus.update('STORE.STORE_TWOSIDE_TABS', { + enabled: groupedTabs.twoSide.length !== 0, + title: __('menu_STORE_TWOSIDE_TABS') + ` (${groupedTabs.twoSide.length})`, + }) + browser.contextMenus.update('STORE.STORE_ALL_TABS_IN_ALL_WINDOWS', { + enabled: windows.length > 1, + title: __('menu_STORE_ALL_TABS_IN_ALL_WINDOWS') + ` (${groupedTabs.all.length})`, + }) + browser.contextMenus.update('STORE.STORE_ALL_TABS_IN_CURRENT_WINDOW', { + title: __('menu_STORE_ALL_TABS_IN_CURRENT_WINDOW') + ` (${groupedTabs.all.length})`, + }) + browser.contextMenus.update('STORE_SELECTED_TABS', { + title: __('menu_STORE_SELECTED_TABS') + ` (${groupedTabs.inter.length})`, + }) + const lists = await storage.getLists() + for (let i = 0; i < lists.length; i += 1) { + if (!lists[i].title) continue + browser.contextMenus.update('STORE_TO_TITLED_LIST.STORE_LEFT_TABS|' + i, { + enabled: groupedTabs.left.length !== 0, + title: __('menu_STORE_LEFT_TABS') + ` (${groupedTabs.left.length})`, + }) + browser.contextMenus.update('STORE_TO_TITLED_LIST.STORE_RIGHT_TABS|' + i, { + enabled: groupedTabs.right.length !== 0, + title: __('menu_STORE_RIGHT_TABS') + ` (${groupedTabs.right.length})`, + }) + browser.contextMenus.update('STORE_TO_TITLED_LIST.STORE_TWOSIDE_TABS|' + i, { + enabled: groupedTabs.twoSide.length !== 0, + title: __('menu_STORE_TWOSIDE_TABS') + ` (${groupedTabs.twoSide.length})`, + }) + browser.contextMenus.update('STORE_TO_TITLED_LIST.STORE_ALL_TABS_IN_CURRENT_WINDOW|' + i, { + title: __('menu_STORE_ALL_TABS_IN_CURRENT_WINDOW') + ` (${groupedTabs.all.length})`, + }) + browser.contextMenus.update('STORE_TO_TITLED_LIST.STORE_SELECTED_TABS|' + i, { + title: __('menu_STORE_SELECTED_TABS') + ` (${groupedTabs.inter.length})`, + }) + } +} + +const setupContextMenus = _.debounce(async ({pageContext, allContext}) => { await browser.contextMenus.removeAll() const contexts = [browser.contextMenus.ContextType.BROWSER_ACTION] if (pageContext) { @@ -67,57 +116,80 @@ const setupContextMenus = async ({pageContext, allContext}) => { if (allContext) contexts.push(browser.contextMenus.ContextType.ALL) } const menus = { - STORE_SELECTED_TABS: tabs.storeSelectedTabs, - STORE_ALL_TABS_IN_CURRENT_WINDOW: tabs.storeAllTabs, SHOW_TAB_LIST: tabs.openTabLists, - STORE_ALL_TABS_IN_ALL_WINDOWS: tabs.storeAllTabInAllWindows, - EXTRA: { + STORE_SELECTED_TABS: tabs.storeSelectedTabs, + STORE: { + STORE_ALL_TABS_IN_CURRENT_WINDOW: tabs.storeAllTabs, + STORE_ALL_TABS_IN_ALL_WINDOWS: tabs.storeAllTabInAllWindows, STORE_LEFT_TABS: tabs.storeLeftTabs, STORE_RIGHT_TABS: tabs.storeRightTabs, STORE_TWOSIDE_TABS: tabs.storeTwoSideTabs, }, + STORE_TO_TITLED_LIST: { + STORE_SELECTED_TABS: tabs.storeSelectedTabs, + STORE_ALL_TABS_IN_CURRENT_WINDOW: tabs.storeAllTabs, + STORE_LEFT_TABS: tabs.storeLeftTabs, + STORE_RIGHT_TABS: tabs.storeRightTabs, + STORE_TWOSIDE_TABS: tabs.storeTwoSideTabs, + } } + const lists = await storage.getLists() const createMenus = async (obj, parent) => { - for (const key of Object.keys(obj)) { - const prop = { - id: key, - title: __('menu_' + key), - contexts, + if (obj === menus.STORE_TO_TITLED_LIST) { + for (let listIndex = 0; listIndex < lists.length; listIndex += 1) { + if (!lists[listIndex].title) continue + const prop = { + id: 'STORE_TO_TITLED_LIST|' + listIndex, + title: lists[listIndex].title, + contexts, + parentId: 'STORE_TO_TITLED_LIST', + } + const id = await browser.contextMenus.create(prop) + console.log('context menu created: ' + id) + for (const key in obj) { + const prop = { + id: 'STORE_TO_TITLED_LIST.' + key + '|' + listIndex, + title: __('menu_' + key), + contexts, + parentId: id, + } + const childId = await browser.contextMenus.create(prop) + console.log('context menu created: ' + childId) + } } - if (parent) { - prop.id = parent + '.' + key - prop.parentId = parent + } else { + for (const key of Object.keys(obj)) { + const prop = { + id: key, + title: __('menu_' + key), + contexts, + } + if (parent) { + prop.id = parent + '.' + key + prop.parentId = parent + } + const id = await browser.contextMenus.create(prop) + console.log('context menu created: ' + id) + if (_.isObject(obj[key])) await createMenus(obj[key], key) } - const id = await browser.contextMenus.create(prop) - console.log('context menu created: ' + id) - if (_.isObject(obj[key])) await createMenus(obj[key], key) } } window.contextMenusClickedHandler = info => { console.log('context menu clicked', info.menuItemId) - _.get(menus, info.menuItemId)() - if (PRODUCTION) ga('send', 'event', 'Menu clicked', info.menuItemId) + if (info.menuItemId.startsWith('STORE_TO_TITLED_LIST')) { + const [key, listIndex] = info.menuItemId.split('|') + _.get(menus, key)(+listIndex) + if (PRODUCTION) ga('send', 'event', 'Menu clicked', key) + } else { + _.get(menus, info.menuItemId)() + if (PRODUCTION) ga('send', 'event', 'Menu clicked', info.menuItemId) + } } console.groupCollapsed('create context menu', contexts) await createMenus(menus) console.groupEnd('create context menu') -} - -const dynamicDisableMenu = async () => { - const groupedTabs = await tabs.groupTabsInCurrentWindow() - browser.contextMenus.update('EXTRA.STORE_LEFT_TABS', { - enabled: groupedTabs.left.length !== 0, - title: __('menu_STORE_LEFT_TABS') + ` (${groupedTabs.left.length})`, - }) - browser.contextMenus.update('EXTRA.STORE_RIGHT_TABS', { - enabled: groupedTabs.right.length !== 0, - title: __('menu_STORE_RIGHT_TABS') + ` (${groupedTabs.right.length})`, - }) - browser.contextMenus.update('EXTRA.STORE_TWOSIDE_TABS', { - enabled: groupedTabs.twoSide.length !== 0, - title: __('menu_STORE_TWOSIDE_TABS') + ` (${groupedTabs.twoSide.length})`, - }) -} + dynamicDisableMenu() +}, 250) const commandHandler = async command => { console.log('received command', command) @@ -184,6 +256,9 @@ const init = async () => { if (msg.forceDownload) { boss.forceDownloadRemoteImmediate() } + if (msg.storeInto) { + tabs.storeSelectedTabs(msg.storeInto.index) + } }) browser.runtime.onMessageExternal.addListener(commandHandler) browser.commands.onCommand.addListener(commandHandler) @@ -191,6 +266,7 @@ const init = async () => { window.update = detail.version }) browser.runtime.onInstalled.addListener(detail => { + if (DEBUG) return if (detail.reason === chrome.runtime.OnInstalledReason.UPDATE) { const updatedNotificationId = 'updated' browser.notifications.onClicked.addListener(id => { @@ -215,11 +291,14 @@ const init = async () => { window.coverBrowserAction(activeInfo) dynamicDisableMenu(activeInfo) }, 200)) - browser.storage.onChanged.addListener(changes => { + browser.storage.onChanged.addListener(async changes => { console.log(changes) if (changes.boss_token) { window.boss_token = changes.boss_token } + if (changes.lists) { + setupContextMenus(await storage.getOptions()) + } }) await boss.forceDownloadRemoteImmediate() setInterval(() => boss.forceDownloadRemoteImmediate(), 60 * 1000) diff --git a/src/common/tabs.js b/src/common/tabs.js index d79eff0..398ba35 100644 --- a/src/common/tabs.js +++ b/src/common/tabs.js @@ -47,11 +47,12 @@ const getAllTabsInCurrentWindow = async () => { const groupTabsInCurrentWindow = async () => { const tabs = await getAllTabsInCurrentWindow() - const result = { left: [], right: [] } + const result = { left: [], right: [], inter: [], all: tabs } let currentIsRight = false for (const tab of tabs) { if (tab.highlighted) { currentIsRight = true + result.inter.push(tab) } else if (currentIsRight) result.right.push(tab) else result.left.push(tab) } @@ -63,7 +64,7 @@ const isLegalURL = url => [ 'about:', 'chrome:', 'file:', 'wss:' ].every(prefix => !url.startsWith(prefix)) -const storeTabs = async tabs => { +const storeTabs = async (tabs, listIndex) => { const appUrl = browser.runtime.getURL('') tabs = tabs.filter(i => !i.url.startsWith(appUrl)) const opts = await storage.getOptions() @@ -72,9 +73,13 @@ const storeTabs = async tabs => { if (tabs.length === 0) return browser.tabs.remove(tabs.map(i => i.id)) const lists = await storage.getLists() - const newList = list.createNewTabList({tabs: pickTabs(tabs)}) - if (opts.pinNewList) newList.pinned = true - lists.unshift(newList) + if (listIndex == null) { + const newList = list.createNewTabList({tabs: pickTabs(tabs)}) + if (opts.pinNewList) newList.pinned = true + lists.unshift(newList) + } else { + tabs.forEach(tab => lists[listIndex].tabs.push(tab)) + } await storage.setLists(lists) if (opts.addHistory) { for (let i = 0; i < tabs.length; i += 1) { @@ -88,21 +93,21 @@ const storeTabs = async tabs => { } } -const storeLeftTabs = async () => storeTabs((await groupTabsInCurrentWindow()).left) -const storeRightTabs = async () => storeTabs((await groupTabsInCurrentWindow()).right) -const storeTwoSideTabs = async () => storeTabs((await groupTabsInCurrentWindow()).twoSide) +const storeLeftTabs = async listIndex => storeTabs((await groupTabsInCurrentWindow()).left, listIndex) +const storeRightTabs = async listIndex => storeTabs((await groupTabsInCurrentWindow()).right, listIndex) +const storeTwoSideTabs = async listIndex => storeTabs((await groupTabsInCurrentWindow()).twoSide, listIndex) -const storeSelectedTabs = async () => { +const storeSelectedTabs = async listIndex => { const tabs = await getSelectedTabs() const allTabs = await getAllTabsInCurrentWindow() if (tabs.length === allTabs.length) await openTabLists() - return storeTabs(tabs) + return storeTabs(tabs, listIndex) } -const storeAllTabs = async () => { +const storeAllTabs = async listIndex => { const tabs = await getAllTabsInCurrentWindow() await openTabLists() - return storeTabs(tabs) + return storeTabs(tabs, listIndex) } const storeAllTabInAllWindows = async () => { diff --git a/src/manifest.json b/src/manifest.json index 8579154..e829fd2 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "__MSG_ext_name__", - "version": "1.3.9", + "version": "1.3.10", "default_locale": "en", "description": "__MSG_ext_desc__", "author": "WangJie ", diff --git a/src/page/DetailList.vue b/src/page/DetailList.vue index a7423b3..2c83360 100644 --- a/src/page/DetailList.vue +++ b/src/page/DetailList.vue @@ -104,6 +104,7 @@ :href="opts.itemClickAction !== 'none' ? tab.url : null" :target="opts.itemClickAction !== 'none' ? '_blank' : null" @click="itemClicked(listIndex, tabIndex)" + @contextmenu="rightClicked(listIndex, tabIndex, $event)" class="list-item" :ref="'list-' + listIndex + '-tab'" :key="tabIndex"> @@ -140,6 +141,31 @@

+ + + + {{ __('ui_move_to') }} + + {{ list.title }} + + + {{ __('ui_a_new_list') }} + + + diff --git a/src/page/Main.vue b/src/page/Main.vue index 8b01f84..34438c6 100644 --- a/src/page/Main.vue +++ b/src/page/Main.vue @@ -40,7 +40,7 @@ - fas fa-file-import + import_export {{ __('ui_export_import') }} diff --git a/src/page/Popup.vue b/src/page/Popup.vue index 30f0ea0..1966746 100644 --- a/src/page/Popup.vue +++ b/src/page/Popup.vue @@ -1,5 +1,5 @@