Skip to content

Commit

Permalink
TAB-484 savepoint
Browse files Browse the repository at this point in the history
  • Loading branch information
evandor committed Jan 28, 2024
1 parent f035002 commit e12c922
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/services/ChromeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class ChromeApi {
if (process.env.MODE !== 'bex') {
return
}
console.log("building context menu:", chrome, chrome?.contextMenus)
console.debug("building context menu")
const tabsStore = useTabsStore()
if (chrome && chrome.contextMenus) {
chrome.contextMenus.removeAll(
Expand Down
36 changes: 32 additions & 4 deletions src/services/IndexedDbPersistenceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,14 +517,41 @@ class IndexedDbPersistenceService implements PersistenceService {

/*** Windows Management ***/

addWindow(window: Window): Promise<any> {
async addWindow(window: Window): Promise<any> {
//console.log("%cadding window", "background-color:yellow", window)
return this.db.add('windows', window, window.id)
.catch((err) => {
const existingWindowForWindowId = await this.db.get('windows', window.id)
if (existingWindowForWindowId) {
// not bad, simply resolve
return Promise.resolve("Key already exists")
}
if (!window.title) {
// try to find matching window
const allWindows: Window[] = await this.db.getAll('windows') as Window[]
console.log(`adding window ${window.id} to window list [${_.join(_.map(allWindows, w => w.id), ',')}]`)
for (const w of allWindows) {
console.log("comparing hostLists", window.hostList, w.hostList, typeof w.hostList)
const intersection = new Set([...window.hostList].filter(x => (w.hostList instanceof Set && w.hostList.has(x))));
console.log("intersection", intersection)
if (intersection.size === window.hostList.size && intersection.size === w.hostList.size) {
// reuse existing
const useId = window.id
const oldId = w.id
window = w
window.id = useId
this.db.delete('windows', oldId)
break
}
}
}
try {
await this.db.add('windows', window, window.id)
}
//.then((res) => console.log("got res", res))
catch(err:any) {
if (!err.toString().indexOf('Key already exists')) {
console.log("error adding window", window, err)
}
})
}
}

// updateGroup(group: chrome.tabGroups.TabGroup): Promise<any> {
Expand All @@ -537,6 +564,7 @@ class IndexedDbPersistenceService implements PersistenceService {
}

getWindow(windowId: number): Promise<Window | undefined> {
console.log("trying to get window with id", windowId)
return this.db.get('windows', windowId)
}

Expand Down
49 changes: 32 additions & 17 deletions src/stores/windowsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,36 +116,48 @@ export const useWindowsStore = defineStore('windows', () => {
console.debug("initializing current windows with", currentWindows.value.length)

// adding potentially new windows to storage - adding windows will not do anything if the key already exists
const res: Promise<any>[] = browserWindows.flatMap((browserWindow: chrome.windows.Window) => {
// const res: Promise<any>[] = browserWindows.flatMap((browserWindow: chrome.windows.Window) => {
// const hostList = new Set(_.map(browserWindow.tabs, bwTabs => urlToHost(bwTabs)))
// return storage.addWindow(new Window(browserWindow.id || 0, browserWindow, undefined, 0, hostList))
// })

for (const browserWindow of browserWindows) {
const hostList = new Set(_.map(browserWindow.tabs, bwTabs => urlToHost(bwTabs)))
return storage.addWindow(new Window(browserWindow.id || 0, browserWindow, undefined, 0, hostList))
})
await storage.addWindow(new Window(browserWindow.id || 0, browserWindow, undefined, 0, hostList))
}

// setting all (new and older) windows to 'allWindows':
await Promise.all(res)
//await Promise.all(res)
allWindows.value = new Map()
let index = 0

const storedWindows: Window[] = await storage.getWindows()
const sortedStoredWindows = _.sortBy(storedWindows, "index")
sortedStoredWindows.forEach(tabsetWindowFromStorage => {
//sortedStoredWindows.forEach(tabsetWindowFromStorage => {
for (const tabsetWindowFromStorage of sortedStoredWindows) {

// index handling
const indexFromDb = tabsetWindowFromStorage.index
const indicesDiffer = indexFromDb !== index
let indexToUse = index++

if (indicesDiffer) {
updateWindowIndex(tabsetWindowFromStorage.id, indexToUse)
.then(() => console.log("done with updating window", tabsetWindowFromStorage.id, indexToUse))
.catch((err) => console.error("error when updating window", tabsetWindowFromStorage.id, indexToUse, err))
}
tabsetWindowFromStorage.index = indexToUse
allWindows.value.set(tabsetWindowFromStorage.id || 0, tabsetWindowFromStorage)
try {
await updateWindowIndex(tabsetWindowFromStorage.id, indexToUse)
}
//.then(() => console.log("done with updating window", tabsetWindowFromStorage.id, indexToUse))
catch (err) {
console.error("error when updating window", tabsetWindowFromStorage.id, indexToUse, err)
}
tabsetWindowFromStorage.index = indexToUse
allWindows.value.set(tabsetWindowFromStorage.id || 0, tabsetWindowFromStorage)

// const inCurrentWindows = browserWindows.find(w => w.id === tabsetWindowFromStorage.id) !== undefined
// console.debug(`assigned window #${tabsetWindowFromStorage.id} (name: ${tabsetWindowFromStorage.title}): ${indexFromDb} -> ${tabsetWindowFromStorage.index}, open: ${inCurrentWindows}`)
})
// const inCurrentWindows = browserWindows.find(w => w.id === tabsetWindowFromStorage.id) !== undefined
// console.debug(`assigned window #${tabsetWindowFromStorage.id} (name: ${tabsetWindowFromStorage.title}): ${indexFromDb} -> ${tabsetWindowFromStorage.index}, open: ${inCurrentWindows}`)
} else {
allWindows.value.set(tabsetWindowFromStorage.id || 0, tabsetWindowFromStorage)
}
}
for (const id of allWindows.value.keys()) {
const w = allWindows.value.get(id)
if (w && w.title) {
Expand Down Expand Up @@ -184,7 +196,7 @@ export const useWindowsStore = defineStore('windows', () => {
async function onRemoved(windowId: number) {
// remove only if window does not have a title
const w = await storage.getWindow(windowId)
console.debug("on removed", w, windowId)
//console.debug("on removed", w, windowId)
if (w && !w.title) {
await removeWindow(windowId)
}
Expand All @@ -199,11 +211,13 @@ export const useWindowsStore = defineStore('windows', () => {
async function onUpdate(windowId: number) {
if (windowId >= 0) {
const windowFromDb = windowForId(windowId)
console.debug(`updating window #${windowId}: title='${windowFromDb?.title}', index=${windowFromDb?.index}`, windowId, windowFromDb)
if (windowFromDb) {
console.debug(`updating window #${windowId}: title='${windowFromDb?.title}', index=${windowFromDb?.index}`)
const chromeWindow = await chrome.windows.get(windowId)
await storage.updateWindow(new Window(windowId, chromeWindow, windowFromDb.title, windowFromDb.index))
refreshCurrentWindows()
} else {
console.log(`could not update window #${windowId}`)
}
}
}
Expand Down Expand Up @@ -377,6 +391,7 @@ export const useWindowsStore = defineStore('windows', () => {
removeWindowByTitle,
refreshCurrentWindows,
windowForId,
updateWindowIndex
updateWindowIndex,
allWindows
}
})
102 changes: 81 additions & 21 deletions test/vitest/__tests__/stores/windowsStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ let onCreatedListener = null as unknown as ((window: chrome.windows.Window) => P
let onRemovedListener = null as unknown as ((windowId: number) => Promise<void>)
let onFocusChangedListener = null as unknown as ((windowId: number) => Promise<void>)

async function setupMocksAndStores(
currentWindows: any[],
currentWindow: any,
db: PersistenceService
) {
let currentWindows: any[]

async function setupMocks(currentWindow: any) {
//console.log("setupMocks with current windows", currentWindows)
// https://groups.google.com/a/chromium.org/g/chromium-extensions/c/hssoAlvluW8
const chromeMock = {
windows: {
getAll: vi.fn((options, callback) => {
console.log("mocking chrome.windows.getAll")
console.log("mocking chrome.windows.getAll", currentWindows.length)
//callback(currentWindows);
return Promise.resolve(currentWindows)
}),
Expand Down Expand Up @@ -57,7 +55,7 @@ async function setupMocksAndStores(
},
onFocusChanged: {
addListener: vi.fn((listener) => {
//console.log("mocking chrome.windows.onFocusChanged.addListener", listener)
console.log("mocking chrome.windows.onFocusChanged.addListener", listener)
onFocusChangedListener = listener
})
},
Expand All @@ -75,7 +73,9 @@ async function setupMocksAndStores(
};

vi.stubGlobal('chrome', chromeMock);
}

async function setupStores(db: PersistenceService) {
await useWindowsStore().initialize(db)
useWindowsStore().initListeners()
}
Expand All @@ -87,10 +87,10 @@ describe('WindowsStore', () => {
const tab1 = ChromeApi.createChromeTabObject("skysail", "https://www.skysail.io")
const tab2 = ChromeApi.createChromeTabObject("tabsets", "https://www.tabsets.net")

const window100:chrome.windows.Window = ChromeApi.createChromeWindowObject(100, 17, 28, [tab1, tab2])
const window200:chrome.windows.Window = ChromeApi.createChromeWindowObject(200, 17, 28, [tab2])
const window100: chrome.windows.Window = ChromeApi.createChromeWindowObject(100, 17, 28, [tab1, tab2])
const window200: chrome.windows.Window = ChromeApi.createChromeWindowObject(200, 17, 28, [tab2])

const currentWindows = [window100, window200]
//const currentWindows = [window100, window200]

beforeEach(async () => {
setActivePinia(createPinia())
Expand All @@ -106,7 +106,9 @@ describe('WindowsStore', () => {

it('initializing correctly with multiple windows and indices differing', async () => {

await setupMocksAndStores(currentWindows, window100, db)
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const windows = await db.getWindows()
expect(windows.length).toBe(2)
Expand All @@ -116,18 +118,21 @@ describe('WindowsStore', () => {
const window = await db.getWindow(100)
expect(window?.id).toBe(100)
expect(window?.index).toBe(0)
expect(window?.hostList).toStrictEqual(new Set(['www.skysail.io','www.tabsets.net']))
expect(window?.hostList).toStrictEqual(new Set(['www.skysail.io', 'www.tabsets.net']))
expect(useWindowsStore().currentWindow.id).toBe(100)

const window200 = await db.getWindow(200)
expect(window200?.id).toBe(200)
expect(window200?.index).toBe(1)
expect(window200?.hostList).toStrictEqual(new Set(['www.tabsets.net']))
const w200 = await db.getWindow(200)
expect(w200?.id).toBe(200)
expect(w200?.index).toBe(1)
expect(w200?.hostList).toStrictEqual(new Set(['www.tabsets.net']))

})

it('upsertWindow saves new title', async () => {
await setupMocksAndStores(currentWindows, window100, db)
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const window = await db.getWindow(100)
if (window) {
await useWindowsStore().upsertWindow(window.browserWindow, "theTitle")
Expand All @@ -139,7 +144,10 @@ describe('WindowsStore', () => {
})

it('onRemoved does not remove window with title', async () => {
await setupMocksAndStores(currentWindows, window100, db)
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const window = await db.getWindow(100)
if (window) {
await useWindowsStore().upsertWindow(window.browserWindow, "theTitle")
Expand All @@ -152,16 +160,27 @@ describe('WindowsStore', () => {
})

it('onFocusChanged updates browserWindow', async () => {
await setupMocksAndStores(currentWindows, window100, db)
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const t = await db.getWindow(100)
// console.log("t", t)
console.log("t", useWindowsStore().allWindows)
console.log("==============")
await onFocusChangedListener(100)
console.log("==============")

const window100FromDb = await db.getWindow(100)
// 33 is a 'magic number' assigned
expect(window100FromDb?.browserWindow.left).toBe(33)
})

it('onCreate', async () => {
it('onCreate yields new window', async () => {
const window: chrome.windows.Window = ChromeApi.createChromeWindowObject(1000, 0, 0)
await setupMocksAndStores(currentWindows.concat(window), window100, db)
currentWindows = [window100, window200, window]
await setupMocks(window100)
await setupStores(db)
await onCreatedListener(window)
// await useWindowsStore().persistGroup(ChromeApi.createChromeTabGroupObject(1, "groupName", 'grey' as chrome.tabGroups.ColorEnum))
//const groups = await db.getGroups()
Expand All @@ -172,6 +191,47 @@ describe('WindowsStore', () => {
expect(window1000FromDb?.index).toBe(2)
})

it('onCreate keeps existing titles', async () => {
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const window100FromDb = await db.getWindow(100)
if (!window100FromDb) {
expect(true).toBeFalsy()
}
// @ts-ignore
await useWindowsStore().upsertWindow(window100FromDb?.browserWindow, "theTitle")

const window: chrome.windows.Window = ChromeApi.createChromeWindowObject(1000, 0, 0)
currentWindows = [window100, window200, window]
await setupMocks(window100)
await onCreatedListener(window)
const updatedWindow100 = await db.getWindow(100)
expect(updatedWindow100?.title).toBe("theTitle")
})

it('onCreate reuses existing window when matched', async () => {
// const tab1 = ChromeApi.createChromeTabObject("skysail", "https://www.skysail.io")
// const tab2 = ChromeApi.createChromeTabObject("tabsets", "https://www.tabsets.net")
const windowWithSameTabsAsWindow100: chrome.windows.Window = ChromeApi.createChromeWindowObject(1000, 0, 0, [tab1,tab2])
currentWindows = [window100, window200]
await setupMocks(window100)
await setupStores(db)

const w100 = await db.getWindow(100)
if (w100) {
w100.title = "theTitle"
await db.updateWindow(w100)
}

console.log("==============")
await onCreatedListener(windowWithSameTabsAsWindow100)
console.log("==============")
const updatedWindow100 = await db.getWindow(100)
expect(updatedWindow100?.title).toBe("theTitle")
})

// it('persists group with changing title', async () => {
// await useGroupsStore().initialize(db)
// await useGroupsStore().persistGroup(ChromeApi.createChromeTabGroupObject(1, "ab", 'grey' as chrome.tabGroups.ColorEnum))
Expand Down

0 comments on commit e12c922

Please sign in to comment.