Skip to content

Commit a75eb7a

Browse files
authored
Add specific fee tracker (#7)
Features: Add specific fee tracker; Add specific fee notification settings tab; Add extension update notification; Design features: Update extension icon; Change tracking design; Update texts; Other changes: Global refactoring; Improve typing;
1 parent d0dd373 commit a75eb7a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+895
-293
lines changed

custom_typing/png.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "*.png" {
2+
const content: string;
3+
export default content;
4+
}

custom_typing/wav.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "*.wav" {
2+
const content: string;
3+
export default content;
4+
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mempool-space-block-tracker-chrome-extenstion",
3-
"version": "1.1.0-beta.2",
3+
"version": "1.1.0",
44
"description": "Chrome extension to track new bitcoin blocks via Mempool Space",
55
"scripts": {
66
"dev": "webpack --watch --progress --mode=development",

public/files/manifest.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
"name": "Bitcoin blocks tracker",
66
"description": "Extension for Bitcoin blocks tracking via Mempool space",
7-
"version": "1.0.1",
8-
"version_name": "1.1.0-beta.2",
7+
"version": "1.1.0",
8+
"version_name": "1.1.0",
99

1010
"icons": {
1111
"16":"icons/16.png",
@@ -22,5 +22,5 @@
2222
"service_worker": "js/background.js"
2323
},
2424

25-
"permissions": ["storage", "offscreen"]
25+
"permissions": ["storage", "offscreen", "notifications"]
2626
}

public/icons/128.png

19.2 KB
Loading

public/icons/16.png

-571 Bytes
Loading

public/icons/32.png

619 Bytes
Loading

public/icons/48.png

2.29 KB
Loading

src/background/index.ts

+187-52
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,68 @@ import { sendMessage } from "@coreUtils/utils";
22
import {
33
BackgroundMessage,
44
BlockPopupMessage,
5+
FeeNotificationBorder,
56
Fees,
67
FeesPopupMessage,
7-
PlayBlockNotificationSoundOffscreenMessage
8+
PlayNotificationSoundOffscreenMessage
89
} from "@models/types";
10+
import logo from "@static/images/logo.png";
911

12+
import { releaseNotes } from "./utils/releaseNotes";
1013
import { getSocketUrl } from "./utils/utils";
1114

1215
const OFFSCREEN_DOCUMENT_NAME = "offScreen.html";
1316
const OFFSCREEN_DOCUMENT_PATH = `../${OFFSCREEN_DOCUMENT_NAME}`;
1417

15-
let creating: Promise<void> | null;
16-
let socket: WebSocket | null;
18+
let isBlockNotificationEnabled: boolean | null = null;
19+
let isMainnet: boolean | null = null;
20+
let creating: Promise<void> | null = null;
21+
let socket: WebSocket | null = null;
1722
let intervalId: NodeJS.Timer | null;
18-
let fees: Fees | null;
19-
let lastBlockTime: number | null;
20-
let lastBlockHeight: number | null;
23+
let fees: Fees | null = null;
24+
let lastBlockTime: number | null = null;
25+
let lastBlockHeight: number | null = null;
2126
let blockNotificationVolume: number = 100;
2227
let blockNotificationSound: string | null = null;
2328

29+
let isFeeNotificationEnabled: boolean | null = null;
30+
let feeNotificationBorder: FeeNotificationBorder | null = null;
31+
let feeNotificationVolume: number = 100;
32+
let feeNotificationSound: string | null = null;
33+
34+
chrome.runtime.onInstalled.addListener((details) => {
35+
try {
36+
const currentVersion = chrome.runtime.getManifest().version;
37+
if (details.reason === "update" && currentVersion !== details.previousVersion) {
38+
console.debug(`Updated version: ${currentVersion}. Send notification`);
39+
40+
const currentVersionReleaseNotes = releaseNotes[currentVersion];
41+
42+
if (currentVersionReleaseNotes) {
43+
chrome.notifications.create(
44+
`relese-notes-${currentVersion}`,
45+
{
46+
type: "basic",
47+
iconUrl: logo,
48+
title: currentVersionReleaseNotes.title,
49+
message: currentVersionReleaseNotes.message,
50+
buttons: [{ title: "Release Notes" }]
51+
},
52+
() => {}
53+
);
54+
55+
chrome.notifications.onClicked.addListener(() => {
56+
chrome.tabs.create({ url: currentVersionReleaseNotes.link });
57+
});
58+
} else {
59+
console.debug(`Release notes for version "${currentVersion}" not found`);
60+
}
61+
}
62+
} catch (error) {
63+
console.warn(`Error sending update notification: ${(error as Error).message}`);
64+
}
65+
});
66+
2467
async function checkOffscreenDocumentExist(): Promise<boolean> {
2568
// Check all windows controlled by the service worker to see if one
2669
// of them is the offscreen document with the given path
@@ -51,23 +94,36 @@ async function setupOffscreenDocument() {
5194
}
5295
}
5396

97+
function initialSettings(): void {
98+
chrome.storage.local
99+
.get([
100+
"blockNotificationVolume",
101+
"blockNotificationSound",
102+
"feeNotificationBorder",
103+
"feeNotificationVolume",
104+
"feeNotificationSound"
105+
])
106+
.then((result) => {
107+
blockNotificationVolume = result.blockNotificationVolume ?? 100;
108+
blockNotificationSound = result.blockNotificationSound;
109+
feeNotificationBorder = result.feeNotificationBorder;
110+
feeNotificationVolume = result.feeNotificationVolume ?? 100;
111+
feeNotificationSound = result.feeNotificationSound;
112+
});
113+
}
114+
54115
function onOpenSocketHandler(): void {
55116
console.debug("Socket is open");
56117
console.debug("Send data to server");
57118
socket?.send(JSON.stringify({ action: "init" }));
58119
socket?.send(JSON.stringify({ action: "want", data: ["blocks", "stats"] }));
59-
60-
chrome.storage.local.get(["blockNotificationVolume", "blockNotificationSound"]).then((result) => {
61-
blockNotificationVolume = result.blockNotificationVolume ?? 100;
62-
blockNotificationSound = result.blockNotificationSound;
63-
});
64120
}
65121

66122
function onCloseSocketWithReconnectHandler(event: CloseEvent, websocketUrl: string): void {
67123
console.debug(`Socket is closed. Reason: ${event.reason}`);
68124

69-
chrome.storage.local.get(["isTrackingEnabled"]).then((result) => {
70-
if (result.isTrackingEnabled && event.code !== 1000) {
125+
chrome.storage.local.get(["isBlockNotificationEnabled", "isFeeNotificationEnabled"]).then((result) => {
126+
if ((result.isTrackingEnabled || result.isFeeNotificationEnabled) && event.code !== 1000) {
71127
console.debug("Reconnect will be attempted in 1 second.");
72128

73129
if (intervalId) {
@@ -99,14 +155,16 @@ function onCloseSocketWithReconnectHandler(event: CloseEvent, websocketUrl: stri
99155
function onBlockMessageHandler(eventData: any): void {
100156
if ("block" in eventData) {
101157
console.info("New block");
102-
setupOffscreenDocument().then(() => {
103-
console.debug("Send block notification method to offscreen");
104-
sendMessage<PlayBlockNotificationSoundOffscreenMessage>({
105-
data: { volume: blockNotificationVolume, sound: blockNotificationSound },
106-
target: "offscreen",
107-
type: "playBlockNotificationSound"
158+
if (isBlockNotificationEnabled) {
159+
setupOffscreenDocument().then(() => {
160+
console.debug("Send block notification to offscreen");
161+
sendMessage<PlayNotificationSoundOffscreenMessage>({
162+
data: { volume: blockNotificationVolume, sound: blockNotificationSound },
163+
target: "offscreen",
164+
type: "playBlockNotificationSound"
165+
});
108166
});
109-
});
167+
}
110168

111169
lastBlockTime = eventData.block.timestamp * 1000;
112170
lastBlockHeight = eventData.block.height;
@@ -124,11 +182,44 @@ function onBlockMessageHandler(eventData: any): void {
124182
}
125183
}
126184

185+
function checkFeeBorder(oldFees: Fees | null, newFees: Fees | null): void {
186+
if (
187+
isFeeNotificationEnabled &&
188+
feeNotificationBorder !== null &&
189+
feeNotificationBorder.feeBorder &&
190+
newFees !== null &&
191+
oldFees !== null
192+
) {
193+
const fromLowToHighCondition =
194+
oldFees[feeNotificationBorder.feeLevel] < newFees[feeNotificationBorder.feeLevel] &&
195+
newFees[feeNotificationBorder.feeLevel] >= feeNotificationBorder.feeBorder &&
196+
oldFees[feeNotificationBorder.feeLevel] < feeNotificationBorder.feeBorder;
197+
const fromHighToLowCondition =
198+
oldFees[feeNotificationBorder.feeLevel] > newFees[feeNotificationBorder.feeLevel] &&
199+
newFees[feeNotificationBorder.feeLevel] <= feeNotificationBorder.feeBorder &&
200+
oldFees[feeNotificationBorder.feeLevel] > feeNotificationBorder.feeBorder;
201+
202+
if (fromLowToHighCondition || fromHighToLowCondition) {
203+
setupOffscreenDocument().then(() => {
204+
console.debug("Send fee notification to offscreen");
205+
sendMessage<PlayNotificationSoundOffscreenMessage>({
206+
data: { volume: feeNotificationVolume, sound: feeNotificationSound },
207+
target: "offscreen",
208+
type: "playFeeNotificationSound"
209+
});
210+
});
211+
}
212+
}
213+
}
214+
127215
function onFeesMessageHandler(eventData: any): void {
128216
if ("fees" in eventData) {
129217
console.debug("Fee updated");
218+
const lastFees = structuredClone(fees);
219+
130220
fees = eventData.fees;
131221
sendMessage<FeesPopupMessage>({ target: "popup", data: { fees }, type: "fees" });
222+
checkFeeBorder(lastFees, fees);
132223

133224
console.info(`Current fees: slow: ${fees?.hourFee}, medium: ${fees?.halfHourFee}, fast: ${fees?.fastestFee}`);
134225
}
@@ -191,55 +282,75 @@ function mempoolSpaceBlockNotification(websocketUrl: string) {
191282
intervalId = setInterval(healthcheck, 1000);
192283
}
193284

194-
function enableBlocksTracking(isMainnet: boolean): void {
195-
console.log("Enabling blocks tracking");
196-
mempoolSpaceBlockNotification(getSocketUrl(isMainnet));
285+
function enableWebSocket(_isMainnet: boolean): void {
286+
if ((isBlockNotificationEnabled || isFeeNotificationEnabled) && !socket) {
287+
console.log("Enabling web socket");
288+
initialSettings();
289+
mempoolSpaceBlockNotification(getSocketUrl(_isMainnet));
290+
}
197291
}
198292

199-
function disableBlocksTracking(): void {
200-
console.log("Disabling blocks tracking");
201-
checkOffscreenDocumentExist().then((isOffscreenDocumentExist: boolean) => {
202-
if (isOffscreenDocumentExist) {
203-
chrome.offscreen.closeDocument();
204-
}
205-
});
206-
socket?.close(1000, "Disabling blocks tracking");
293+
function disableWebSocket(): void {
294+
if (!isBlockNotificationEnabled && !isFeeNotificationEnabled) {
295+
console.log("Disabling web socket");
296+
checkOffscreenDocumentExist().then((isOffscreenDocumentExist: boolean) => {
297+
if (isOffscreenDocumentExist) {
298+
chrome.offscreen.closeDocument();
299+
}
300+
});
301+
socket?.close(1000, "Disabling blocks tracking");
207302

208-
fees = null;
209-
lastBlockTime = null;
210-
lastBlockHeight = null;
303+
fees = null;
304+
lastBlockTime = null;
305+
lastBlockHeight = null;
211306

212-
sendMessage<BlockPopupMessage>({
213-
target: "popup",
214-
data: { blockInfo: { lastBlockTime, lastBlockHeight } },
215-
type: "blockInfo"
216-
});
217-
sendMessage<FeesPopupMessage>({ target: "popup", data: { fees }, type: "fees" });
307+
sendMessage<BlockPopupMessage>({
308+
target: "popup",
309+
data: { blockInfo: { lastBlockTime, lastBlockHeight } },
310+
type: "blockInfo"
311+
});
312+
sendMessage<FeesPopupMessage>({ target: "popup", data: { fees }, type: "fees" });
313+
}
218314
}
219315

220-
chrome.storage.local.get(["isTrackingEnabled", "isMainnet"]).then((result) => {
221-
const isTrackingEnabled = result.isTrackingEnabled || false;
222-
const isMainnet = result.isMainnet || true;
316+
chrome.storage.local.get(["isBlockNotificationEnabled", "isMainnet", "isFeeNotificationEnabled"]).then((result) => {
317+
isBlockNotificationEnabled = result.isBlockNotificationEnabled ?? false;
318+
isFeeNotificationEnabled = result.isFeeNotificationEnabled ?? false;
319+
isMainnet = result.isMainnet ?? true;
223320

224-
if (isTrackingEnabled && !socket) {
225-
enableBlocksTracking(isMainnet);
321+
enableWebSocket(!!isMainnet);
322+
});
323+
324+
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
325+
if (message.target === "background" && message.type === "changeBlockNotificationEnabled") {
326+
isBlockNotificationEnabled = message.data.enabled;
327+
if (isBlockNotificationEnabled) {
328+
enableWebSocket(!!isMainnet);
329+
} else {
330+
disableWebSocket();
331+
}
226332
}
227333
});
228334

229335
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
230-
if (message.target === "background" && message.type === "changeEnabled") {
231-
if (message.data.enabled) {
232-
enableBlocksTracking(message.data.isMainnet);
336+
if (message.target === "background" && message.type === "changeFeeNotificationEnabled") {
337+
isFeeNotificationEnabled = message.data.enabled;
338+
if (isFeeNotificationEnabled) {
339+
enableWebSocket(!!isMainnet);
233340
} else {
234-
disableBlocksTracking();
341+
disableWebSocket();
235342
}
236343
}
237344
});
238345

239346
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
240-
if (message.target === "background" && message.type === "changeBlockchain" && message.data.enabled && socket) {
241-
console.log(`Changing blockchain to ${message.data.isMainnet ? "mainnet" : "testnet"}`);
242-
socket.close(3001, getSocketUrl(message.data.isMainnet));
347+
if (message.target === "background" && message.type === "changeBlockchain") {
348+
isMainnet = message.data.isMainnet;
349+
350+
if ((isBlockNotificationEnabled || isFeeNotificationEnabled) && socket) {
351+
console.log(`Changing blockchain to ${message.data.isMainnet ? "mainnet" : "testnet"}`);
352+
socket.close(3001, getSocketUrl(message.data.isMainnet));
353+
}
243354
}
244355
});
245356

@@ -278,3 +389,27 @@ chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
278389
blockNotificationSound = message.data.sound;
279390
}
280391
});
392+
393+
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
394+
if (message.target === "background" && message.type === "changeFeeNotificationSoundVolume") {
395+
console.debug("Change fee notification sound volume");
396+
397+
feeNotificationVolume = message.data.volume;
398+
}
399+
});
400+
401+
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
402+
if (message.target === "background" && message.type === "changeFeeNotificationSound") {
403+
console.debug("Change fee notification sound");
404+
405+
feeNotificationSound = message.data.sound;
406+
}
407+
});
408+
409+
chrome.runtime.onMessage.addListener((message: BackgroundMessage) => {
410+
if (message.target === "background" && message.type === "changeFeeNotificationBorder") {
411+
console.debug("Change fee notification border");
412+
413+
feeNotificationBorder = message.data.feeBorder;
414+
}
415+
});

src/background/utils/releaseNotes.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const releaseNotes: Record<string, { title: string; message: string; link: string }> = {
2+
"1.1.0": {
3+
title: "Update 1.1.0",
4+
message:
5+
"Settings for notifications, commission tracking and other changes in version 1.1.0. More details at the link https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0",
6+
link: "https://github.com/IvanSavoskin/bitcoin-blocks-tracker-extension/releases/tag/v1.1.0"
7+
}
8+
};

0 commit comments

Comments
 (0)