Skip to content

Commit 89647ef

Browse files
committed
fix: dnd for bookmarks and tabs
1 parent 2e43158 commit 89647ef

File tree

5 files changed

+102
-44
lines changed

5 files changed

+102
-44
lines changed

index.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ export class UI {
227227
document.body.addEventListener('contextmenu', (e) => {
228228
e.preventDefault();
229229
e.stopPropagation();
230+
if (!this.isSidePanel) this.openSettings();
230231
return false;
231232
});
232233

@@ -415,16 +416,18 @@ export class UI {
415416
if (sourceElement === null || !isDragEnabled) return;
416417
const currentTiles = Array.from(grid.querySelectorAll(tileClassNames));
417418
const targetIdx = currentTiles.indexOf(sourceElement);
418-
if (sourceIdx !== targetIdx) {
419-
if (tile.classList.contains('folder')) {
420-
const itemId = sourceElement.dataset.id;
421-
const groupId = e.currentTarget.dataset.id;
422-
items[sourceIdx].move()
423-
grid.dispatchEvent(new Event('move', { groupId, itemId }));
424-
} else {
425-
grid.dispatchEvent(new Event('reorder', { sourceIdx, targetIdx }));
419+
const item = items[sourceIdx];
420+
const itemId = sourceElement.dataset.id;
421+
const parentId = e.currentTarget.dataset.id;
422+
if (tile.classList.contains('folder') && itemId !== parentId) {
423+
if (!(item instanceof Tab && item.url === '')) {
424+
item.save({ parentId: parentId }); // move
426425
}
426+
} else if (sourceIdx !== targetIdx) {
427+
item.save({ index: targetIdx }); // reorder
427428
}
429+
tile.classList.remove('dropping');
430+
sourceElement.style.opacity = 1;
428431
});
429432
});
430433
}
@@ -436,7 +439,6 @@ export class UI {
436439
}
437440

438441
handleTileEvent(view, id, eventType, item) {
439-
console.log(view, id, eventType, item);
440442
if (this.currentView !== view) return;
441443

442444
const isCurrentFolder = this.currentPath[this.currentPath.length - 1]?.id === id;
@@ -453,23 +455,32 @@ export class UI {
453455
}
454456
return;
455457
}
458+
return this.loadContent();
456459

457460
const grid = document.getElementById('grid');
458461
const oldTile = grid.querySelector(`.tile[data-id="${id}"]`);
459462
const newTile = item ? this.createTile(item) : null;
463+
const render = (tile) => {
464+
if (!item.index || item.index >= grid.children.length) {
465+
grid.appendChild(tile);
466+
} else {
467+
grid.insertBefore(tile, grid.children[item.index]);
468+
}
469+
}
460470

