mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-22 22:33:34 -07:00
WebUI: Remove unnecessary hashing
Now the containers support using string as key so the intermediate hashing/mapping to number isn't needed now.
This commit is contained in:
parent
6fe02895a8
commit
73e9116d21
6 changed files with 255 additions and 299 deletions
|
@ -139,31 +139,33 @@ const useAutoHideZeroStatusFilters = LocalPreferences.get("hide_zero_status_filt
|
||||||
const displayFullURLTrackerColumn = LocalPreferences.get("full_url_tracker_column", "false") === "true";
|
const displayFullURLTrackerColumn = LocalPreferences.get("full_url_tracker_column", "false") === "true";
|
||||||
|
|
||||||
/* Categories filter */
|
/* Categories filter */
|
||||||
const CATEGORIES_ALL = 1;
|
const CATEGORIES_ALL = "b4af0e4c-e76d-4bac-a392-46cbc18d9655";
|
||||||
const CATEGORIES_UNCATEGORIZED = 2;
|
const CATEGORIES_UNCATEGORIZED = "e24bd469-ea22-404c-8e2e-a17c82f37ea0";
|
||||||
|
|
||||||
const category_list = new Map();
|
// Map<category: String, {savePath: String, torrents: Set}>
|
||||||
|
const categoryMap = new Map();
|
||||||
|
|
||||||
let selectedCategory = Number(LocalPreferences.get("selected_category", CATEGORIES_ALL));
|
let selectedCategory = LocalPreferences.get("selected_category", CATEGORIES_ALL);
|
||||||
let setCategoryFilter = () => {};
|
let setCategoryFilter = () => {};
|
||||||
|
|
||||||
/* Tags filter */
|
/* Tags filter */
|
||||||
const TAGS_ALL = 1;
|
const TAGS_ALL = "b4af0e4c-e76d-4bac-a392-46cbc18d9655";
|
||||||
const TAGS_UNTAGGED = 2;
|
const TAGS_UNTAGGED = "e24bd469-ea22-404c-8e2e-a17c82f37ea0";
|
||||||
|
|
||||||
const tagList = new Map();
|
// Map<tag: String, torrents: Set>
|
||||||
|
const tagMap = new Map();
|
||||||
|
|
||||||
let selectedTag = Number(LocalPreferences.get("selected_tag", TAGS_ALL));
|
let selectedTag = LocalPreferences.get("selected_tag", TAGS_ALL);
|
||||||
let setTagFilter = () => {};
|
let setTagFilter = () => {};
|
||||||
|
|
||||||
/* Trackers filter */
|
/* Trackers filter */
|
||||||
const TRACKERS_ALL = 1;
|
const TRACKERS_ALL = "b4af0e4c-e76d-4bac-a392-46cbc18d9655";
|
||||||
const TRACKERS_TRACKERLESS = 2;
|
const TRACKERS_TRACKERLESS = "e24bd469-ea22-404c-8e2e-a17c82f37ea0";
|
||||||
|
|
||||||
/** @type Map<number, {host: string, trackerTorrentMap: Map<string, string[]>}> **/
|
// Map<trackerHost: String, Map<trackerURL: String, torrents: Set>>
|
||||||
const trackerList = new Map();
|
const trackerMap = new Map();
|
||||||
|
|
||||||
let selectedTracker = Number(LocalPreferences.get("selected_tracker", TRACKERS_ALL));
|
let selectedTracker = LocalPreferences.get("selected_tracker", TRACKERS_ALL);
|
||||||
let setTrackerFilter = () => {};
|
let setTrackerFilter = () => {};
|
||||||
|
|
||||||
/* All filters */
|
/* All filters */
|
||||||
|
@ -272,11 +274,11 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
handleFilterSelectionChange(currentHash, newHash);
|
handleFilterSelectionChange(currentHash, newHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
setCategoryFilter = (hash) => {
|
setCategoryFilter = (category) => {
|
||||||
const currentHash = torrentsTable.getCurrentTorrentID();
|
const currentHash = torrentsTable.getCurrentTorrentID();
|
||||||
|
|
||||||
LocalPreferences.set("selected_category", hash);
|
LocalPreferences.set("selected_category", category);
|
||||||
selectedCategory = Number(hash);
|
selectedCategory = category;
|
||||||
highlightSelectedCategory();
|
highlightSelectedCategory();
|
||||||
updateMainData();
|
updateMainData();
|
||||||
|
|
||||||
|
@ -284,11 +286,11 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
handleFilterSelectionChange(currentHash, newHash);
|
handleFilterSelectionChange(currentHash, newHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
setTagFilter = (hash) => {
|
setTagFilter = (tag) => {
|
||||||
const currentHash = torrentsTable.getCurrentTorrentID();
|
const currentHash = torrentsTable.getCurrentTorrentID();
|
||||||
|
|
||||||
LocalPreferences.set("selected_tag", hash);
|
LocalPreferences.set("selected_tag", tag);
|
||||||
selectedTag = Number(hash);
|
selectedTag = tag;
|
||||||
highlightSelectedTag();
|
highlightSelectedTag();
|
||||||
updateMainData();
|
updateMainData();
|
||||||
|
|
||||||
|
@ -296,11 +298,11 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
handleFilterSelectionChange(currentHash, newHash);
|
handleFilterSelectionChange(currentHash, newHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
setTrackerFilter = (hash) => {
|
setTrackerFilter = (tracker) => {
|
||||||
const currentHash = torrentsTable.getCurrentTorrentID();
|
const currentHash = torrentsTable.getCurrentTorrentID();
|
||||||
|
|
||||||
LocalPreferences.set("selected_tracker", hash);
|
LocalPreferences.set("selected_tracker", tracker);
|
||||||
selectedTracker = Number(hash);
|
selectedTracker = tracker;
|
||||||
highlightSelectedTracker();
|
highlightSelectedTracker();
|
||||||
updateMainData();
|
updateMainData();
|
||||||
|
|
||||||
|
@ -374,21 +376,18 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
const serverState = {};
|
const serverState = {};
|
||||||
|
|
||||||
const removeTorrentFromCategoryList = (hash) => {
|
const removeTorrentFromCategoryList = (hash) => {
|
||||||
if (!hash)
|
if (hash === undefined)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let removed = false;
|
let removed = false;
|
||||||
category_list.forEach((category) => {
|
for (const data of categoryMap.values())
|
||||||
const deleteResult = category.torrents.delete(hash);
|
removed ||= data.torrents.delete(hash);
|
||||||
removed ||= deleteResult;
|
|
||||||
});
|
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addTorrentToCategoryList = (torrent) => {
|
const addTorrentToCategoryList = (torrent) => {
|
||||||
const category = torrent["category"];
|
const category = torrent["category"];
|
||||||
if (typeof category === "undefined")
|
if (category === undefined)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const hash = torrent["hash"];
|
const hash = torrent["hash"];
|
||||||
|
@ -397,33 +396,29 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const categoryHash = window.qBittorrent.Misc.genHash(category);
|
let categoryData = categoryMap.get(category);
|
||||||
if (!category_list.has(categoryHash)) { // This should not happen
|
if (categoryData === undefined) { // This should not happen
|
||||||
category_list.set(categoryHash, {
|
categoryData = {
|
||||||
name: category,
|
|
||||||
torrents: new Set()
|
torrents: new Set()
|
||||||
});
|
};
|
||||||
|
categoryMap.set(category, categoryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const torrents = category_list.get(categoryHash).torrents;
|
if (categoryData.torrents.has(hash))
|
||||||
if (!torrents.has(hash)) {
|
|
||||||
removeTorrentFromCategoryList(hash);
|
|
||||||
torrents.add(hash);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
removeTorrentFromCategoryList(hash);
|
||||||
|
categoryData.torrents.add(hash);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeTorrentFromTagList = (hash) => {
|
const removeTorrentFromTagList = (hash) => {
|
||||||
if (!hash)
|
if (hash === undefined)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let removed = false;
|
let removed = false;
|
||||||
tagList.forEach((tag) => {
|
for (const torrents of tagMap.values())
|
||||||
const deleteResult = tag.torrents.delete(hash);
|
removed ||= torrents.delete(hash);
|
||||||
removed ||= deleteResult;
|
|
||||||
});
|
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -437,23 +432,21 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
if (torrent["tags"].length === 0) // No tags
|
if (torrent["tags"].length === 0) // No tags
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const tags = torrent["tags"].split(",");
|
const tags = torrent["tags"].split(", ");
|
||||||
let added = false;
|
let added = false;
|
||||||
for (let i = 0; i < tags.length; ++i) {
|
for (const tag of tags) {
|
||||||
const tagHash = window.qBittorrent.Misc.genHash(tags[i].trim());
|
let torrents = tagMap.get(tag);
|
||||||
if (!tagList.has(tagHash)) { // This should not happen
|
if (torrents === undefined) { // This should not happen
|
||||||
tagList.set(tagHash, {
|
torrents = new Set();
|
||||||
name: tags,
|
tagMap.set(tag, torrents);
|
||||||
torrents: new Set()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const torrents = tagList.get(tagHash).torrents;
|
|
||||||
if (!torrents.has(hash)) {
|
if (!torrents.has(hash)) {
|
||||||
torrents.add(hash);
|
torrents.add(hash);
|
||||||
added = true;
|
added = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return added;
|
return added;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -500,13 +493,13 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
const categoryItemTemplate = document.getElementById("categoryFilterItem");
|
const categoryItemTemplate = document.getElementById("categoryFilterItem");
|
||||||
|
|
||||||
const createCategoryLink = (hash, name, count) => {
|
const createLink = (category, text, count) => {
|
||||||
const categoryFilterItem = categoryItemTemplate.content.cloneNode(true).firstElementChild;
|
const categoryFilterItem = categoryItemTemplate.content.cloneNode(true).firstElementChild;
|
||||||
categoryFilterItem.id = hash;
|
categoryFilterItem.id = category;
|
||||||
categoryFilterItem.classList.toggle("selectedFilter", hash === selectedCategory);
|
categoryFilterItem.classList.toggle("selectedFilter", (category === selectedCategory));
|
||||||
|
|
||||||
const span = categoryFilterItem.firstElementChild;
|
const span = categoryFilterItem.firstElementChild;
|
||||||
span.lastElementChild.textContent = `${name} (${count})`;
|
span.lastElementChild.textContent = `${text} (${count})`;
|
||||||
|
|
||||||
return categoryFilterItem;
|
return categoryFilterItem;
|
||||||
};
|
};
|
||||||
|
@ -516,7 +509,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const { parent, category } = stack.pop();
|
const { parent, category } = stack.pop();
|
||||||
const displayName = category.nameSegments.at(-1);
|
const displayName = category.nameSegments.at(-1);
|
||||||
const listItem = createCategoryLink(category.categoryHash, displayName, category.categoryCount);
|
const listItem = createLink(category.categoryName, displayName, category.categoryCount);
|
||||||
listItem.firstElementChild.style.paddingLeft = `${(category.nameSegments.length - 1) * 20 + 6}px`;
|
listItem.firstElementChild.style.paddingLeft = `${(category.nameSegments.length - 1) * 20 + 6}px`;
|
||||||
|
|
||||||
parent.appendChild(listItem);
|
parent.appendChild(listItem);
|
||||||
|
@ -528,7 +521,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
for (const subcategory of category.children.reverse())
|
for (const subcategory of category.children.reverse())
|
||||||
stack.push({ parent: unorderedList, category: subcategory });
|
stack.push({ parent: unorderedList, category: subcategory });
|
||||||
}
|
}
|
||||||
const categoryLocalPref = `category_${category.categoryHash}_collapsed`;
|
const categoryLocalPref = `category_${category.categoryName}_collapsed`;
|
||||||
const isCollapsed = !category.forceExpand && (LocalPreferences.get(categoryLocalPref, "false") === "true");
|
const isCollapsed = !category.forceExpand && (LocalPreferences.get(categoryLocalPref, "false") === "true");
|
||||||
LocalPreferences.set(categoryLocalPref, listItem.classList.toggle("collapsedCategory", isCollapsed).toString());
|
LocalPreferences.set(categoryLocalPref, listItem.classList.toggle("collapsedCategory", isCollapsed).toString());
|
||||||
}
|
}
|
||||||
|
@ -541,17 +534,18 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedCategories = [];
|
const sortedCategories = [];
|
||||||
category_list.forEach((category, hash) => sortedCategories.push({
|
for (const [category, categoryData] of categoryMap) {
|
||||||
categoryName: category.name,
|
sortedCategories.push({
|
||||||
categoryHash: hash,
|
categoryName: category,
|
||||||
categoryCount: category.torrents.size,
|
categoryCount: categoryData.torrents.size,
|
||||||
nameSegments: category.name.split("/"),
|
nameSegments: category.split("/"),
|
||||||
...(useSubcategories && {
|
...(useSubcategories && {
|
||||||
children: [],
|
children: [],
|
||||||
parentID: null,
|
isRoot: true,
|
||||||
forceExpand: LocalPreferences.get(`category_${hash}_collapsed`) === null
|
forceExpand: LocalPreferences.get(`category_${category}_collapsed`) === null
|
||||||
})
|
})
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
sortedCategories.sort((left, right) => {
|
sortedCategories.sort((left, right) => {
|
||||||
const leftSegments = left.nameSegments;
|
const leftSegments = left.nameSegments;
|
||||||
const rightSegments = right.nameSegments;
|
const rightSegments = right.nameSegments;
|
||||||
|
@ -567,8 +561,8 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const categoriesFragment = new DocumentFragment();
|
const categoriesFragment = new DocumentFragment();
|
||||||
categoriesFragment.appendChild(createCategoryLink(CATEGORIES_ALL, "QBT_TR(All)QBT_TR[CONTEXT=CategoryFilterModel]", torrentsTable.getRowSize()));
|
categoriesFragment.appendChild(createLink(CATEGORIES_ALL, "QBT_TR(All)QBT_TR[CONTEXT=CategoryFilterModel]", torrentsTable.getRowSize()));
|
||||||
categoriesFragment.appendChild(createCategoryLink(CATEGORIES_UNCATEGORIZED, "QBT_TR(Uncategorized)QBT_TR[CONTEXT=CategoryFilterModel]", uncategorized));
|
categoriesFragment.appendChild(createLink(CATEGORIES_UNCATEGORIZED, "QBT_TR(Uncategorized)QBT_TR[CONTEXT=CategoryFilterModel]", uncategorized));
|
||||||
|
|
||||||
if (useSubcategories) {
|
if (useSubcategories) {
|
||||||
categoryList.classList.add("subcategories");
|
categoryList.classList.add("subcategories");
|
||||||
|
@ -582,20 +576,20 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
const isDirectSubcategory = (subcategory.nameSegments.length - category.nameSegments.length) === 1;
|
const isDirectSubcategory = (subcategory.nameSegments.length - category.nameSegments.length) === 1;
|
||||||
if (isDirectSubcategory) {
|
if (isDirectSubcategory) {
|
||||||
subcategory.parentID = category.categoryHash;
|
subcategory.isRoot = false;
|
||||||
category.children.push(subcategory);
|
category.children.push(subcategory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const category of sortedCategories) {
|
for (const category of sortedCategories) {
|
||||||
if (category.parentID === null)
|
if (category.isRoot)
|
||||||
createCategoryTree(category);
|
createCategoryTree(category);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
categoryList.classList.remove("subcategories");
|
categoryList.classList.remove("subcategories");
|
||||||
for (const { categoryHash, categoryName, categoryCount } of sortedCategories)
|
for (const { categoryName, categoryCount } of sortedCategories)
|
||||||
categoriesFragment.appendChild(createCategoryLink(categoryHash, categoryName, categoryCount));
|
categoriesFragment.appendChild(createLink(categoryName, categoryName, categoryCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
categoryList.appendChild(categoriesFragment);
|
categoryList.appendChild(categoriesFragment);
|
||||||
|
@ -608,7 +602,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const category of categoryList.getElementsByTagName("li"))
|
for (const category of categoryList.getElementsByTagName("li"))
|
||||||
category.classList.toggle("selectedFilter", (Number(category.id) === selectedCategory));
|
category.classList.toggle("selectedFilter", (category.id === selectedCategory));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTagList = () => {
|
const updateTagList = () => {
|
||||||
|
@ -620,10 +614,10 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
const tagItemTemplate = document.getElementById("tagFilterItem");
|
const tagItemTemplate = document.getElementById("tagFilterItem");
|
||||||
|
|
||||||
const createLink = (hash, text, count) => {
|
const createLink = (tag, text, count) => {
|
||||||
const tagFilterItem = tagItemTemplate.content.cloneNode(true).firstElementChild;
|
const tagFilterItem = tagItemTemplate.content.cloneNode(true).firstElementChild;
|
||||||
tagFilterItem.id = hash;
|
tagFilterItem.id = tag;
|
||||||
tagFilterItem.classList.toggle("selectedFilter", hash === selectedTag);
|
tagFilterItem.classList.toggle("selectedFilter", (tag === selectedTag));
|
||||||
|
|
||||||
const span = tagFilterItem.firstElementChild;
|
const span = tagFilterItem.firstElementChild;
|
||||||
span.lastChild.textContent = `${text} (${count})`;
|
span.lastChild.textContent = `${text} (${count})`;
|
||||||
|
@ -641,15 +635,16 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
tagFilterList.appendChild(createLink(TAGS_UNTAGGED, "QBT_TR(Untagged)QBT_TR[CONTEXT=TagFilterModel]", untagged));
|
tagFilterList.appendChild(createLink(TAGS_UNTAGGED, "QBT_TR(Untagged)QBT_TR[CONTEXT=TagFilterModel]", untagged));
|
||||||
|
|
||||||
const sortedTags = [];
|
const sortedTags = [];
|
||||||
tagList.forEach((tag, hash) => sortedTags.push({
|
for (const [tag, torrents] of tagMap) {
|
||||||
tagName: tag.name,
|
sortedTags.push({
|
||||||
tagHash: hash,
|
tagName: tag,
|
||||||
tagSize: tag.torrents.size
|
tagSize: torrents.size
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
sortedTags.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.tagName, right.tagName));
|
sortedTags.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.tagName, right.tagName));
|
||||||
|
|
||||||
for (const { tagName, tagHash, tagSize } of sortedTags)
|
for (const { tagName, tagSize } of sortedTags)
|
||||||
tagFilterList.appendChild(createLink(tagHash, tagName, tagSize));
|
tagFilterList.appendChild(createLink(tagName, tagName, tagSize));
|
||||||
|
|
||||||
window.qBittorrent.Filters.tagsFilterContextMenu.searchAndAddTargets();
|
window.qBittorrent.Filters.tagsFilterContextMenu.searchAndAddTargets();
|
||||||
};
|
};
|
||||||
|
@ -660,7 +655,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const tag of tagFilterList.children)
|
for (const tag of tagFilterList.children)
|
||||||
tag.classList.toggle("selectedFilter", (Number(tag.id) === selectedTag));
|
tag.classList.toggle("selectedFilter", (tag.id === selectedTag));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateTrackerList = () => {
|
const updateTrackerList = () => {
|
||||||
|
@ -672,13 +667,13 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
const trackerItemTemplate = document.getElementById("trackerFilterItem");
|
const trackerItemTemplate = document.getElementById("trackerFilterItem");
|
||||||
|
|
||||||
const createLink = (hash, text, count) => {
|
const createLink = (host, text, count) => {
|
||||||
const trackerFilterItem = trackerItemTemplate.content.cloneNode(true).firstElementChild;
|
const trackerFilterItem = trackerItemTemplate.content.cloneNode(true).firstElementChild;
|
||||||
trackerFilterItem.id = hash;
|
trackerFilterItem.id = host;
|
||||||
trackerFilterItem.classList.toggle("selectedFilter", hash === selectedTracker);
|
trackerFilterItem.classList.toggle("selectedFilter", (host === selectedTracker));
|
||||||
|
|
||||||
const span = trackerFilterItem.firstElementChild;
|
const span = trackerFilterItem.firstElementChild;
|
||||||
span.lastChild.textContent = text.replace("%1", count);
|
span.lastChild.textContent = `${text} (${count})`;
|
||||||
|
|
||||||
return trackerFilterItem;
|
return trackerFilterItem;
|
||||||
};
|
};
|
||||||
|
@ -689,12 +684,12 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
trackerlessTorrentsCount += 1;
|
trackerlessTorrentsCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackerFilterList.appendChild(createLink(TRACKERS_ALL, "QBT_TR(All (%1))QBT_TR[CONTEXT=TrackerFiltersList]", torrentsTable.getRowSize()));
|
trackerFilterList.appendChild(createLink(TRACKERS_ALL, "QBT_TR(All)QBT_TR[CONTEXT=TrackerFiltersList]", torrentsTable.getRowSize()));
|
||||||
trackerFilterList.appendChild(createLink(TRACKERS_TRACKERLESS, "QBT_TR(Trackerless (%1))QBT_TR[CONTEXT=TrackerFiltersList]", trackerlessTorrentsCount));
|
trackerFilterList.appendChild(createLink(TRACKERS_TRACKERLESS, "QBT_TR(Trackerless)QBT_TR[CONTEXT=TrackerFiltersList]", trackerlessTorrentsCount));
|
||||||
|
|
||||||
// Sort trackers by hostname
|
// Sort trackers by hostname
|
||||||
const sortedList = [];
|
const sortedList = [];
|
||||||
trackerList.forEach(({ host, trackerTorrentMap }, hash) => {
|
for (const [host, trackerTorrentMap] of trackerMap) {
|
||||||
const uniqueTorrents = new Set();
|
const uniqueTorrents = new Set();
|
||||||
for (const torrents of trackerTorrentMap.values()) {
|
for (const torrents of trackerTorrentMap.values()) {
|
||||||
for (const torrent of torrents)
|
for (const torrent of torrents)
|
||||||
|
@ -703,13 +698,12 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
sortedList.push({
|
sortedList.push({
|
||||||
trackerHost: host,
|
trackerHost: host,
|
||||||
trackerHash: hash,
|
|
||||||
trackerCount: uniqueTorrents.size,
|
trackerCount: uniqueTorrents.size,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
sortedList.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.trackerHost, right.trackerHost));
|
sortedList.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.trackerHost, right.trackerHost));
|
||||||
for (const { trackerHost, trackerHash, trackerCount } of sortedList)
|
for (const { trackerHost, trackerCount } of sortedList)
|
||||||
trackerFilterList.appendChild(createLink(trackerHash, (trackerHost + " (%1)"), trackerCount));
|
trackerFilterList.appendChild(createLink(trackerHost, trackerHost, trackerCount));
|
||||||
|
|
||||||
window.qBittorrent.Filters.trackersFilterContextMenu.searchAndAddTargets();
|
window.qBittorrent.Filters.trackersFilterContextMenu.searchAndAddTargets();
|
||||||
};
|
};
|
||||||
|
@ -720,7 +714,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const tracker of trackerFilterList.children)
|
for (const tracker of trackerFilterList.children)
|
||||||
tracker.classList.toggle("selectedFilter", (Number(tracker.id) === selectedTracker));
|
tracker.classList.toggle("selectedFilter", (tracker.id === selectedTracker));
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusSortOrder = Object.freeze({
|
const statusSortOrder = Object.freeze({
|
||||||
|
@ -768,7 +762,7 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
let torrentsTableSelectedRows;
|
let torrentsTableSelectedRows;
|
||||||
let updateStatuses = false;
|
let updateStatuses = false;
|
||||||
let update_categories = false;
|
let updateCategories = false;
|
||||||
let updateTags = false;
|
let updateTags = false;
|
||||||
let updateTrackers = false;
|
let updateTrackers = false;
|
||||||
let updateTorrents = false;
|
let updateTorrents = false;
|
||||||
|
@ -776,76 +770,64 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
if (fullUpdate) {
|
if (fullUpdate) {
|
||||||
torrentsTableSelectedRows = torrentsTable.selectedRowsIds();
|
torrentsTableSelectedRows = torrentsTable.selectedRowsIds();
|
||||||
updateStatuses = true;
|
updateStatuses = true;
|
||||||
update_categories = true;
|
updateCategories = true;
|
||||||
updateTags = true;
|
updateTags = true;
|
||||||
updateTrackers = true;
|
updateTrackers = true;
|
||||||
updateTorrents = true;
|
updateTorrents = true;
|
||||||
torrentsTable.clear();
|
torrentsTable.clear();
|
||||||
category_list.clear();
|
categoryMap.clear();
|
||||||
tagList.clear();
|
tagMap.clear();
|
||||||
trackerList.clear();
|
trackerMap.clear();
|
||||||
}
|
}
|
||||||
if (responseJSON["rid"])
|
if (responseJSON["rid"])
|
||||||
syncMainDataLastResponseId = responseJSON["rid"];
|
syncMainDataLastResponseId = responseJSON["rid"];
|
||||||
if (responseJSON["categories"]) {
|
if (responseJSON["categories"]) {
|
||||||
for (const key in responseJSON["categories"]) {
|
for (const responseName in responseJSON["categories"]) {
|
||||||
if (!Object.hasOwn(responseJSON["categories"], key))
|
if (!Object.hasOwn(responseJSON["categories"], responseName))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const responseCategory = responseJSON["categories"][key];
|
const responseData = responseJSON["categories"][responseName];
|
||||||
const categoryHash = window.qBittorrent.Misc.genHash(key);
|
const categoryData = categoryMap.get(responseName);
|
||||||
const category = category_list.get(categoryHash);
|
if (categoryData === undefined) {
|
||||||
if (category !== undefined) {
|
categoryMap.set(responseName, {
|
||||||
// only the save path can change for existing categories
|
savePath: responseData.savePath,
|
||||||
category.savePath = responseCategory.savePath;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
category_list.set(categoryHash, {
|
|
||||||
name: responseCategory.name,
|
|
||||||
savePath: responseCategory.savePath,
|
|
||||||
torrents: new Set()
|
torrents: new Set()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// only the save path can change for existing categories
|
||||||
|
categoryData.savePath = responseData.savePath;
|
||||||
}
|
}
|
||||||
update_categories = true;
|
}
|
||||||
|
updateCategories = true;
|
||||||
}
|
}
|
||||||
if (responseJSON["categories_removed"]) {
|
if (responseJSON["categories_removed"]) {
|
||||||
responseJSON["categories_removed"].each((category) => {
|
for (const category of responseJSON["categories_removed"])
|
||||||
const categoryHash = window.qBittorrent.Misc.genHash(category);
|
categoryMap.delete(category);
|
||||||
category_list.delete(categoryHash);
|
updateCategories = true;
|
||||||
});
|
|
||||||
update_categories = true;
|
|
||||||
}
|
}
|
||||||
if (responseJSON["tags"]) {
|
if (responseJSON["tags"]) {
|
||||||
for (const tag of responseJSON["tags"]) {
|
for (const tag of responseJSON["tags"]) {
|
||||||
const tagHash = window.qBittorrent.Misc.genHash(tag);
|
if (!tagMap.has(tag))
|
||||||
if (!tagList.has(tagHash)) {
|
tagMap.set(tag, new Set());
|
||||||
tagList.set(tagHash, {
|
|
||||||
name: tag,
|
|
||||||
torrents: new Set()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
updateTags = true;
|
updateTags = true;
|
||||||
}
|
}
|
||||||
if (responseJSON["tags_removed"]) {
|
if (responseJSON["tags_removed"]) {
|
||||||
for (let i = 0; i < responseJSON["tags_removed"].length; ++i) {
|
for (const tag of responseJSON["tags_removed"])
|
||||||
const tagHash = window.qBittorrent.Misc.genHash(responseJSON["tags_removed"][i]);
|
tagMap.delete(tag);
|
||||||
tagList.delete(tagHash);
|
|
||||||
}
|
|
||||||
updateTags = true;
|
updateTags = true;
|
||||||
}
|
}
|
||||||
if (responseJSON["trackers"]) {
|
if (responseJSON["trackers"]) {
|
||||||
for (const [tracker, torrents] of Object.entries(responseJSON["trackers"])) {
|
for (const [tracker, torrents] of Object.entries(responseJSON["trackers"])) {
|
||||||
const host = window.qBittorrent.Misc.getHost(tracker);
|
const host = window.qBittorrent.Misc.getHost(tracker);
|
||||||
const hash = window.qBittorrent.Misc.genHash(host);
|
|
||||||
|
|
||||||
let trackerListItem = trackerList.get(hash);
|
let trackerListItem = trackerMap.get(host);
|
||||||
if (trackerListItem === undefined) {
|
if (trackerListItem === undefined) {
|
||||||
trackerListItem = { host: host, trackerTorrentMap: new Map() };
|
trackerListItem = new Map();
|
||||||
trackerList.set(hash, trackerListItem);
|
trackerMap.set(host, trackerListItem);
|
||||||
}
|
}
|
||||||
trackerListItem.trackerTorrentMap.set(tracker, new Set(torrents));
|
trackerListItem.set(tracker, new Set(torrents));
|
||||||
}
|
}
|
||||||
updateTrackers = true;
|
updateTrackers = true;
|
||||||
}
|
}
|
||||||
|
@ -853,16 +835,16 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
for (let i = 0; i < responseJSON["trackers_removed"].length; ++i) {
|
for (let i = 0; i < responseJSON["trackers_removed"].length; ++i) {
|
||||||
const tracker = responseJSON["trackers_removed"][i];
|
const tracker = responseJSON["trackers_removed"][i];
|
||||||
const host = window.qBittorrent.Misc.getHost(tracker);
|
const host = window.qBittorrent.Misc.getHost(tracker);
|
||||||
const hash = window.qBittorrent.Misc.genHash(host);
|
|
||||||
const trackerListEntry = trackerList.get(hash);
|
const trackerTorrentMap = trackerMap.get(host);
|
||||||
if (trackerListEntry) {
|
if (trackerTorrentMap !== undefined) {
|
||||||
trackerListEntry.trackerTorrentMap.delete(tracker);
|
trackerTorrentMap.delete(tracker);
|
||||||
// Remove unused trackers
|
// Remove unused trackers
|
||||||
if (trackerListEntry.trackerTorrentMap.size === 0) {
|
if (trackerTorrentMap.size === 0) {
|
||||||
trackerList.delete(hash);
|
trackerMap.delete(host);
|
||||||
if (selectedTracker === hash) {
|
if (selectedTracker === host) {
|
||||||
selectedTracker = TRACKERS_ALL;
|
selectedTracker = TRACKERS_ALL;
|
||||||
LocalPreferences.set("selected_tracker", selectedTracker.toString());
|
LocalPreferences.set("selected_tracker", selectedTracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -884,9 +866,10 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
}
|
}
|
||||||
torrentsTable.updateRowData(responseJSON["torrents"][key]);
|
torrentsTable.updateRowData(responseJSON["torrents"][key]);
|
||||||
if (addTorrentToCategoryList(responseJSON["torrents"][key]))
|
if (addTorrentToCategoryList(responseJSON["torrents"][key]))
|
||||||
update_categories = true;
|
updateCategories = true;
|
||||||
if (addTorrentToTagList(responseJSON["torrents"][key]))
|
if (addTorrentToTagList(responseJSON["torrents"][key]))
|
||||||
updateTags = true;
|
updateTags = true;
|
||||||
|
updateTrackers = true;
|
||||||
updateTorrents = true;
|
updateTorrents = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -894,9 +877,10 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
responseJSON["torrents_removed"].each((hash) => {
|
responseJSON["torrents_removed"].each((hash) => {
|
||||||
torrentsTable.removeRow(hash);
|
torrentsTable.removeRow(hash);
|
||||||
removeTorrentFromCategoryList(hash);
|
removeTorrentFromCategoryList(hash);
|
||||||
update_categories = true; // Always to update All category
|
updateCategories = true; // Always to update All category
|
||||||
removeTorrentFromTagList(hash);
|
removeTorrentFromTagList(hash);
|
||||||
updateTags = true; // Always to update All tag
|
updateTags = true; // Always to update All tag
|
||||||
|
updateTrackers = true;
|
||||||
});
|
});
|
||||||
updateTorrents = true;
|
updateTorrents = true;
|
||||||
updateStatuses = true;
|
updateStatuses = true;
|
||||||
|
@ -919,13 +903,13 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
if (updateStatuses)
|
if (updateStatuses)
|
||||||
updateFiltersList();
|
updateFiltersList();
|
||||||
|
|
||||||
if (update_categories) {
|
if (updateCategories) {
|
||||||
updateCategoryList();
|
updateCategoryList();
|
||||||
window.qBittorrent.TransferList.contextMenu.updateCategoriesSubMenu(category_list);
|
window.qBittorrent.TransferList.contextMenu.updateCategoriesSubMenu(categoryMap);
|
||||||
}
|
}
|
||||||
if (updateTags) {
|
if (updateTags) {
|
||||||
updateTagList();
|
updateTagList();
|
||||||
window.qBittorrent.TransferList.contextMenu.updateTagsSubMenu(tagList);
|
window.qBittorrent.TransferList.contextMenu.updateTagsSubMenu(tagMap);
|
||||||
}
|
}
|
||||||
if (updateTrackers)
|
if (updateTrackers)
|
||||||
updateTrackerList();
|
updateTrackerList();
|
||||||
|
|
|
@ -457,25 +457,25 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
this.setEnabled("copyInfohash2", thereAreV2Hashes);
|
this.setEnabled("copyInfohash2", thereAreV2Hashes);
|
||||||
|
|
||||||
const contextTagList = $("contextTagList");
|
const contextTagList = $("contextTagList");
|
||||||
tagList.forEach((tag, tagHash) => {
|
for (const tag of tagMap.keys()) {
|
||||||
const checkbox = contextTagList.querySelector(`a[href="#Tag/${tag.name}"] input[type="checkbox"]`);
|
const checkbox = contextTagList.querySelector(`a[href="#Tag/${tag}"] input[type="checkbox"]`);
|
||||||
const count = tagCount.get(tag.name);
|
const count = tagCount.get(tag);
|
||||||
const hasCount = (count !== undefined);
|
const hasCount = (count !== undefined);
|
||||||
const isLesser = (count < selectedRows.length);
|
const isLesser = (count < selectedRows.length);
|
||||||
checkbox.indeterminate = (hasCount ? isLesser : false);
|
checkbox.indeterminate = (hasCount ? isLesser : false);
|
||||||
checkbox.checked = (hasCount ? !isLesser : false);
|
checkbox.checked = (hasCount ? !isLesser : false);
|
||||||
});
|
|
||||||
|
|
||||||
const contextCategoryList = document.getElementById("contextCategoryList");
|
|
||||||
category_list.forEach((category, categoryHash) => {
|
|
||||||
const categoryIcon = contextCategoryList.querySelector(`a[href$="#Category/${category.name}"] img`);
|
|
||||||
const count = categoryCount.get(category.name);
|
|
||||||
const isEqual = ((count !== undefined) && (count === selectedRows.length));
|
|
||||||
categoryIcon.classList.toggle("highlightedCategoryIcon", isEqual);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCategoriesSubMenu(categoryList) {
|
const contextCategoryList = document.getElementById("contextCategoryList");
|
||||||
|
for (const category of categoryMap.keys()) {
|
||||||
|
const categoryIcon = contextCategoryList.querySelector(`a[href$="#Category/${category}"] img`);
|
||||||
|
const count = categoryCount.get(category);
|
||||||
|
const isEqual = ((count !== undefined) && (count === selectedRows.length));
|
||||||
|
categoryIcon.classList.toggle("highlightedCategoryIcon", isEqual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCategoriesSubMenu(categories) {
|
||||||
const contextCategoryList = $("contextCategoryList");
|
const contextCategoryList = $("contextCategoryList");
|
||||||
contextCategoryList.getChildren().each(c => c.destroy());
|
contextCategoryList.getChildren().each(c => c.destroy());
|
||||||
|
|
||||||
|
@ -495,24 +495,19 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
contextCategoryList.appendChild(createMenuItem("QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentNewCategoryFN));
|
contextCategoryList.appendChild(createMenuItem("QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentNewCategoryFN));
|
||||||
contextCategoryList.appendChild(createMenuItem("QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", () => { torrentSetCategoryFN(0); }));
|
contextCategoryList.appendChild(createMenuItem("QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", () => { torrentSetCategoryFN(""); }));
|
||||||
|
|
||||||
const sortedCategories = [];
|
const sortedCategories = [...categories.keys()];
|
||||||
categoryList.forEach((category, hash) => sortedCategories.push({
|
sortedCategories.sort(window.qBittorrent.Misc.naturalSortCollator.compare);
|
||||||
categoryName: category.name,
|
|
||||||
categoryHash: hash
|
|
||||||
}));
|
|
||||||
sortedCategories.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(
|
|
||||||
left.categoryName, right.categoryName));
|
|
||||||
|
|
||||||
let first = true;
|
let first = true;
|
||||||
for (const { categoryName, categoryHash } of sortedCategories) {
|
for (const categoryName of sortedCategories) {
|
||||||
const anchor = document.createElement("a");
|
const anchor = document.createElement("a");
|
||||||
anchor.href = `#Category/${categoryName}`;
|
anchor.href = `#Category/${categoryName}`;
|
||||||
anchor.textContent = categoryName;
|
anchor.textContent = categoryName;
|
||||||
anchor.addEventListener("click", (event) => {
|
anchor.addEventListener("click", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
torrentSetCategoryFN(categoryHash);
|
torrentSetCategoryFN(categoryName);
|
||||||
});
|
});
|
||||||
|
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
|
@ -530,7 +525,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTagsSubMenu(tagList) {
|
updateTagsSubMenu(tags) {
|
||||||
const contextTagList = $("contextTagList");
|
const contextTagList = $("contextTagList");
|
||||||
contextTagList.replaceChildren();
|
contextTagList.replaceChildren();
|
||||||
|
|
||||||
|
@ -552,15 +547,11 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
contextTagList.appendChild(createMenuItem("QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentAddTagsFN));
|
contextTagList.appendChild(createMenuItem("QBT_TR(Add...)QBT_TR[CONTEXT=TransferListWidget]", "images/list-add.svg", torrentAddTagsFN));
|
||||||
contextTagList.appendChild(createMenuItem("QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", torrentRemoveAllTagsFN));
|
contextTagList.appendChild(createMenuItem("QBT_TR(Remove All)QBT_TR[CONTEXT=TransferListWidget]", "images/edit-clear.svg", torrentRemoveAllTagsFN));
|
||||||
|
|
||||||
const sortedTags = [];
|
const sortedTags = [...tags.keys()];
|
||||||
tagList.forEach((tag, hash) => sortedTags.push({
|
sortedTags.sort(window.qBittorrent.Misc.naturalSortCollator.compare);
|
||||||
tagName: tag.name,
|
|
||||||
tagHash: hash
|
|
||||||
}));
|
|
||||||
sortedTags.sort((left, right) => window.qBittorrent.Misc.naturalSortCollator.compare(left.tagName, right.tagName));
|
|
||||||
|
|
||||||
for (let i = 0; i < sortedTags.length; ++i) {
|
for (let i = 0; i < sortedTags.length; ++i) {
|
||||||
const { tagName, tagHash } = sortedTags[i];
|
const tagName = sortedTags[i];
|
||||||
|
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "checkbox";
|
input.type = "checkbox";
|
||||||
|
@ -573,7 +564,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
anchor.textContent = tagName;
|
anchor.textContent = tagName;
|
||||||
anchor.addEventListener("click", (event) => {
|
anchor.addEventListener("click", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
torrentSetTagsFN(tagHash, !input.checked);
|
torrentSetTagsFN(tagName, !input.checked);
|
||||||
});
|
});
|
||||||
anchor.prepend(input);
|
anchor.prepend(input);
|
||||||
|
|
||||||
|
@ -595,7 +586,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
|
|
||||||
class CategoriesFilterContextMenu extends FilterListContextMenu {
|
class CategoriesFilterContextMenu extends FilterListContextMenu {
|
||||||
updateMenuItems() {
|
updateMenuItems() {
|
||||||
const id = Number(this.options.element.id);
|
const id = this.options.element.id;
|
||||||
if ((id !== CATEGORIES_ALL) && (id !== CATEGORIES_UNCATEGORIZED)) {
|
if ((id !== CATEGORIES_ALL) && (id !== CATEGORIES_UNCATEGORIZED)) {
|
||||||
this.showItem("editCategory");
|
this.showItem("editCategory");
|
||||||
this.showItem("deleteCategory");
|
this.showItem("deleteCategory");
|
||||||
|
@ -616,7 +607,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
|
|
||||||
class TagsFilterContextMenu extends FilterListContextMenu {
|
class TagsFilterContextMenu extends FilterListContextMenu {
|
||||||
updateMenuItems() {
|
updateMenuItems() {
|
||||||
const id = Number(this.options.element.id);
|
const id = this.options.element.id;
|
||||||
if ((id !== TAGS_ALL) && (id !== TAGS_UNTAGGED))
|
if ((id !== TAGS_ALL) && (id !== TAGS_UNTAGGED))
|
||||||
this.showItem("deleteTag");
|
this.showItem("deleteTag");
|
||||||
else
|
else
|
||||||
|
@ -628,7 +619,7 @@ window.qBittorrent.ContextMenu ??= (() => {
|
||||||
|
|
||||||
class TrackersFilterContextMenu extends FilterListContextMenu {
|
class TrackersFilterContextMenu extends FilterListContextMenu {
|
||||||
updateMenuItems() {
|
updateMenuItems() {
|
||||||
const id = Number(this.options.element.id);
|
const id = this.options.element.id;
|
||||||
if ((id !== TRACKERS_ALL) && (id !== TRACKERS_TRACKERLESS))
|
if ((id !== TRACKERS_ALL) && (id !== TRACKERS_TRACKERLESS))
|
||||||
this.showItem("deleteTracker");
|
this.showItem("deleteTracker");
|
||||||
else
|
else
|
||||||
|
|
|
@ -1451,7 +1451,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
applyFilter: (row, filterName, categoryHash, tagHash, trackerHash, filterTerms) => {
|
applyFilter: (row, filterName, category, tag, tracker, filterTerms) => {
|
||||||
const state = row["full_data"].state;
|
const state = row["full_data"].state;
|
||||||
let inactive = false;
|
let inactive = false;
|
||||||
|
|
||||||
|
@ -1515,59 +1515,64 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (categoryHash) {
|
switch (category) {
|
||||||
case CATEGORIES_ALL:
|
case CATEGORIES_ALL:
|
||||||
break; // do nothing
|
break; // do nothing
|
||||||
|
|
||||||
case CATEGORIES_UNCATEGORIZED:
|
case CATEGORIES_UNCATEGORIZED:
|
||||||
if (row["full_data"].category.length !== 0)
|
if (row["full_data"].category.length > 0)
|
||||||
return false;
|
return false;
|
||||||
break; // do nothing
|
break; // do nothing
|
||||||
default:
|
|
||||||
|
default: {
|
||||||
if (!useSubcategories) {
|
if (!useSubcategories) {
|
||||||
if (categoryHash !== window.qBittorrent.Misc.genHash(row["full_data"].category))
|
if (category !== row["full_data"].category)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const selectedCategory = category_list.get(categoryHash);
|
const selectedCategory = categoryMap.get(category);
|
||||||
if (selectedCategory !== undefined) {
|
if (selectedCategory !== undefined) {
|
||||||
const selectedCategoryName = selectedCategory.name + "/";
|
const selectedCategoryName = `${category}/`;
|
||||||
const torrentCategoryName = row["full_data"].category + "/";
|
const torrentCategoryName = `${row["full_data"].category}/`;
|
||||||
if (!torrentCategoryName.startsWith(selectedCategoryName))
|
if (!torrentCategoryName.startsWith(selectedCategoryName))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (tagHash) {
|
switch (tag) {
|
||||||
case TAGS_ALL:
|
case TAGS_ALL:
|
||||||
break; // do nothing
|
break; // do nothing
|
||||||
|
|
||||||
case TAGS_UNTAGGED:
|
case TAGS_UNTAGGED:
|
||||||
if (row["full_data"].tags.length !== 0)
|
if (row["full_data"].tags.length > 0)
|
||||||
return false;
|
return false;
|
||||||
break; // do nothing
|
break; // do nothing
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
const tagHashes = row["full_data"].tags.split(", ").map(tag => window.qBittorrent.Misc.genHash(tag));
|
const tags = row["full_data"].tags.split(", ");
|
||||||
if (!tagHashes.contains(tagHash))
|
if (!tags.contains(tag))
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (trackerHash) {
|
switch (tracker) {
|
||||||
case TRACKERS_ALL:
|
case TRACKERS_ALL:
|
||||||
break; // do nothing
|
break; // do nothing
|
||||||
|
|
||||||
case TRACKERS_TRACKERLESS:
|
case TRACKERS_TRACKERLESS:
|
||||||
if (row["full_data"].trackers_count !== 0)
|
if (row["full_data"].trackers_count > 0)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
const tracker = trackerList.get(trackerHash);
|
const trackerTorrentMap = trackerMap.get(tracker);
|
||||||
if (tracker) {
|
if (trackerTorrentMap !== undefined) {
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const torrents of tracker.trackerTorrentMap.values()) {
|
for (const torrents of trackerTorrentMap.values()) {
|
||||||
if (torrents.has(row["full_data"].rowId)) {
|
if (torrents.has(row["full_data"].rowId)) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1596,17 +1601,17 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
getFilteredTorrentsNumber: function(filterName, categoryHash, tagHash, trackerHash) {
|
getFilteredTorrentsNumber: function(filterName, category, tag, tracker) {
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
|
|
||||||
for (const row of this.rows.values()) {
|
for (const row of this.rows.values()) {
|
||||||
if (this.applyFilter(row, filterName, categoryHash, tagHash, trackerHash, null))
|
if (this.applyFilter(row, filterName, category, tag, tracker, null))
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
return cnt;
|
return cnt;
|
||||||
},
|
},
|
||||||
|
|
||||||
getFilteredTorrentsHashes: function(filterName, categoryHash, tagHash, trackerHash) {
|
getFilteredTorrentsHashes: function(filterName, category, tag, tracker) {
|
||||||
const rowsHashes = [];
|
const rowsHashes = [];
|
||||||
const useRegex = document.getElementById("torrentsFilterRegexBox").checked;
|
const useRegex = document.getElementById("torrentsFilterRegexBox").checked;
|
||||||
const filterText = document.getElementById("torrentsFilterInput").value.trim().toLowerCase();
|
const filterText = document.getElementById("torrentsFilterInput").value.trim().toLowerCase();
|
||||||
|
@ -1621,7 +1626,7 @@ window.qBittorrent.DynamicTable ??= (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const row of this.rows.values()) {
|
for (const row of this.rows.values()) {
|
||||||
if (this.applyFilter(row, filterName, categoryHash, tagHash, trackerHash, filterTerms))
|
if (this.applyFilter(row, filterName, category, tag, tracker, filterTerms))
|
||||||
rowsHashes.push(row["rowId"]);
|
rowsHashes.push(row["rowId"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ window.qBittorrent ??= {};
|
||||||
window.qBittorrent.Misc ??= (() => {
|
window.qBittorrent.Misc ??= (() => {
|
||||||
const exports = () => {
|
const exports = () => {
|
||||||
return {
|
return {
|
||||||
genHash: genHash,
|
|
||||||
getHost: getHost,
|
getHost: getHost,
|
||||||
createDebounceHandler: createDebounceHandler,
|
createDebounceHandler: createDebounceHandler,
|
||||||
friendlyUnit: friendlyUnit,
|
friendlyUnit: friendlyUnit,
|
||||||
|
@ -54,16 +53,6 @@ window.qBittorrent.Misc ??= (() => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const genHash = (string) => {
|
|
||||||
// origins:
|
|
||||||
// https://stackoverflow.com/a/8831937
|
|
||||||
// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0
|
|
||||||
let hash = 0;
|
|
||||||
for (let i = 0; i < string.length; ++i)
|
|
||||||
hash = ((Math.imul(hash, 31) + string.charCodeAt(i)) | 0);
|
|
||||||
return hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
// getHost emulate the GUI version `QString getHost(const QString &url)`
|
// getHost emulate the GUI version `QString getHost(const QString &url)`
|
||||||
const getHost = (url) => {
|
const getHost = (url) => {
|
||||||
// We want the hostname.
|
// We want the hostname.
|
||||||
|
|
|
@ -780,15 +780,16 @@ const initializeWindows = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
torrentNewCategoryFN = () => {
|
torrentNewCategoryFN = () => {
|
||||||
const action = "set";
|
|
||||||
const hashes = torrentsTable.selectedRowsIds();
|
const hashes = torrentsTable.selectedRowsIds();
|
||||||
if (hashes.length) {
|
if (hashes.length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "newCategoryPage",
|
id: "newCategoryPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(New Category)QBT_TR[CONTEXT=TransferListWidget]",
|
title: "QBT_TR(New Category)QBT_TR[CONTEXT=TransferListWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newcategory.html").setData("action", action).setData("hashes", hashes.join("|")).toString(),
|
contentURL: new URI("newcategory.html").setData("action", "set").setData("hashes", hashes.join("|")).toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -797,22 +798,18 @@ const initializeWindows = () => {
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 150
|
height: 150
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
torrentSetCategoryFN = (categoryHash) => {
|
torrentSetCategoryFN = (category) => {
|
||||||
const hashes = torrentsTable.selectedRowsIds();
|
const hashes = torrentsTable.selectedRowsIds();
|
||||||
if (hashes.length <= 0)
|
if (hashes.length <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const categoryName = category_list.has(categoryHash)
|
|
||||||
? category_list.get(categoryHash).name
|
|
||||||
: "";
|
|
||||||
fetch("api/v2/torrents/setCategory", {
|
fetch("api/v2/torrents/setCategory", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
hashes: hashes.join("|"),
|
hashes: hashes.join("|"),
|
||||||
category: categoryName
|
category: category
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
@ -824,13 +821,12 @@ const initializeWindows = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
createCategoryFN = () => {
|
createCategoryFN = () => {
|
||||||
const action = "create";
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "newCategoryPage",
|
id: "newCategoryPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]",
|
title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newcategory.html").setData("action", action).toString(),
|
contentURL: new URI("newcategory.html").setData("action", "create").toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -841,15 +837,13 @@ const initializeWindows = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
createSubcategoryFN = (categoryHash) => {
|
createSubcategoryFN = (category) => {
|
||||||
const action = "createSubcategory";
|
|
||||||
const categoryName = category_list.get(categoryHash).name + "/";
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "newSubcategoryPage",
|
id: "newSubcategoryPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]",
|
title: "QBT_TR(New Category)QBT_TR[CONTEXT=CategoryFilterWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", categoryName).toString(),
|
contentURL: new URI("newcategory.html").setData("action", "createSubcategory").setData("categoryName", `${category}/`).toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -860,15 +854,13 @@ const initializeWindows = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
editCategoryFN = (categoryHash) => {
|
editCategoryFN = (category) => {
|
||||||
const action = "edit";
|
|
||||||
const category = category_list.get(categoryHash);
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "editCategoryPage",
|
id: "editCategoryPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(Edit Category)QBT_TR[CONTEXT=TransferListWidget]",
|
title: "QBT_TR(Edit Category)QBT_TR[CONTEXT=TransferListWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newcategory.html").setData("action", action).setData("categoryName", category.name).setData("savePath", category.savePath).toString(),
|
contentURL: new URI("newcategory.html").setData("action", "edit").setData("categoryName", category).setData("savePath", categoryMap.get(category).savePath).toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -879,11 +871,11 @@ const initializeWindows = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
removeCategoryFN = (categoryHash) => {
|
removeCategoryFN = (category) => {
|
||||||
fetch("api/v2/torrents/removeCategories", {
|
fetch("api/v2/torrents/removeCategories", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
categories: category_list.get(categoryHash).name
|
categories: category
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
@ -897,10 +889,10 @@ const initializeWindows = () => {
|
||||||
|
|
||||||
deleteUnusedCategoriesFN = () => {
|
deleteUnusedCategoriesFN = () => {
|
||||||
const categories = [];
|
const categories = [];
|
||||||
category_list.forEach((category, hash) => {
|
for (const category of categoryMap.keys()) {
|
||||||
if (torrentsTable.getFilteredTorrentsNumber("all", hash, TAGS_ALL, TRACKERS_ALL) === 0)
|
if (torrentsTable.getFilteredTorrentsNumber("all", category, TAGS_ALL, TRACKERS_ALL) === 0)
|
||||||
categories.push(category.name);
|
categories.push(category);
|
||||||
});
|
}
|
||||||
fetch("api/v2/torrents/removeCategories", {
|
fetch("api/v2/torrents/removeCategories", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
|
@ -917,15 +909,16 @@ const initializeWindows = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
torrentAddTagsFN = () => {
|
torrentAddTagsFN = () => {
|
||||||
const action = "set";
|
|
||||||
const hashes = torrentsTable.selectedRowsIds();
|
const hashes = torrentsTable.selectedRowsIds();
|
||||||
if (hashes.length) {
|
if (hashes.length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "newTagPage",
|
id: "newTagPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(Add tags)QBT_TR[CONTEXT=TransferListWidget]",
|
title: "QBT_TR(Add tags)QBT_TR[CONTEXT=TransferListWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newtag.html").setData("action", action).setData("hashes", hashes.join("|")).toString(),
|
contentURL: new URI("newtag.html").setData("action", "set").setData("hashes", hashes.join("|")).toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -934,10 +927,9 @@ const initializeWindows = () => {
|
||||||
width: 250,
|
width: 250,
|
||||||
height: 100
|
height: 100
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
torrentSetTagsFN = (tagHash, isSet) => {
|
torrentSetTagsFN = (tag, isSet) => {
|
||||||
const hashes = torrentsTable.selectedRowsIds();
|
const hashes = torrentsTable.selectedRowsIds();
|
||||||
if (hashes.length <= 0)
|
if (hashes.length <= 0)
|
||||||
return;
|
return;
|
||||||
|
@ -946,7 +938,7 @@ const initializeWindows = () => {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
hashes: hashes.join("|"),
|
hashes: hashes.join("|"),
|
||||||
tags: (tagList.get(tagHash)?.name || "")
|
tags: tag
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -964,13 +956,12 @@ const initializeWindows = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
createTagFN = () => {
|
createTagFN = () => {
|
||||||
const action = "create";
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "newTagPage",
|
id: "newTagPage",
|
||||||
icon: "images/qbittorrent-tray.svg",
|
icon: "images/qbittorrent-tray.svg",
|
||||||
title: "QBT_TR(New Tag)QBT_TR[CONTEXT=TagFilterWidget]",
|
title: "QBT_TR(New Tag)QBT_TR[CONTEXT=TagFilterWidget]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("newtag.html").setData("action", action).toString(),
|
contentURL: new URI("newtag.html").setData("action", "create").toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
@ -982,11 +973,11 @@ const initializeWindows = () => {
|
||||||
updateMainData();
|
updateMainData();
|
||||||
};
|
};
|
||||||
|
|
||||||
removeTagFN = (tagHash) => {
|
removeTagFN = (tag) => {
|
||||||
fetch("api/v2/torrents/deleteTags", {
|
fetch("api/v2/torrents/deleteTags", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
tags: tagList.get(tagHash).name
|
tags: tag
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
setTagFilter(TAGS_ALL);
|
setTagFilter(TAGS_ALL);
|
||||||
|
@ -994,10 +985,10 @@ const initializeWindows = () => {
|
||||||
|
|
||||||
deleteUnusedTagsFN = () => {
|
deleteUnusedTagsFN = () => {
|
||||||
const tags = [];
|
const tags = [];
|
||||||
tagList.forEach((tag, hash) => {
|
for (const tag of tagMap.keys()) {
|
||||||
if (torrentsTable.getFilteredTorrentsNumber("all", CATEGORIES_ALL, hash, TRACKERS_ALL) === 0)
|
if (torrentsTable.getFilteredTorrentsNumber("all", CATEGORIES_ALL, tag, TRACKERS_ALL) === 0)
|
||||||
tags.push(tag.name);
|
tags.push(tag);
|
||||||
});
|
}
|
||||||
fetch("api/v2/torrents/deleteTags", {
|
fetch("api/v2/torrents/deleteTags", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: new URLSearchParams({
|
body: new URLSearchParams({
|
||||||
|
@ -1007,20 +998,16 @@ const initializeWindows = () => {
|
||||||
setTagFilter(TAGS_ALL);
|
setTagFilter(TAGS_ALL);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteTrackerFN = (trackerHash) => {
|
deleteTrackerFN = (trackerHost) => {
|
||||||
const trackerHashInt = Number(trackerHash);
|
if ((trackerHost === TRACKERS_ALL) || (trackerHost === TRACKERS_TRACKERLESS))
|
||||||
if ((trackerHashInt === TRACKERS_ALL) || (trackerHashInt === TRACKERS_TRACKERLESS))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const tracker = trackerList.get(trackerHashInt);
|
const trackerURLs = [...trackerMap.get(trackerHost).keys()].map(encodeURIComponent).join("|");
|
||||||
const host = tracker.host;
|
|
||||||
const urls = [...tracker.trackerTorrentMap.keys()];
|
|
||||||
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: "confirmDeletionPage",
|
id: "confirmDeletionPage",
|
||||||
title: "QBT_TR(Remove tracker)QBT_TR[CONTEXT=confirmDeletionDlg]",
|
title: "QBT_TR(Remove tracker)QBT_TR[CONTEXT=confirmDeletionDlg]",
|
||||||
loadMethod: "iframe",
|
loadMethod: "iframe",
|
||||||
contentURL: new URI("confirmtrackerdeletion.html").setData("host", host).setData("urls", urls.map(encodeURIComponent).join("|")).toString(),
|
contentURL: new URI("confirmtrackerdeletion.html").setData("host", trackerHost).setData("urls", trackerURLs).toString(),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
|
|
|
@ -118,13 +118,13 @@
|
||||||
createCategoryFN();
|
createCategoryFN();
|
||||||
},
|
},
|
||||||
createSubcategory: (element, ref) => {
|
createSubcategory: (element, ref) => {
|
||||||
createSubcategoryFN(Number(element.id));
|
createSubcategoryFN(element.id);
|
||||||
},
|
},
|
||||||
editCategory: (element, ref) => {
|
editCategory: (element, ref) => {
|
||||||
editCategoryFN(Number(element.id));
|
editCategoryFN(element.id);
|
||||||
},
|
},
|
||||||
deleteCategory: (element, ref) => {
|
deleteCategory: (element, ref) => {
|
||||||
removeCategoryFN(Number(element.id));
|
removeCategoryFN(element.id);
|
||||||
},
|
},
|
||||||
deleteUnusedCategories: (element, ref) => {
|
deleteUnusedCategories: (element, ref) => {
|
||||||
deleteUnusedCategoriesFN();
|
deleteUnusedCategoriesFN();
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
createTagFN();
|
createTagFN();
|
||||||
},
|
},
|
||||||
deleteTag: (element, ref) => {
|
deleteTag: (element, ref) => {
|
||||||
removeTagFN(Number(element.id));
|
removeTagFN(element.id);
|
||||||
},
|
},
|
||||||
deleteUnusedTags: (element, ref) => {
|
deleteUnusedTags: (element, ref) => {
|
||||||
deleteUnusedTagsFN();
|
deleteUnusedTagsFN();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue