Skip to content
This repository was archived by the owner on Oct 27, 2025. It is now read-only.

Commit 5151b74

Browse files
author
Morten Hundevad
authored
notification filter / storage change (#122)
* notification filter * Fixed conflicts
1 parent 0c43958 commit 5151b74

File tree

5 files changed

+154
-88
lines changed

5 files changed

+154
-88
lines changed

src/common/messages/message-handler.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,18 @@ const pageInfoHandler: MessageHandler<'pageInfo'> = (payload, context) => {
3838
});
3939
};
4040

41+
const notifyUpdateHandler: MessageHandler<'notifyUpdate'> = (payload, context) => {
42+
return new Promise(() => {
43+
console.log('Notify update Received, triggering page load handler:', payload);
44+
context.main.onNotifyUpdate(payload.pageId, payload.action);
45+
});
46+
};
47+
4148
const handlers = {
4249
log: logHandler,
4350
notify: notifyHandler,
4451
pageInfo: pageInfoHandler,
52+
notifyUpdate: notifyUpdateHandler,
4553
} satisfies { [K in keyof MessageMap]: MessageHandler<K> };
4654

4755
function messageHandler(

src/common/messages/messages.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface MessageMap {
1212
log: [{ message: string }];
1313
notify: [{ title: string; message: string }];
1414
pageInfo: [{ domain: string; url: string }];
15+
notifyUpdate: [{ pageId: number; action: string }];
1516
}
1617
export interface MessageHandlerContext {
1718
main: Main;

src/main.ts

Lines changed: 77 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { MessageHandlerContext } from '@/common/messages/messages.types';
1414
import browser from 'webextension-polyfill';
1515
import { Page } from './models/page';
1616

17+
import NotificationsFilter, { INotificationsFilter } from './utils/helpers/notification-filter';
18+
1719
export interface IMainMessage {
1820
badgeText: string;
1921
domain: string;
@@ -43,68 +45,67 @@ export class Main {
4345
* Display how many pages were found by updating the badge text
4446
*/
4547
indicateCATPages(pages: CATWikiPageSearchResults, domMessenger: IDOMMessengerInterface): void {
46-
const totalPages = pages.totalPagesFound;
47-
console.log(pages);
48-
49-
if (totalPages > 0) {
50-
this.onBadgeTextUpdate(totalPages.toString(), domMessenger);
51-
52-
Promise.all([
53-
Preferences.getPreference(Preferences.IS_ENABLED_KEY),
54-
Preferences.getPreference(Preferences.BROWSER_NOTIFICATIONS_ENABLED_KEY),
55-
])
56-
.then(([isEnabled, browserNotificationsEnabled]) => {
57-
if (isEnabled && browserNotificationsEnabled) {
58-
// Example: show a notification about the found pages
59-
// NOTE: Requires "notifications" permission in your manifest.json
60-
void browser.notifications.create({
61-
type: 'basic',
62-
iconUrl: browser.runtime.getURL('alert.png'),
63-
title: 'CAT Pages Found',
64-
message: `Found ${pages.totalPagesFound.toString()} page(s).`,
65-
});
66-
}
67-
})
68-
.catch((error: unknown) =>
69-
console.error('Failed to get preferences to send browser notification:', error)
70-
);
71-
Promise.all([
72-
Preferences.getPreference(Preferences.IS_ENABLED_KEY),
73-
Preferences.getPreference(Preferences.PAGE_NOTIFICATIONS_ENABLED_KEY),
74-
])
75-
.then(([isEnabled, pageNotificationsEnabled]) => {
76-
if (isEnabled && pageNotificationsEnabled) {
77-
const message = `Found ${pages.totalPagesFound.toString()} CAT page(s).`;
78-
const entries: Page[] = [];
79-
pages.pageEntries.forEach((page) => {
80-
entries.push(page);
81-
});
82-
83-
domMessenger
84-
.showInPageNotification(message, entries)
85-
.then(() => console.log('In-page notification shown.'))
86-
.catch((error: unknown) => {
87-
if (error instanceof Error && error.message.includes('Receiving end does not exist')) {
88-
console.warn(
89-
`Failed to send in-page notification (tab might be inactive or closed/navigated away before message was sent): ${error.message}`
90-
);
91-
} else {
92-
console.error(
93-
'Failed to show in-page notification due to unexpected error:',
94-
error
95-
);
96-
}
48+
Promise.all([
49+
Preferences.getPreference(Preferences.IS_ENABLED_KEY),
50+
Preferences.getPreference(Preferences.BROWSER_NOTIFICATIONS_ENABLED_KEY),
51+
Preferences.getPreference(Preferences.PAGE_NOTIFICATIONS_ENABLED_KEY),
52+
NotificationsFilter.get(),
53+
])
54+
.then(([isEnabled, browserNotificationsEnabled, pageNotificationsEnabled, filters]) => {
55+
if (isEnabled) {
56+
let pageEntries = pages.pageEntries;
57+
pageEntries = NotificationsFilter.filterPages(pageEntries, filters, 60 * 60 * 1000); // check if muted 1 hour
58+
59+
const totalPages = pageEntries.length;
60+
console.log('pageEntries', pages.pageEntries, pageEntries);
61+
if (totalPages > 0) {
62+
this.onBadgeTextUpdate(totalPages.toString(), domMessenger);
63+
64+
if (browserNotificationsEnabled) {
65+
// Example: show a notification about the found pages
66+
// NOTE: Requires "notifications" permission in your manifest.json
67+
void browser.notifications.create({
68+
type: 'basic',
69+
iconUrl: browser.runtime.getURL('alert.png'),
70+
title: 'CAT Pages Found',
71+
message: `Found ${totalPages.toString()} page(s).`,
9772
});
73+
}
74+
75+
if (pageNotificationsEnabled) {
76+
const message = `Found ${totalPages.toString()} CAT page(s).`;
77+
const entries: Page[] = [];
78+
pageEntries.forEach((page) => {
79+
entries.push(page);
80+
});
81+
82+
domMessenger
83+
.showInPageNotification(message, entries)
84+
.then(() => console.log('In-page notification shown.'))
85+
.catch((error: unknown) => {
86+
if (
87+
error instanceof Error &&
88+
error.message.includes('Receiving end does not exist')
89+
) {
90+
console.warn(
91+
`Failed to send in-page notification (tab might be inactive or closed/navigated away before message was sent): ${error.message}`
92+
);
93+
} else {
94+
console.error(
95+
'Failed to show in-page notification due to unexpected error:',
96+
error
97+
);
98+
}
99+
});
100+
}
101+
} else {
102+
// Revert badge text back to "on" or "off" as set by indicateStatus
103+
console.log('Resetting badge text');
104+
this.indicateStatus();
98105
}
99-
})
100-
.catch((error: unknown) =>
101-
console.error('Failed to get preferences to send in-page notification:', error)
102-
);
103-
} else {
104-
// Revert badge text back to "on" or "off" as set by indicateStatus
105-
console.log('Resetting badge text');
106-
this.indicateStatus();
107-
}
106+
}
107+
})
108+
.catch((error: unknown) => console.error('Failed to get preferences to send in-page notification:', error));
108109
}
109110

110111
notify(message: string) {
@@ -186,6 +187,22 @@ export class Main {
186187
await this.contentScanner.checkPageContents(scannerParameters);
187188
}
188189

190+
/**
191+
* Called when a page (tab) has finished loading.
192+
* Scans the domain and in-page contents, merges results,
193+
* and indicates how many CAT pages were found.
194+
*/
195+
onNotifyUpdate(pageId: number, action: string) {
196+
const page: INotificationsFilter = {
197+
timestamp: 0,
198+
pageId: pageId,
199+
};
200+
if (action == 'mute') {
201+
page.timestamp = Date.now();
202+
}
203+
NotificationsFilter.update(page);
204+
}
205+
189206
/**
190207
* Called when the extension is installed.
191208
* Initializes default settings and indicates current status.

src/ui/inpagenotification/Inspagenotification.tsx

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22

33
import { ArticleType, IPage, Page } from '@/models/page';
4-
import LocalStorage from '@/utils/helpers/local-storage';
54
import { CompanyPage, ICompanyPage } from '@/models/company';
65
import { IIncidentPage, IncidentPage } from '@/models/incident';
76
import { IProductPage, ProductPage } from '@/models/product';
@@ -14,14 +13,16 @@ export interface IInpagenotificationPage {
1413
const InpagenotificationPage = ({ page }: IInpagenotificationPage) => {
1514
const componentReferance = React.createRef<HTMLParagraphElement>();
1615

17-
const showPage = Date.now() > LocalStorage.readPage(page.pageId).timestamp + 60 * 60 * 1000; // curent mute 1 hour, TODO: should come from an option.
18-
1916
const closePage = () => {
20-
const storedPage = LocalStorage.readPage(page.pageId);
21-
22-
storedPage.timestamp = Date.now();
17+
const notifyUpdatePayload = {
18+
pageId: page.pageId,
19+
action: 'mute',
20+
};
2321

24-
LocalStorage.writePage(storedPage);
22+
void browser.runtime.sendMessage({
23+
type: 'notifyUpdate',
24+
payload: notifyUpdatePayload,
25+
});
2526

2627
if (componentReferance) {
2728
componentReferance.current?.remove();
@@ -41,28 +42,24 @@ const InpagenotificationPage = ({ page }: IInpagenotificationPage) => {
4142
}
4243
};
4344

44-
if (showPage) {
45-
return (
46-
<>
47-
<div className="page" ref={componentReferance}>
48-
<div className="page-menu">
49-
<span className="page-more" onClick={seeMore}>
50-
51-
</span>
52-
<span className="page-close" onClick={closePage}>
53-
54-
</span>
55-
</div>
56-
<a href={page.url()} target="_blank">
57-
{page.pageName}
58-
</a>
59-
<div className="page-info hidden">{page.description}</div>
45+
return (
46+
<>
47+
<div className="page" ref={componentReferance}>
48+
<div className="page-menu">
49+
<span className="page-more" onClick={seeMore}>
50+
51+
</span>
52+
<span className="page-close" onClick={closePage}>
53+
54+
</span>
6055
</div>
61-
</>
62-
);
63-
} else {
64-
return <></>;
65-
}
56+
<a href={page.url()} target="_blank">
57+
{page.pageName}
58+
</a>
59+
<div className="page-info hidden">{page.description}</div>
60+
</div>
61+
</>
62+
);
6663
};
6764

6865
export interface IInpagenotificationMessage {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Preferences from '@/common/services/preferences';
2+
3+
import { Page } from '@/models/page';
4+
5+
export interface INotificationsFilter {
6+
timestamp: number;
7+
pageId: number;
8+
}
9+
10+
class NotificationsFilter {
11+
static readonly NOTIFICATIONS_FILTER_KEY = 'notifications_filter';
12+
13+
public static async get(): Promise<Record<string, INotificationsFilter>> {
14+
let result: Record<string, INotificationsFilter> = {};
15+
const data = await Preferences.getStorage(NotificationsFilter.NOTIFICATIONS_FILTER_KEY);
16+
if (data) {
17+
result = data as Record<string, INotificationsFilter>;
18+
}
19+
return result;
20+
}
21+
22+
public static update(page: INotificationsFilter) {
23+
Promise.all([NotificationsFilter.get()])
24+
.then(([pages]) => {
25+
pages[page.pageId] = page;
26+
Preferences.setStorage(NotificationsFilter.NOTIFICATIONS_FILTER_KEY, pages);
27+
})
28+
.catch((error: unknown) => console.error('Failed to get notifications filter:', error));
29+
}
30+
31+
public static filterPages(pages: readonly Page[], filters: Record<string, INotificationsFilter>, muteTime: number) {
32+
const now = Date.now();
33+
return pages.filter((entry) => {
34+
const existingPage = filters[entry.pageId];
35+
36+
if (!existingPage) return true;
37+
38+
return existingPage.timestamp + muteTime < now;
39+
});
40+
}
41+
}
42+
43+
export default NotificationsFilter;

0 commit comments

Comments
 (0)