461471
const isContentOfCurrentFolder = grid.dataset.parentId === item?.parentId;
462472
if (isContentOfCurrentFolder) {
463473
switch (eventType) {
464474
case 'created':
465-
grid.appendChild(newTile);
475+
render(newTile);
466476
break;
467477

468478
case 'updated':
469479
if (oldTile) { // updated inplace
470480
oldTile.innerHTML = newTile.innerHTML;
481+
render(oldTile);
471482
} else { // probably moved here from another folder, so we need to create the tile here
472-
grid.appendChild(newTile);
483+
render(newTile);
473484
}
474485
break;
475486

@@ -486,8 +497,8 @@ export class UI {
486497
chrome.bookmarks?.onCreated?.addListener((id, bookmark) => this.handleTileEvent('bookmarks', null, 'created', new Bookmark(bookmark)));
487498
chrome.bookmarks?.onChanged?.addListener(async (id, changeInfo) => this.handleTileEvent('bookmarks', id, 'updated', (await Bookmark.get(id))[0]));
488499
chrome.bookmarks?.onMoved?.addListener(async (id, moveInfo) => this.handleTileEvent('bookmarks', id, 'updated', (await Bookmark.get(id))[0]));
500+
chrome.bookmarks?.onChildrenReordered?.addListener(async (id, reorderInfo) => this.handleTileEvent('bookmarks', id, 'updated', (await Bookmark.get(id))[0]));
489501
chrome.bookmarks?.onRemoved?.addListener((id, removeInfo) => this.handleTileEvent('bookmarks', id, 'removed', null));
490-
// onChildrenReordered
491502

492503
chrome.readingList?.onEntryAdded?.addListener((entry) => this.handleTileEvent('readingList', null, 'created', new Page(entry)));
493504
chrome.readingList?.onEntryUpdated?.addListener((entry) => this.handleTileEvent('readingList', entry.url, 'updated', new Page(entry)));
@@ -498,11 +509,16 @@ export class UI {
498509
chrome.tabGroups?.onMoved?.addListener((group) => this.handleTileEvent('tabGroups', group.id, 'updated', new Tab(group)));
499510
chrome.tabGroups?.onRemoved?.addListener((group) => this.handleTileEvent('tabGroups', group.id, 'removed', null));
500511

512+
chrome.tabs?.onAttached?.addListener(async (id, attachInfo) => this.handleTileEvent('tabGroups', null, 'created', (await Tab.get(id))[0]));
501513
chrome.tabs?.onCreated?.addListener((tab) => this.handleTileEvent('tabGroups', null, 'created', new Tab(tab)));
502514
chrome.tabs?.onUpdated?.addListener((id, changeInfo, tab) => this.handleTileEvent('tabGroups', id, 'updated', new Tab(tab)));
503515
chrome.tabs?.onMoved?.addListener(async (id, moveInfo) => this.handleTileEvent('tabGroups', id, 'updated', (await Tab.get(id))[0]));
504516
chrome.tabs?.onRemoved?.addListener((id, removeInfo) => this.handleTileEvent('tabGroups', id, 'removed', null));
505-
// onReplaced, onAttached, onDetached
517+
chrome.tabs?.onDetached?.addListener((id, detachInfo) => this.handleTileEvent('tabGroups', id, 'removed', null));
518+
chrome.tabs?.onReplaced?.addListener(async (addedTabId, removedTabId) => {
519+
this.handleTileEvent('tabGroups', removedTabId, 'removed', null);
520+
this.handleTileEvent('tabGroups', null, 'created', (await Tab.get(addedTabId))[0]);
521+
});
506522

507523
chrome.history?.onVisited?.addListener((history) => this.handleTileEvent('history', history.id, 'created', new History(history)));
508524
chrome.history?.onVisitRemoved?.addListener(({allHistory, urls}) => urls?.forEach(url => this.handleTileEvent('history', url, 'removed', null)));
@@ -729,7 +745,7 @@ export class UI {
729745

730746
// MARK: Edit button
731747
createEditButton(item) {
732-
if ((item instanceof Bookmark && item.parentId === '0') || (item instanceof Page && !item.url) || item instanceof History || item instanceof TopSite || item instanceof Tab) return;
748+
if ((item instanceof Bookmark && item.parentId === '0') || item instanceof Page || item instanceof History || item instanceof TopSite) return;
733749

734750
let title = '';
735751
switch (this.currentView) {
@@ -759,7 +775,7 @@ export class UI {
759775

760776
// MARK: Delete button
761777
createDeleteButton(item) {
762-
if ((item instanceof Bookmark && item.parentId === '0') || ((item instanceof Tab || item instanceof Page) && !item.url) || item instanceof TopSite) return;
778+
if ((item instanceof Bookmark && item.parentId === '0') || (item instanceof Page && !item.url) || item instanceof TopSite) return;
763779

764780
const deleteBtn = document.createElement('button');
765781
deleteBtn.title = chrome.i18n.getMessage('delete');
@@ -850,7 +866,7 @@ export class UI {
850866
const option = document.createElement('option');
851867
option.value = item.id;
852868
option.textContent = item.title;
853-
option.selected = (defaultValue && defaultValue === item.id) ? true : undefined;
869+
option.selected = (defaultValue && defaultValue == item.id) ? true : undefined;
854870
target.appendChild(option);
855871
}
856872
}

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"minimum_chrome_version": "116",
44
"name": "__MSG_app_name__",
55
"description": "__MSG_app_description__",
6-
"version": "1.0",
6+
"version": "1.4.1",
77
"default_locale": "en",
88
"icons": {
99
"16": "images/icon-16.png",

src/Bookmark.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,18 @@ export class Bookmark {
5353
return await chrome.bookmarks.get(id.toString());
5454
}
5555

56-
async save({ title, url, parentId }) {
57-
if (!this.id) {
56+
async save({ title, url, parentId, index }) {
57+
if (!this.id) { // create
5858
await chrome.bookmarks.create({ title, url, parentId });
59-
} else {
60-
await chrome.bookmarks.update(this.id, { title, url });
59+
60+
} else { // update
61+
if (index || parentId) { // move or reorder
62+
await chrome.bookmarks.move(this.id, { parentId: parentId ?? this.parentId, index });
63+
}
64+
65+
if (title || url) { // edit
66+
await chrome.bookmarks.update(this.id, { title, url });
67+
}
6168
}
6269
}
6370

src/Page.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export class Page {
33
constructor({ url, title, id, parentId, hasBeenRead, creationTime, lastUpdateTime, children }) {
44
this.title = title;
55
this.url = url; // pkey
6-
this.id = id; // missing
6+
this.id = id ?? url; // missing
77
this.index = null; // missing
88
this.parentId = parentId ? parentId : (hasBeenRead ? '1' : '0');
99
this.createdAt = creationTime;
@@ -68,7 +68,10 @@ export class Page {
6868
}
6969
}
7070

71-
open(newTab = false) {
71+
async open(newTab = false) {
72+
if (this.parentId !== '1') {
73+
await this.save({ parentId: '1' }); // mark as read
74+
}
7275
window.open(this.url, newTab ? '_blank' : '_self');
7376
}
7477
}

src/Tab.js

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ export class Tab {
22

33
constructor({ url, title, id, index, parentId, groupId, windowId, color, lastAccessed, children }) {
44
this.title = title;
5-
this.url = url;
5+
this.url = (url !== undefined) ? url : '';
66
this.id = id;
77
this.index = index;
8-
this.parentId = parentId ? parentId : (groupId === -1 ? '' : groupId);
8+
this.parentId = String((parentId !== undefined) ? parentId : (groupId ?? ''));
99
this.createdAt = null; // missing
1010
this.updatedAt = lastAccessed;
1111
this.color = color;
@@ -28,6 +28,8 @@ export class Tab {
2828
// selected
2929
// status
3030
// width
31+
32+
if (this.parentId == -1) this.parentId = '';
3133
}
3234

3335
static async all() {
@@ -62,15 +64,15 @@ export class Tab {
6264
let items = [];
6365
if (parentId === '') {
6466
items = [
65-
...(await chrome.tabGroups.query({})),
66-
...(await chrome.tabs.query({ groupId: -1 }))
67+
...(await chrome.tabGroups.query({ windowId: chrome.windows.WINDOW_ID_CURRENT })),
68+
...(await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, groupId: -1 }))
6769
];
6870
// items.push({
6971
// id: -1,
7072
// title: chrome.i18n.getMessage('ungrouped_tabs'),
7173
// });
7274
} else {
73-
items = (await chrome.tabs.query({ groupId: parseInt(parentId) }));
75+
items = (await chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT, groupId: parseInt(parentId) }));
7476
}
7577
return items.map(item => new this(item)) || [];
7678
}
@@ -79,36 +81,66 @@ export class Tab {
7981
return await chrome.tabs.get(id);
8082
}
8183

82-
async save({ url, active }) {
83-
if (!this.id) {
84-
await chrome.tabs.create({ url, active });
85-
} else {
86-
await chrome.tabs.update(this.id, { active });
84+
async save({ title, url, parentId, index }) {
85+
if (!this.id) { // create
86+
if (!url) { // group
87+
const newTab = await chrome.tabs.create({ windowId: chrome.windows.WINDOW_ID_CURRENT, url: 'chrome://newtab', active: false });
88+
const newGroup = await chrome.tabs.group({ tabIds: [newTab.id], createProperties: { windowId: chrome.windows.WINDOW_ID_CURRENT } });
89+
await chrome.tabGroups.update(newGroup, { title });
90+
91+
} else { // tab
92+
const newTab = await chrome.tabs.create({ windowId: chrome.windows.WINDOW_ID_CURRENT, url, active: false });
93+
if (parentId) { // attach to group
94+
await chrome.tabs.group({ tabIds: [newTab.id], groupId: Number(parentId) });
95+
}
96+
}
97+
98+
} else { // update
99+
if (!this.url) {
100+
if (index || parentId) { // move or reorder
101+
if (parentId) return;
102+
await chrome.tabGroups.move(this.id, { windowId: chrome.windows.WINDOW_ID_CURRENT, index: index ?? -1 });
103+
}
104+
105+
if (title) { // edit
106+
await chrome.tabGroups.update(this.id, { title });
107+
}
108+
109+
} else {
110+
if (parentId === '') {
111+
await chrome.tabs.ungroup([this.id]);
112+
}
113+
else if (parentId) { // move to another group
114+
await chrome.tabs.group({ tabIds: [this.id], groupId: Number(parentId) });
115+
}
116+
117+
if (index) { // reorder
118+
await chrome.tabs.move(this.id, { windowId: chrome.windows.WINDOW_ID_CURRENT, index });
119+
}
120+
121+
if (url) { // edit
122+
await chrome.tabs.update(this.id, { url });
123+
}
124+
}
87125
}
88126
}
89127

90128
async remove() {
91129
if (!this.url) { // group
92-
// tab groups can't be removed
130+
const childTabs = await Tab.find(this.id);
131+
childTabs.forEach(tab => chrome.tabs.remove(tab.id));
93132
} else {
94133
await chrome.tabs.remove(this.id);
95134
}
96135
}
97136

98-
async open() {
99-
if (!this.url) { // group
137+
async open(newTab = false) {
138+
if (!this.url) {
100139
await chrome.tabGroups.update(this.id, { collapsed: false });
101140
} else {
102141
await chrome.tabs.update(this.id, { active: true });
103142
}
104-
window.close();
105-
/*
106-
chrome.tabs.query({ groupId: group.id }, (tabs) => {
107-
if (tabs.length > 0) {
108-
chrome.tabs.update(tabs[0].id, { active: true });
109-
}
110-
});
111-
*/
143+
if (!newTab) window.close();
112144
}
113145

114146
getColor() {

0 commit comments

Comments
 (0)