Commit d9aeafc3 authored by Andrea Giammarchi's avatar Andrea Giammarchi Committed by Winsley

Issue 7231 - Cumulative fixes for io-filter-search

Cumulative patches for #308 #302 #293 #291 #289 #283 #280
parent 42a90914
...@@ -48,17 +48,11 @@ io-filter-table > io-filter-list > table ...@@ -48,17 +48,11 @@ io-filter-table > io-filter-list > table
io-filter-table .footer io-filter-table .footer
{ {
display: flex; display: flex;
visibility: hidden;
flex-direction: row; flex-direction: row;
margin-top: 16px; margin-top: 16px;
align-items: center; align-items: center;
} }
io-filter-table .footer.visible
{
visibility: visible;
}
io-filter-table .footer button io-filter-table .footer button
{ {
display: inline-flex; display: inline-flex;
...@@ -68,6 +62,11 @@ io-filter-table .footer button ...@@ -68,6 +62,11 @@ io-filter-table .footer button
align-content: center; align-content: center;
} }
io-filter-table .footer button[disabled]
{
visibility: hidden;
}
io-filter-table .footer button:not(:last-child) io-filter-table .footer button:not(:last-child)
{ {
text-transform: uppercase; text-transform: uppercase;
......
...@@ -684,14 +684,14 @@ function setupPort() ...@@ -684,14 +684,14 @@ function setupPort()
{ {
if (message.type === "filters.respond" && message.action === "disabled") if (message.type === "filters.respond" && message.action === "disabled")
{ {
const [beFilter, value] = message.args; const {text, disabled} = message.args[0];
const filter = this.filters.find(f => f.text === beFilter.text); const filter = this.filters.find(f => f.text === text);
if (value !== filter.disabled) if (disabled !== filter.disabled)
{ {
dispatchError.call(this, "filter.disabled", filter); dispatchError.call(this, "filter.disabled", filter);
filter.disabled = value; filter.disabled = disabled;
this.render();
} }
this.render();
} }
}); });
} }
......
...@@ -108,7 +108,7 @@ class IOFilterSearch extends IOElement ...@@ -108,7 +108,7 @@ class IOFilterSearch extends IOElement
onclick() onclick()
{ {
if (this.value) if (this.value)
dispatch.call(this, "filter:add", this.value); addFilter.call(this, this.value);
} }
ondrop(event) ondrop(event)
...@@ -139,10 +139,16 @@ class IOFilterSearch extends IOElement ...@@ -139,10 +139,16 @@ class IOFilterSearch extends IOElement
onkeyup() onkeyup()
{ {
const {match, value} = this; const {match, value} = this;
// clear on backspace
if (!value.length)
{
this.value = "";
return;
}
// no match means don't validate // no match means don't validate
// but also multi line (paste on old browsers) // but also multi line (paste on old browsers)
// shouldn't pass through this logic (filtered later on) // shouldn't pass through this logic (filtered later on)
if (!match || !value || value.includes("\n")) if (!match || value.includes("\n"))
return; return;
clearTimeout(this._timer); clearTimeout(this._timer);
// debounce the search to avoid degrading // debounce the search to avoid degrading
...@@ -159,7 +165,10 @@ class IOFilterSearch extends IOElement ...@@ -159,7 +165,10 @@ class IOFilterSearch extends IOElement
onpaste(event) onpaste(event)
{ {
const clipboardData = event.clipboardData || window.clipboardData; const clipboardData = event.clipboardData || window.clipboardData;
addFilter.call(this, clipboardData.getData("text")); const data = clipboardData.getData("text").trim();
// do not automatically paste on single line
if (isMultiLine(data))
addFilter.call(this, data);
} }
render() render()
...@@ -186,14 +195,34 @@ module.exports = IOFilterSearch; ...@@ -186,14 +195,34 @@ module.exports = IOFilterSearch;
function addFilter(data) function addFilter(data)
{ {
const value = data.trim(); let value = data.trim();
if (!value) if (!value)
return; return;
const result = search.call(this, value);
if (result.accuracy < 1) if (value[0] === "[")
{
const message = browser.i18n.getMessage("unexpected_filter_list_header");
dispatch.call(this, "filter:error", {
errors: [message]
});
value = value.replace(/^\S+/, "").trim();
if (!value)
return;
}
// in case of multi line don't bother the search
if (isMultiLine(value))
{
dispatch.call(this, "filter:add", value); dispatch.call(this, "filter:add", value);
else if (result.accuracy) }
dispatch.call(this, "filter:match", result); else
{
const result = search.call(this, value);
if (result.accuracy < 1)
dispatch.call(this, "filter:add", value);
else if (result.accuracy)
dispatch.call(this, "filter:match", result);
}
} }
function dispatch(type, detail) function dispatch(type, detail)
...@@ -206,6 +235,11 @@ function hasValue(filter) ...@@ -206,6 +235,11 @@ function hasValue(filter)
return filter.text == this; return filter.text == this;
} }
function isMultiLine(data)
{
return /[\r\n]/.test(data.trim());
}
function search(value) function search(value)
{ {
let accuracy = 0; let accuracy = 0;
......
...@@ -56,6 +56,10 @@ class IOFilterTable extends IOElement ...@@ -56,6 +56,10 @@ class IOFilterTable extends IOElement
"filter:match", "filter:match",
event => this.onFilterMatch(event) event => this.onFilterMatch(event)
); );
this.search.addEventListener(
"filter:error",
event => this.onerror(event)
);
this.list = this.appendChild(new IOFilterList()); this.list = this.appendChild(new IOFilterList());
this.footer = this.appendChild(wire()`<div class="footer" />`); this.footer = this.appendChild(wire()`<div class="footer" />`);
this.addEventListener("click", this); this.addEventListener("click", this);
...@@ -94,12 +98,15 @@ class IOFilterTable extends IOElement ...@@ -94,12 +98,15 @@ class IOFilterTable extends IOElement
{ {
if (event.target.closest("io-checkbox")) if (event.target.closest("io-checkbox"))
{ {
this.updateFooter(); cleanErrors.call(this);
} }
} }
onerror(event) onerror(event)
{ {
// force the footer to be visible since errors are shown there
this.updateFooter();
this.footer.classList.add("visible");
const {filter, errors} = event.detail; const {filter, errors} = event.detail;
const node = this.querySelector(".footer .error"); const node = this.querySelector(".footer .error");
if (filter) if (filter)
...@@ -142,7 +149,7 @@ class IOFilterTable extends IOElement ...@@ -142,7 +149,7 @@ class IOFilterTable extends IOElement
() => updateList(this.list), () => updateList(this.list),
(errors) => this.onerror({detail: {errors}}) (errors) => this.onerror({detail: {errors}})
); );
this.updateFooter(); cleanErrors.call(this);
break; break;
case classList.contains("copy"): case classList.contains("copy"):
const filters = []; const filters = [];
...@@ -157,41 +164,24 @@ class IOFilterTable extends IOElement ...@@ -157,41 +164,24 @@ class IOFilterTable extends IOElement
onFilterAdd(event) onFilterAdd(event)
{ {
const unknown = new WeakSet();
const filtersBefore = this.filters;
const filters = event.detail const filters = event.detail
.split(/(?:\r\n|\n)/) .split(/(?:\r\n|\n)/)
.map(text => .reverse();
{
let value = filtersBefore.find(
filter => filter.text === text
);
if (!value)
{
value = {text};
unknown.add(value);
}
return value;
});
browser.runtime.sendMessage({ browser.runtime.sendMessage({
type: "filters.importRaw", type: "filters.importRaw",
text: filters.map(filter => filter.text).join("\n") text: filters.join("\n")
}) })
.then(errors => .then(errors =>
{ {
if (!errors.length) if (!errors.length)
{ {
// this is false only when the extension sets filters right away, cleanErrors.call(this);
// as example the first time custom filters are created. for (const text of filters)
// in that case, the order should be the one the extension decided.
if (filtersBefore === this.filters)
{ {
for (const filter of filters) const i = this.filters.findIndex(flt => flt.text === text);
{ const [filter] = i < 0 ? [{text}] : this.filters.splice(i, 1);
if (!unknown.has(filter)) this.filters.unshift(filter);
this.filters.splice(this.filters.indexOf(filter), 1);
this.filters.unshift(filter);
}
} }
// needed in case there were no filters whatsoever // needed in case there were no filters whatsoever
// and the table never got a chance to initialize // and the table never got a chance to initialize
...@@ -232,20 +222,23 @@ class IOFilterTable extends IOElement ...@@ -232,20 +222,23 @@ class IOFilterTable extends IOElement
if (this.list.filters !== filters) if (this.list.filters !== filters)
this.list.filters = filters; this.list.filters = filters;
this.renderFooter(); this.updateFooter();
} }
renderFooter() updateFooter()
{ {
const disabled = !this.list.selected.size;
bind(this.footer)` bind(this.footer)`
<button <button
class="delete" class="delete"
onclick="${this}" onclick="${this}"
disabled="${disabled}"
data-call="onfooterclick" data-call="onfooterclick"
>${{i18n: "delete"}}</button> >${{i18n: "delete"}}</button>
<button <button
class="copy" class="copy"
onclick="${this}" onclick="${this}"
disabled="${disabled}"
data-call="onfooterclick" data-call="onfooterclick"
>${{i18n: "copy_selected"}}</button> >${{i18n: "copy_selected"}}</button>
<button <button
...@@ -254,17 +247,19 @@ class IOFilterTable extends IOElement ...@@ -254,17 +247,19 @@ class IOFilterTable extends IOElement
data-call="onerrorclick" data-call="onerrorclick"
></button> ></button>
`; `;
this.updateFooter();
}
updateFooter()
{
this.footer.classList.toggle("visible", !!this.filters.length);
} }
} }
IOFilterTable.define("io-filter-table"); IOFilterTable.define("io-filter-table");
function cleanErrors()
{
const error = this.querySelector(".footer .error");
if (error)
error.textContent = "";
this.updateFooter();
}
function updateList(list) function updateList(list)
{ {
list.render(); list.render();
......
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
}, },
"options_filters_search_or_add": { "options_filters_search_or_add": {
"description": "The filter search placeholder.", "description": "The filter search placeholder.",
"message": "Search / add filter (e.g. $filter$)", "message": "Search or add filter(s) (e.g. $filter$)",
"placeholders": { "placeholders": {
"filter": { "filter": {
"content": "/ads/track/*" "content": "/ads/track/*"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment