Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: See which rules are affecting a particular SERP entry #522

Merged
merged 11 commits into from
Feb 23, 2025
19 changes: 19 additions & 0 deletions src/_locales/en/messages.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export default exportAsMessages({
// The text of an OK button.
okButton: "OK",

// The text that represents the user's own personal blocklist.
personalBlocklist: "Personal Blocklist",

// The text that means one site has been blocked.
content_singleSiteBlocked: "uBlacklist has blocked 1 site",

Expand Down Expand Up @@ -89,6 +92,18 @@ export default exportAsMessages({
// The text of the button to activate this extension.
popup_activateButton: "Activate",

// The title of the disclosure widget that contains the matching rules information.
popup_matchingRules: "Matching Rules",

// The label for the textarea that shows rules blocking the entry.
popup_blockingRulesLabel: "Rules blocking this entry",

// The label for the textarea that shows rules unblocking the entry.
popup_unblockingRulesLabel: "Rules unblocking this entry",

// The label for the textarea that shows rules highlighting the entry.
popup_highlightingRulesLabel: "Rules highlighting this entry",

// The title of the general section.
options_generalTitle: "General",

Expand Down Expand Up @@ -178,6 +193,10 @@ export default exportAsMessages({
options_blockWholeSiteDescription:
'For example, to block the page "https://a.b.example.uk.com/", a rule "*://*.example.uk.com/*" will be added.',

// The label for the switch whether to enable matching rules.
options_enableMatchingRules:
'Show matching rules in the "Block this site" dialog',

// The title of the appearance section.
options_appearanceTitle: "Appearance",

Expand Down
6 changes: 6 additions & 0 deletions src/_locales/ja/messages.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export default exportAsMessages({
popup_active: "uBlacklist が有効です",
popup_inactive: "uBlacklist が無効です",
popup_activateButton: "有効にする",
popup_matchingRules: "マッチするルール",
popup_blockingRulesLabel: "ブロックするルール",
popup_unblockingRulesLabel: "ブロックを解除するルール",
popup_highlightingRulesLabel: "ハイライトするルール",
options_generalTitle: "一般",
options_blacklistLabel: "Google の検索結果で表示されないようにするサイト",
options_blacklistHelper:
Expand Down Expand Up @@ -67,6 +71,8 @@ export default exportAsMessages({
options_blockWholeSiteLabel: "サイト全体をブロックするルールを追加する",
options_blockWholeSiteDescription:
'例えば、ページ "https://a.b.example.uk.com/" をブロックすると、ルール "*://*.example.uk.com/*" が追加されます。',
options_enableMatchingRules:
"「このサイトをブロックする」ダイアログに「マッチするルール」を表示する",
options_appearanceTitle: "外観",
options_linkColor: "リンクの色",
options_blockColor: "ブロックされた検索結果の色",
Expand Down
5 changes: 5 additions & 0 deletions src/_locales/pt_BR/messages.json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default exportAsMessages({
"Não autorizado. Por favor, desligue a sincronização e ligue novamente.",
cancelButton: "Cancelar",
okButton: "OK",
personalBlocklist: "Lista de Bloqueio Pessoal",
content_singleSiteBlocked: "uBlacklist bloqueou 1 site",
content_multipleSitesBlocked: "uBlacklist bloqueou $1 sites",
content_showBlockedSitesLink: "Mostrar",
Expand All @@ -31,6 +32,10 @@ export default exportAsMessages({
popup_active: "uBlacklist está ativo",
popup_inactive: "uBlacklist está inativo",
popup_activateButton: "Ativar",
popup_matchingRules: "Regras em efeito",
popup_blockingRulesLabel: "Regras bloqueando este resultado",
popup_unblockingRulesLabel: "Regras desbloqueando este resultado",
popup_highlightingRulesLabel: "Regras destacando este resultado",
options_generalTitle: "Geral",
options_blacklistLabel:
"Sites bloqueados de aparecer nos resultados de busca do Google",
Expand Down
6 changes: 6 additions & 0 deletions src/common/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type MessageName =
| "unauthorizedError"
| "cancelButton"
| "okButton"
| "personalBlocklist"
| "content_singleSiteBlocked"
| "content_multipleSitesBlocked"
| "content_showBlockedSitesLink"
Expand All @@ -27,6 +28,10 @@ export type MessageName =
| "popup_active"
| "popup_inactive"
| "popup_activateButton"
| "popup_matchingRules"
| "popup_blockingRulesLabel"
| "popup_unblockingRulesLabel"
| "popup_highlightingRulesLabel"
| "options_generalTitle"
| "options_blacklistLabel"
| "options_blacklistHelper"
Expand Down Expand Up @@ -54,6 +59,7 @@ export type MessageName =
| "options_hideControlLabel"
| "options_blockWholeSiteLabel"
| "options_blockWholeSiteDescription"
| "options_enableMatchingRules"
| "options_appearanceTitle"
| "options_linkColor"
| "options_blockColor"
Expand Down
3 changes: 3 additions & 0 deletions src/scripts/background/backup-restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export async function backup(): Promise<LocalStorageItemsBackupRestore> {
"hideBlockLinks",
"hideControl",
"enablePathDepth",
"enableMatchingRules",
"linkColor",
"blockColor",
"highlightColors",
Expand Down Expand Up @@ -77,6 +78,8 @@ export async function restore(
hideBlockLinks: items.hideBlockLinks ?? defaults.hideBlockLinks,
hideControl: items.hideControl ?? defaults.hideControl,
enablePathDepth: items.enablePathDepth ?? defaults.enablePathDepth,
enableMatchingRules:
items.enableMatchingRules ?? defaults.enableMatchingRules,
generalLastModified: now,

linkColor: items.linkColor ?? defaults.linkColor,
Expand Down
1 change: 1 addition & 0 deletions src/scripts/background/local-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const localStorageSections: readonly LocalStorageSection[] = [
items.hideBlockLinks != null ||
items.hideControl != null ||
items.enablePathDepth != null ||
items.enableMatchingRules != null ||
items.blockWholeSite != null
) {
items.generalLastModified = now.toISOString();
Expand Down
6 changes: 6 additions & 0 deletions src/scripts/background/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const syncSections: readonly SyncSection[] = [
hideBlockLinks: localItems.hideBlockLinks,
hideControl: localItems.hideControl,
enablePathDepth: localItems.enablePathDepth,
enableMatchingRules: localItems.enableMatchingRules,
blockWholeSite: localItems.blockWholeSite,
}),
modifiedTime: dayjs(localItems.generalLastModified),
Expand All @@ -129,6 +130,7 @@ const syncSections: readonly SyncSection[] = [
hideBlockLinks: z.boolean(),
hideControl: z.boolean(),
enablePathDepth: z.boolean(),
enableMatchingRules: z.boolean().optional(),
blockWholeSite: z.boolean().optional(),
})
.safeParse(parseJSON(cloudContent));
Expand All @@ -142,6 +144,9 @@ const syncSections: readonly SyncSection[] = [
hideBlockLinks: items.hideBlockLinks,
hideControl: items.hideControl,
enablePathDepth: items.enablePathDepth,
...(items.enableMatchingRules != null
? { enableMatchingRules: items.enableMatchingRules }
: {}),
...(items.blockWholeSite != null
? { blockWholeSite: items.blockWholeSite }
: {}),
Expand All @@ -160,6 +165,7 @@ const syncSections: readonly SyncSection[] = [
hideBlockLinks,
hideControl,
enablePathDepth,
enableMatchingRules,
blockWholeSite,
generalLastModified,
...newCloudItems
Expand Down
97 changes: 95 additions & 2 deletions src/scripts/block-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ import type { InteractiveRuleset } from "./interactive-ruleset.ts";
import { translate } from "./locales.ts";
import { PathDepth } from "./path-depth.ts";
import type { LinkProps } from "./ruleset/ruleset.ts";
import type { DialogTheme } from "./types.ts";
import { makeAltURL, svgToDataURL } from "./utilities.ts";
import type { DialogTheme, MatchingRulesText } from "./types.ts";
import { getMatchingRulesText, makeAltURL, svgToDataURL } from "./utilities.ts";

type BlockDialogContentProps = {
blockWholeSite: boolean;
close: () => void;
enablePathDepth: boolean;
enableMatchingRules: boolean;
entryProps: LinkProps;
open: boolean;
openOptionsPage: () => Promise<void>;
Expand All @@ -48,6 +49,7 @@ const BlockDialogContent: React.FC<BlockDialogContentProps> = ({
blockWholeSite,
close,
enablePathDepth,
enableMatchingRules,
entryProps,
open,
openOptionsPage,
Expand All @@ -59,6 +61,8 @@ const BlockDialogContent: React.FC<BlockDialogContentProps> = ({
unblock: false,
host: "",
detailsOpen: false,
matchingRulesOpen: false,
matchingRulesText: null as MatchingRulesText | null,
pathDepth: null as PathDepth | null,
depth: "",
rulesToAdd: "",
Expand All @@ -76,6 +80,8 @@ const BlockDialogContent: React.FC<BlockDialogContentProps> = ({
blockWholeSite ? tldts.getDomain(url.host) ?? url.host : url.host,
);
state.detailsOpen = false;
state.matchingRulesOpen = false;
state.matchingRulesText = null;
state.pathDepth = enablePathDepth ? new PathDepth(url) : null;
state.depth = "0";
state.rulesToAdd = patch.rulesToAdd;
Expand All @@ -86,6 +92,8 @@ const BlockDialogContent: React.FC<BlockDialogContentProps> = ({
state.unblock = false;
state.host = entryProps.url;
state.detailsOpen = false;
state.matchingRulesOpen = false;
state.matchingRulesText = null;
state.pathDepth = null;
state.depth = "";
state.rulesToAdd = "";
Expand Down Expand Up @@ -269,6 +277,91 @@ const BlockDialogContent: React.FC<BlockDialogContentProps> = ({
</Row>
</DetailsBody>
</Details>
{enableMatchingRules && (
<Details
open={state.matchingRulesOpen}
onToggle={(e) => {
const { open } = e.currentTarget;
const matchingRulesText = open
? getMatchingRulesText(ruleset, entryProps)
: null;
setState((s) => ({
...s,
matchingRulesOpen: open,
matchingRulesText,
}));
}}
>
<DetailsSummary className={FOCUS_START_CLASS}>
{translate("popup_matchingRules")}
</DetailsSummary>
<DetailsBody>
<Row>
<RowItem expanded>
<LabelWrapper fullWidth>
<ControlLabel for="blocking-rules-text">
{translate("popup_blockingRulesLabel")}
</ControlLabel>
</LabelWrapper>
{state.matchingRulesOpen && (
<TextArea
breakAll
id="blocking-rules-text"
readOnly
monospace
nowrap
rows={4}
resizable
value={state.matchingRulesText?.blockRules}
/>
)}
</RowItem>
</Row>
<Row>
<RowItem expanded>
<LabelWrapper fullWidth>
<ControlLabel for="unblocking-rules-text">
{translate("popup_unblockingRulesLabel")}
</ControlLabel>
</LabelWrapper>
{state.matchingRulesOpen && (
<TextArea
breakAll
id="unblocking-rules-text"
readOnly
monospace
nowrap
rows={4}
resizable
value={state.matchingRulesText?.unblockRules}
/>
)}
</RowItem>
</Row>
<Row>
<RowItem expanded>
<LabelWrapper fullWidth>
<ControlLabel for="highlight-rules-text">
{translate("popup_highlightingRulesLabel")}
</ControlLabel>
</LabelWrapper>
{state.matchingRulesOpen && (
<TextArea
breakAll
id="highlight-rules-text"
readOnly
monospace
nowrap
rows={4}
resizable
value={state.matchingRulesText?.highlightRules}
/>
)}
</RowItem>
</Row>
</DetailsBody>
</Details>
)}
</RowItem>
</Row>
</DialogBody>
Expand Down
13 changes: 12 additions & 1 deletion src/scripts/components/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@ export type DetailsProps = JSX.IntrinsicElements["details"];

export const Details = React.forwardRef<HTMLDetailsElement, DetailsProps>(
function Details(props, ref) {
return <details {...props} ref={ref} />;
const className = useClassName(
() => ({
"&:not(:first-of-type)": {
marginTop: "0.5em",
},
"&:is(details[open] + &)": {
marginTop: "1em",
},
}),
[],
);
return <details {...applyClassName(props, className)} ref={ref} />;
},
);

Expand Down
18 changes: 16 additions & 2 deletions src/scripts/components/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@ import { useClassName } from "./utilities.ts";

export type TextAreaProps = JSX.IntrinsicElements["textarea"] & {
breakAll?: boolean;
resizable?: boolean;
monospace?: boolean;
nowrap?: boolean;
};

export const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
function TextArea({ breakAll = false, ...props }, ref) {
function TextArea(
{
breakAll = false,
resizable = false,
monospace = false,
nowrap = false,
...props
},
ref,
) {
const className = useClassName(
(theme) => ({
background: "transparent",
Expand All @@ -22,8 +34,10 @@ export const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
? `calc(1.5em * ${props.rows} + 1em + 2px)`
: "auto",
lineHeight: "1.5",
fontFamily: monospace ? "monospace" : "inherit",
textWrap: nowrap ? "nowrap" : "wrap",
padding: "0.5em 0.625em",
resize: "none",
resize: resizable ? "vertical" : "none",
width: "100%",
wordBreak: breakAll ? "break-all" : "normal",
"&:disabled": {
Expand Down
Loading