Skip to content

Commit

Permalink
Added workspace selector on bookmarks create/edit modal, added table …
Browse files Browse the repository at this point in the history
…for storing relationships between workspaces and bookmarks.
  • Loading branch information
kristijanribaric committed Nov 26, 2024
1 parent 3f58521 commit ba2bc4a
Show file tree
Hide file tree
Showing 8 changed files with 745 additions and 21 deletions.
71 changes: 60 additions & 11 deletions src/browser/base/zen-components/ZenWorkspaces.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
};
_hoveringSidebar = false;
_lastScrollTime = 0;
bookmarkMenus = [
"PlacesToolbar",
"bookmarks-menu-button",
"BMB_bookmarksToolbar",
"BMB_unsortedBookmarks",
"BMB_mobileBookmarks"
];

async init() {
if (!this.shouldHaveWorkspaces) {
Expand Down Expand Up @@ -61,6 +68,11 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}

Services.obs.addObserver(this, 'weave:engine:sync:finish');
Services.obs.addObserver(async function observe(subject) {
this._workspaceBookmarksCache = null;
await this.workspaceBookmarks();
this._invalidateBookmarkContainers();
}.bind(this), "workspace-bookmarks-updated");
}

initializeWorkspaceNavigation() {
Expand Down Expand Up @@ -128,7 +140,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}

// Change workspace based on scroll direction
const direction = event.deltaX > 0 ? -1 : 1;
const direction = event.deltaX > 0 ? 1 : -1;
await this.changeWorkspaceShortcut(direction);
this._lastScrollTime = currentTime;
}, { passive: true });
Expand Down Expand Up @@ -320,6 +332,21 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
return this._workspaceCache;
}

async workspaceBookmarks() {
if (this._workspaceBookmarksCache) {
return this._workspaceBookmarksCache;
}

const [bookmarks, lastChangeTimestamp] = await Promise.all([
ZenWorkspaceBookmarksStorage.getBookmarkGuidsByWorkspace(),
ZenWorkspaceBookmarksStorage.getLastChangeTimestamp(),
]);

this._workspaceBookmarksCache = { bookmarks, lastChangeTimestamp };

return this._workspaceCache;
}

async onWorkspacesEnabledChanged() {
if (this.workspaceEnabled) {
throw Error("Shoud've had reloaded the window");
Expand All @@ -339,6 +366,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
if (this.workspaceEnabled) {
this._initializeWorkspaceCreationIcons();
this._initializeWorkspaceTabContextMenus();
await this.workspaceBookmarks();
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
await SessionStore.promiseInitialized;
let workspaces = await this._workspaces();
Expand Down Expand Up @@ -754,8 +782,10 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {

if(clearCache) {
browser.ZenWorkspaces._workspaceCache = null;
browser.ZenWorkspaces._bookmarkWorkspaceCache = null;
}
let workspaces = await browser.ZenWorkspaces._workspaces();
await browser.ZenWorkspaces.workspaceBookmarks();
workspaceList.innerHTML = '';
workspaceList.parentNode.style.display = 'flex';
if (workspaces.workspaces.length <= 0) {
Expand Down Expand Up @@ -1252,16 +1282,23 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}
}

// Reset bookmarks toolbar
const placesToolbar = document.getElementById("PlacesToolbar");
if (placesToolbar?._placesView) {
placesToolbar._placesView.invalidateContainer(placesToolbar._placesView._resultNode);
}
// Reset bookmarks
this._invalidateBookmarkContainers();

// Update workspace indicator
await this.updateWorkspaceIndicator();
}

_invalidateBookmarkContainers() {
for (let i = 0, len = this.bookmarkMenus.length; i < len; i++) {
const element = document.getElementById(this.bookmarkMenus[i]);
if (element && element._placesView) {
const placesView = element._placesView;
placesView.invalidateContainer(placesView._resultNode);
}
}
}

async updateWorkspaceIndicator() {
// Update current workspace indicator
const currentWorkspace = await this.getActiveWorkspace();
Expand Down Expand Up @@ -1534,12 +1571,24 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
}

isBookmarkInAnotherWorkspace(bookmark) {
let tags = bookmark.tags;
// if any tag starts with "_workspace_id" and the workspace id doesnt match the active workspace id, return null
if (tags) {
for (let tag of tags.split(",")) {
return !!(tag.startsWith("zen_workspace_") && this.getActiveWorkspaceFromCache()?.uuid !== tag.split("_")[2]);
if (!this._workspaceBookmarksCache?.bookmarks) return false;
const bookmarkGuid = bookmark.bookmarkGuid;
const activeWorkspaceUuid = this.activeWorkspace;
let isInActiveWorkspace = false;
let isInOtherWorkspace = false;

for (const [workspaceUuid, bookmarkGuids] of Object.entries(this._workspaceBookmarksCache.bookmarks)) {
if (bookmarkGuids.includes(bookmarkGuid)) {
if (workspaceUuid === activeWorkspaceUuid) {
isInActiveWorkspace = true;
} else {
isInOtherWorkspace = true;
}
}
}

// Return true only if the bookmark is in another workspace and not in the active one
return isInOtherWorkspace && !isInActiveWorkspace;
}

})();
152 changes: 151 additions & 1 deletion src/browser/base/zen-components/ZenWorkspacesStorage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ var ZenWorkspacesStorage = {
async init() {
console.log('ZenWorkspacesStorage: Initializing...');
await this._ensureTable();
await ZenWorkspaceBookmarksStorage.init();
ZenWorkspaces._delayedStartup();
},

async _ensureTable() {
Expand Down Expand Up @@ -64,7 +66,6 @@ var ZenWorkspacesStorage = {
await ZenWorkspacesStorage.migrateWorkspacesFromJSON();
}

ZenWorkspaces._delayedStartup();
});
},

Expand Down Expand Up @@ -405,3 +406,152 @@ var ZenWorkspacesStorage = {
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
},
};

