select: frontend/backend mappings were out of sync

Closes #27
This commit is contained in:
Nils Maier 2019-08-29 19:36:06 +02:00
parent 687b6e1aa9
commit d1cc406f05
2 changed files with 91 additions and 39 deletions

View File

@ -48,6 +48,7 @@ type DELTAS = {deltaLinks: ItemDelta[]; deltaMedia: ItemDelta[]};
interface BaseMatchedItem extends BaseItem { interface BaseMatchedItem extends BaseItem {
matched?: string | null; matched?: string | null;
rowid: number;
} }
function cleaErrors() { function cleaErrors() {
@ -96,8 +97,6 @@ class PausedModalDialog extends ModalDialog {
} }
} }
class CheckClasser extends Map<string, string> { class CheckClasser extends Map<string, string> {
gen: IterableIterator<string>; gen: IterableIterator<string>;
@ -126,19 +125,82 @@ class CheckClasser extends Map<string, string> {
type KeyFn = (item: BaseMatchedItem) => any; type KeyFn = (item: BaseMatchedItem) => any;
class ItemCollection {
private items: BaseMatchedItem[];
private indexes: Map<number, BaseMatchedItem>;
constructor(items: BaseMatchedItem[]) {
this.items = items;
this.assignRows();
this.indexes = new Map(items.map(i => [i.idx, i]));
}
assignRows() {
this.items.forEach((item, idx) => item.rowid = idx);
}
get length() {
return this.items.length;
}
get checked() {
const rv: number[] = [];
this.items.forEach(function (item, idx) {
if (item.matched && item.matched !== "unmanual") {
rv.push(idx);
}
});
return rv;
}
get checkedIndexes() {
const rv: number[] = [];
this.items.forEach(function (item) {
if (item.matched && item.matched !== "unmanual") {
rv.push(item.idx);
}
});
return rv;
}
at(idx: number) {
return this.items[idx];
}
byIndex(idx: number) {
return this.indexes.get(idx);
}
sort(keyFn: KeyFn) {
sort(this.items, keyFn, naturalCaseCompare);
this.assignRows();
}
reverse() {
this.items.reverse();
this.assignRows();
}
filter(fn: (item: BaseMatchedItem, idx: number) => boolean) {
return this.items.filter(fn);
}
}
class SelectionTable extends VirtualTable { class SelectionTable extends VirtualTable {
checkClasser: CheckClasser; checkClasser: CheckClasser;
icons: Icons; icons: Icons;
links: BaseMatchedItem[]; links: ItemCollection;
media: BaseMatchedItem[]; media: ItemCollection;
items: ItemCollection;
type: string; type: string;
items: BaseMatchedItem[];
status: HTMLElement; status: HTMLElement;
linksTab: HTMLElement; linksTab: HTMLElement;
@ -170,10 +232,10 @@ class SelectionTable extends VirtualTable {
this.checkClasser = new CheckClasser(NUM_FILTER_CLASSES); this.checkClasser = new CheckClasser(NUM_FILTER_CLASSES);
this.icons = new Icons($("#icons") as HTMLStyleElement); this.icons = new Icons($("#icons") as HTMLStyleElement);
this.links = links; this.links = new ItemCollection(links);
this.media = media; this.media = new ItemCollection(media);
this.type = type; this.type = type;
this.items = (this as any)[type]; this.items = type === "links" ? this.links : this.media;
this.status = $("#statusItems"); this.status = $("#statusItems");
this.linksTab = $("#linksTab"); this.linksTab = $("#linksTab");
@ -217,8 +279,8 @@ class SelectionTable extends VirtualTable {
if (!keyfn) { if (!keyfn) {
return false; return false;
} }
sort(this.links, keyfn, naturalCaseCompare); this.links.sort(keyfn);
sort(this.media, keyfn, naturalCaseCompare); this.media.sort(keyfn);
const elem = document.querySelector<HTMLElement>(`#${colid}`); const elem = document.querySelector<HTMLElement>(`#${colid}`);
const oldelem = (this.sortcol && document.querySelector<HTMLElement>(`#${this.sortcol}`)); const oldelem = (this.sortcol && document.querySelector<HTMLElement>(`#${this.sortcol}`));
if (this.sortcol === colid && this.sortasc) { if (this.sortcol === colid && this.sortasc) {
@ -280,7 +342,7 @@ class SelectionTable extends VirtualTable {
} }
let oldmask = ""; let oldmask = "";
for (const r of this.selection) { for (const r of this.selection) {
const m = this.items[r].mask; const m = this.items.at(r).mask;
if (oldmask && m !== oldmask) { if (oldmask && m !== oldmask) {
oldmask = ""; oldmask = "";
break; break;
@ -292,7 +354,7 @@ class SelectionTable extends VirtualTable {
const newmask = await ModalDialog.prompt( const newmask = await ModalDialog.prompt(
"Renaming mask", "Set new renaming mask", oldmask); "Renaming mask", "Set new renaming mask", oldmask);
for (const r of this.selection) { for (const r of this.selection) {
this.items[r].mask = newmask; this.items.at(r).mask = newmask;
this.invalidateRow(r); this.invalidateRow(r);
} }
} }
@ -317,16 +379,6 @@ class SelectionTable extends VirtualTable {
this.switchTab(type); this.switchTab(type);
} }
get checkedIndexes() {
const rv: number[] = [];
this.items.forEach(function (item, idx) {
if (item.matched && item.matched !== "unmanual") {
rv.push(idx);
}
});
return rv;
}
get rowCount() { get rowCount() {
return this.items.length; return this.items.length;
} }
@ -336,7 +388,7 @@ class SelectionTable extends VirtualTable {
return false; return false;
} }
for (const rowid of this.selection) { for (const rowid of this.selection) {
const item = this.items[rowid]; const item = this.items.at(rowid);
if (!state) { if (!state) {
state = matched(item) ? "unmanual" : "manual"; state = matched(item) ? "unmanual" : "manual";
} }
@ -362,7 +414,7 @@ class SelectionTable extends VirtualTable {
selectChecked() { selectChecked() {
this.selection.clear(); this.selection.clear();
let min = null; let min = null;
for (const ci of this.checkedIndexes) { for (const ci of this.items.checked) {
this.selection.add(ci); this.selection.add(ci);
min = min === null ? ci : Math.min(min, ci); min = min === null ? ci : Math.min(min, ci);
} }
@ -381,7 +433,7 @@ class SelectionTable extends VirtualTable {
if (this.focusRow < 0) { if (this.focusRow < 0) {
return; return;
} }
items.push(this.items[this.focusRow]); items.push(this.items.at(this.focusRow));
} }
PORT.postMessage({ PORT.postMessage({
msg: "openUrls", msg: "openUrls",
@ -389,14 +441,14 @@ class SelectionTable extends VirtualTable {
}); });
} }
applyDeltaTo(delta: ItemDelta[], items: BaseMatchedItem[]) { applyDeltaTo(delta: ItemDelta[], items: ItemCollection) {
const active = items === this.items; const active = items === this.items;
for (const d of delta) { for (const d of delta) {
const {idx = -1, matched = null} = d; const {idx = -1, matched = null} = d;
if (idx < 0) { if (idx < 0) {
continue; continue;
} }
const item = items[idx]; const item = items.byIndex(idx);
if (!item) { if (!item) {
continue; continue;
} }
@ -410,7 +462,7 @@ class SelectionTable extends VirtualTable {
} }
item.matched = matched; item.matched = matched;
if (active) { if (active) {
this.invalidateRow(idx); this.invalidateRow(item.rowid);
} }
} }
} }
@ -435,7 +487,7 @@ class SelectionTable extends VirtualTable {
} }
updateStatus() { updateStatus() {
const selected = this.checkedIndexes.length; const selected = this.items.checked.length;
if (!selected) { if (!selected) {
this.status.textContent = _("noitems.label"); this.status.textContent = _("noitems.label");
} }
@ -446,7 +498,7 @@ class SelectionTable extends VirtualTable {
} }
getRowClasses(rowid: number) { getRowClasses(rowid: number) {
const item = this.items[rowid]; const item = this.items.at(rowid);
if (!item || !matched(item) || !item.matched) { if (!item || !matched(item) || !item.matched) {
return null; return null;
} }
@ -454,7 +506,7 @@ class SelectionTable extends VirtualTable {
} }
getCellIcon(rowid: number, colid: number) { getCellIcon(rowid: number, colid: number) {
const item = this.items[rowid]; const item = this.items.at(rowid);
if (item && colid === COL_DOWNLOAD) { if (item && colid === COL_DOWNLOAD) {
return this.icons.get(iconForPath(item.url, ICON_BASE_SIZE)); return this.icons.get(iconForPath(item.url, ICON_BASE_SIZE));
} }
@ -471,7 +523,7 @@ class SelectionTable extends VirtualTable {
} }
getDownloadText(idx: number) { getDownloadText(idx: number) {
const item = this.items[idx]; const item = this.items.at(idx);
if (!item) { if (!item) {
return ""; return "";
} }
@ -482,7 +534,7 @@ class SelectionTable extends VirtualTable {
} }
getText(prop: string, idx: number) { getText(prop: string, idx: number) {
const item: any = this.items[idx]; const item: any = this.items.at(idx);
if (!item || !(prop in item) || !item[prop]) { if (!item || !(prop in item) || !item[prop]) {
return ""; return "";
} }
@ -490,7 +542,7 @@ class SelectionTable extends VirtualTable {
} }
getMaskText(idx: number) { getMaskText(idx: number) {
const item = this.items[idx]; const item = this.items.at(idx);
if (item) { if (item) {
return item.mask; return item.mask;
} }
@ -521,13 +573,13 @@ class SelectionTable extends VirtualTable {
getCellCheck(rowid: number, colid: number) { getCellCheck(rowid: number, colid: number) {
if (colid === COL_CHECK) { if (colid === COL_CHECK) {
return !!matched(this.items[rowid]); return !!matched(this.items.at(rowid));
} }
return false; return false;
} }
setCellCheck(rowid: number, colid: number, value: boolean) { setCellCheck(rowid: number, colid: number, value: boolean) {
this.items[rowid].matched = value ? "manual" : "unmanual"; this.items.at(rowid).matched = value ? "manual" : "unmanual";
this.invalidateRow(rowid); this.invalidateRow(rowid);
this.updateStatus(); this.updateStatus();
} }
@ -539,7 +591,7 @@ async function download(paused = false) {
if (!mask) { if (!mask) {
throw new Error("error.invalidMask"); throw new Error("error.invalidMask");
} }
const items = Table.checkedIndexes; const items = Table.items.checkedIndexes;
if (!items.length) { if (!items.length) {
throw new Error("error.noItemsSelected"); throw new Error("error.noItemsSelected");
} }