// Integration of workspace-specific bookmarks into Places
var ZenWorkspaceBookmarksStorage = {
async init() {
await this._ensureTable();
},

async _ensureTable() {
await PlacesUtils.withConnectionWrapper('ZenWorkspaceBookmarksStorage.init', async (db) => {
// Create table using GUIDs instead of IDs
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_bookmarks_workspaces (
id INTEGER PRIMARY KEY,
bookmark_guid TEXT NOT NULL,
workspace_uuid TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(bookmark_guid, workspace_uuid),
FOREIGN KEY(workspace_uuid) REFERENCES zen_workspaces(uuid) ON DELETE CASCADE,
FOREIGN KEY(bookmark_guid) REFERENCES moz_bookmarks(guid) ON DELETE CASCADE
)
`);

// Create index for fast lookups
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_lookup
ON zen_bookmarks_workspaces(workspace_uuid, bookmark_guid)
`);

// Add changes tracking table
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_bookmarks_workspaces_changes (
id INTEGER PRIMARY KEY,
bookmark_guid TEXT NOT NULL,
workspace_uuid TEXT NOT NULL,
change_type TEXT NOT NULL,
timestamp INTEGER NOT NULL,
UNIQUE(bookmark_guid, workspace_uuid),
FOREIGN KEY(workspace_uuid) REFERENCES zen_workspaces(uuid) ON DELETE CASCADE,
FOREIGN KEY(bookmark_guid) REFERENCES moz_bookmarks(guid) ON DELETE CASCADE
)
`);

// Create index for changes tracking
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_changes
ON zen_bookmarks_workspaces_changes(bookmark_guid, workspace_uuid)
`);

});
},

/**
* Updates the last change timestamp in the metadata table.
* @param {Object} db - The database connection.
*/
async updateLastChangeTimestamp(db) {
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO moz_meta (key, value)
VALUES ('zen_bookmarks_workspaces_last_change', :now)
`, { now });
},

/**
* Gets the timestamp of the last change.
* @returns {Promise<number>} The timestamp of the last change.
*/
async getLastChangeTimestamp() {
const db = await PlacesUtils.promiseDBConnection();
const result = await db.executeCached(`
SELECT value FROM moz_meta WHERE key = 'zen_bookmarks_workspaces_last_change'
`);
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
},

async getBookmarkWorkspaces(bookmarkGuid) {
const db = await PlacesUtils.promiseDBConnection();

const rows = await db.execute(`
SELECT workspace_uuid
FROM zen_bookmarks_workspaces
WHERE bookmark_guid = :bookmark_guid
`, { bookmark_guid: bookmarkGuid });

return rows.map(row => row.getResultByName("workspace_uuid"));
},

/**
* Get all bookmark GUIDs organized by workspace UUID.
* @returns {Promise<Object>} A dictionary with workspace UUIDs as keys and arrays of bookmark GUIDs as values.
* @example
* // Returns:
* {
* "workspace-uuid-1": ["bookmark-guid-1", "bookmark-guid-2"],
* "workspace-uuid-2": ["bookmark-guid-3"]
* }
*/
async getBookmarkGuidsByWorkspace() {
const db = await PlacesUtils.promiseDBConnection();

const rows = await db.execute(`
SELECT workspace_uuid, GROUP_CONCAT(bookmark_guid) as bookmark_guids
FROM zen_bookmarks_workspaces
GROUP BY workspace_uuid
`);

const result = {};
for (const row of rows) {
const workspaceUuid = row.getResultByName("workspace_uuid");
const bookmarkGuids = row.getResultByName("bookmark_guids");
result[workspaceUuid] = bookmarkGuids ? bookmarkGuids.split(',') : [];
}

return result;
},

/**
* Get all changed bookmarks with their change types.
* @returns {Promise<Object>} An object mapping bookmark+workspace pairs to their change data.
*/
async getChangedIDs() {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.execute(`
SELECT bookmark_guid, workspace_uuid, change_type, timestamp
FROM zen_bookmarks_workspaces_changes
`);

const changes = {};
for (const row of rows) {
const key = `${row.getResultByName('bookmark_guid')}:${row.getResultByName('workspace_uuid')}`;
changes[key] = {
type: row.getResultByName('change_type'),
timestamp: row.getResultByName('timestamp')
};
}
return changes;
},

/**
* Clear all recorded changes.
*/
async clearChangedIDs() {
await PlacesUtils.withConnectionWrapper('ZenWorkspaceBookmarksStorage.clearChangedIDs', async (db) => {
await db.execute(`DELETE FROM zen_bookmarks_workspaces_changes`);
});
},

};
Loading

0 comments on commit ba2bc4a

Please sign in to comment.