Skip to content

Commit

Permalink
feat: add browser notification (#528)
Browse files Browse the repository at this point in the history
  • Loading branch information
dni authored May 23, 2024
1 parent 47ec05c commit e5461f7
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 48 deletions.
39 changes: 19 additions & 20 deletions public/boltz-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions src/components/BrowserNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useGlobalContext } from "../context/Global";
import { registerNotifications } from "../utils/notification";

const BrowserNotification = () => {
const { browserNotification, setBrowserNotification, t, notify } =
useGlobalContext();

const toggle = (evt: MouseEvent) => {
// When disabled, we try to request permission and enable them
if (!browserNotification()) {
registerNotifications().then((state: boolean) => {
setBrowserNotification(state);
if (state === false) {
notify("error", t("browsernotification_error"));
}
});
evt.stopPropagation();
return;
}
// When enabled, we disable sending them
setBrowserNotification(false);
evt.stopPropagation();
};

return (
<>
<div
class="browser-notification toggle"
title={t("browsernotification_tooltip")}
onClick={toggle}>
<span class={browserNotification() ? "active" : ""}>
{t("on")}
</span>
<span class={!browserNotification() ? "active" : ""}>
{t("off")}
</span>
</div>
</>
);
};

export default BrowserNotification;
2 changes: 1 addition & 1 deletion src/components/RefundButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ const RefundButton = ({
setRefundTxId?: Setter<string>;
}) => {
const {
notify,
getSwap,
setSwapStorage,
setRefundAddress,
refundAddress,
notify,
t,
} = useGlobalContext();
const { setSwap } = usePayContext();
Expand Down
7 changes: 7 additions & 0 deletions src/components/SettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IoClose } from "solid-icons/io";
import { useGlobalContext } from "../context/Global";
import "../style/settings.scss";
import AudioNotificationSetting from "./AudioNotificationSetting";
import BrowserNotification from "./BrowserNotification";
import Denomination from "./Denomination";
import Logs from "./Logs";
import Separator from "./Separator";
Expand Down Expand Up @@ -41,6 +42,12 @@ const SettingsMenu = () => {
<div class="spacer"></div>
<AudioNotificationSetting />
</span>
<span class="setting">
<label>{t("browsernotification")}: </label>
<Tooltip label="browsernotification_tooltip" />
<div class="spacer"></div>
<BrowserNotification />
</span>
<span class="setting">
<label>{t("logs")}: </label>
<Tooltip label="logs_tooltip" />
Expand Down
21 changes: 16 additions & 5 deletions src/components/SwapChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,12 @@ export const SwapChecker = () => {
if (claimedSwap.id === swap().id) {
setSwap(claimedSwap);
}
notify("success", t("claim_success", { id: res.id }), true);
notify(
"success",
t("swap_completed", { id: res.id }),
true,
true,
);
} catch (e) {
const msg = t("claim_fail", { id: currentSwap.id });
log.warn(msg, e);
Expand All @@ -220,11 +225,17 @@ export const SwapChecker = () => {
} else if (data.status === swapStatusPending.TransactionClaimPending) {
try {
await createSubmarineSignature(currentSwap);
} catch (e) {
log.warn(
`creating cooperative signature for submarine swap claim failed`,
e,
notify(
"success",
t("swap_completed", { id: currentSwap.id }),
true,
true,
);
} catch (e) {
const msg =
"creating cooperative signature for submarine swap claim failed";
log.warn(msg, e);
notify("error", msg);
}
}
};
Expand Down
31 changes: 29 additions & 2 deletions src/context/Global.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,16 @@ export type GlobalContextType = {
setSettingsMenu: Setter<boolean>;
audioNotification: Accessor<boolean>;
setAudioNotification: Setter<boolean>;
browserNotification: Accessor<boolean>;
setBrowserNotification: Setter<boolean>;
// functions
t: (key: string, values?: Record<string, any>) => string;
notify: (type: string, message: string, audio?: boolean) => void;
notify: (
type: string,
message: string,
browser?: boolean,
audio?: boolean,
) => void;
playNotificationSound: () => void;
fetchPairs: (asset?: string) => void;

Expand Down Expand Up @@ -171,10 +178,21 @@ const GlobalProvider = (props: { children: any }) => {
},
);

const notify = (type: string, message: string, audio: boolean = false) => {
const notify = (
type: string,
message: string,
browser: boolean = false,
audio: boolean = false,
) => {
setNotificationType(type);
setNotification(message);
if (audio && audioNotification()) playNotificationSound();
if (browser && browserNotification()) {
new Notification(t("notification_header"), {
body: message,
icon: "/boltz-icon.svg",
});
}
};

const playNotificationSound = () => {
Expand Down Expand Up @@ -294,6 +312,13 @@ const GlobalProvider = (props: { children: any }) => {
setEmbedded(true);
}

const [browserNotification, setBrowserNotification] = makePersisted(
createSignal<boolean>(false),
{
name: "browserNotification",
},
);

// i18n
let dictLocale: any;
createMemo(() => setI18n(i18nConfigured()));
Expand Down Expand Up @@ -345,6 +370,8 @@ const GlobalProvider = (props: { children: any }) => {
setSettingsMenu,
audioNotification,
setAudioNotification,
browserNotification,
setBrowserNotification,
// functions
t,
notify,
Expand Down
48 changes: 34 additions & 14 deletions src/i18n/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ const dict = {
pay_timeout_blockheight: "Timeout block height",
pay_expected_amount: "Expected amount",
send_to: "Send {{ amount }} {{ denomination }} to",
pay_invoice_to: "Pay this invoice for {{ amount }} {{ denomination }}",
pay_invoice_to:
"Pay this invoice about {{ amount }} {{ denomination }}",
pay_address: "Address",
no_metamask: "MetaMask not installed",
connect_metamask: "Connect MetaMask",
Expand Down Expand Up @@ -182,15 +183,19 @@ const dict = {
denomination_tooltip: "Choose your preferred denomination: BTC or sats",
decimal_tooltip:
"Choose your preferred decimal separator: dot or comma",
claim_success: "Swap {{ id }} claimed successfully!",
swap_completed: "Swap {{ id }} completed successfully!",
claim_fail: "Failed to claim swap: {{ id }}",
logs: "Logs",
logs_tooltip: "Logs of the web app, useful for debugging.",
enable_audio_notifications: "Enable Audio Notifications",
logs_tooltip: "Logs of the web app, useful for debugging",
enable_audio_notifications: "Audio Notifications",
enable_audio_notifications_tooltip:
"Enable or disable audio notifications",
on: "on",
off: "off",
notification_header: "Boltz",
browsernotification: "Browser Notifications",
browsernotification_tooltip: "Enable or disable browser notifications",
browsernotification_error: "Notification permissions denied",
},
de: {
language: "Deutsch",
Expand Down Expand Up @@ -255,7 +260,8 @@ const dict = {
pay_timeout_blockheight: "Timeout Blockhöhe",
pay_expected_amount: "Erwarteter Betrag",
send_to: "Sende {{ amount }} {{ denomination }} an",
pay_invoice_to: "Zahle Rechnung über {{ amount }} {{ denomination }}",
pay_invoice_to:
"Zahle diese Rechnung über {{ amount }} {{ denomination }}",
pay_address: "Adresse",
no_metamask: "MetaMask ist nicht installiert",
connect_metamask: "MetaMask verbinden",
Expand Down Expand Up @@ -382,15 +388,20 @@ const dict = {
"Wähle deine bevorzugte Denomination: BTC oder sats",
decimal_tooltip:
"Wähle dein bevorzugtes Dezimaltrennzeichen: Punkt oder Komma",
claim_success: "Swap {{ id }} erfolgreich geclaimed!",
swap_completed: "Swap {{ id }} erfolgreich abgeschlossen!",
claim_fail: "Swap {{ id }} konnte nicht geclaimed werden!",
logs: "Logs",
logs_tooltip: "Logs der Web App, nützlich für Debugging.",
enable_audio_notifications: "Audio Benachrichtigungen aktivieren",
logs_tooltip: "Logs der Web App, nützlich für Debugging",
enable_audio_notifications: "Audio Benachrichtigungen",
enable_audio_notifications_tooltip:
"Aktiviere oder deaktiviere Audio-Benachrichtigungen",
on: "an",
off: "aus",
notification_header: "Boltz",
browsernotification: "Browser Benachrichtigungen",
browsernotification_tooltip:
"Aktiviere oder deaktiviere Browser Benachrichtigungen",
browsernotification_error: "Benachrichtigungsrechte verweigert",
},
es: {
language: "Español",
Expand Down Expand Up @@ -582,16 +593,21 @@ const dict = {
decimal_separator: "Separador decimal",
denomination_tooltip: "Elige tu denominación preferida: BTC o sats",
decimal_tooltip: "Elige tu separador decimal preferido: punto o coma",
claim_success: "¡Intercambio {{ id }} reclamado con éxito!",
swap_completed: "¡Intercambio {{ id }} completado con éxito!",
claim_fail: "¡Error en reclamar el intercambio {{ id }}!",
logs: "Logs",
logs_tooltip:
"Registros de la aplicación web como herramienta de depuración.",
enable_audio_notifications: "Activar notificaciones de audio",
"Registros de la aplicación web como herramienta de depuración",
enable_audio_notifications: "Notificaciones de Audio",
enable_audio_notifications_tooltip:
"Activar o desactivar notificaciones de audio",
on: "on",
off: "off",
notification_header: "Boltz",
browsernotification: "Notificaciones del navegador",
browsernotification_tooltip:
"Activar o desactivar notificaciones del navegador",
browsernotification_error: "Permisos de notificación denegados",
},
zh: {
language: "中文",
Expand Down Expand Up @@ -762,14 +778,18 @@ const dict = {
decimal_separator: "小数分隔符",
denomination_tooltip: "选择您的首选面额:BTC 或 sats",
decimal_tooltip: "选择您的首选小数分隔符:点或逗号",
claim_success: "交换{{ id }}成功索赔!",
swap_completed: "交换{{ id }} 已成功完成!",
claim_fail: "交换{{ id }}索赔失败!",
logs: "日志",
logs_tooltip: "网络应用程序的日志,用于调试",
enable_audio_notifications: "启用音频通知",
logs_tooltip: "网络应用程序的日志,用于调试",
enable_audio_notifications: "音频通知",
enable_audio_notifications_tooltip: "启用或禁用音频通知",
on: "开",
off: "关",
notification_header: "Boltz",
browsernotification: "浏览器通知",
browsernotification_tooltip: "启用或禁用浏览器通知",
browsernotification_error: "通知权限被拒绝",
},
};

Expand Down
6 changes: 1 addition & 5 deletions src/style/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,6 @@ textarea {
display: block;
}

#notification {
background: black;
color: white;
}
.toggle {
cursor: pointer;
flex-grow: 0;
Expand Down Expand Up @@ -508,7 +504,7 @@ textarea {
width: 100%;
left: 0;
top: 64px;
z-index: 9999;
z-index: 999;
display: none;
box-shadow: 0 0 12px black;
margin: 0;
Expand Down
6 changes: 5 additions & 1 deletion src/style/notification.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import "vars";

#notification {
background: black;
color: white;
}
#notification {
visibility: hidden;
max-width: 480px;
Expand All @@ -8,7 +12,7 @@
color: #fff;
text-align: center;
position: fixed;
z-index: 1;
z-index: 9999;
left: 0;
right: 0;
bottom: 30px;
Expand Down
10 changes: 10 additions & 0 deletions src/utils/notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import log from "loglevel";

export const registerNotifications = () => {
return new Promise<boolean>((resolve) => {
Notification.requestPermission().then((result) => {
log.info("Notification permission: ", result);
resolve(result === "granted");
});
});
};
5 changes: 5 additions & 0 deletions tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ import regtest from "../src/configs/regtest.json";

regtest.loglevel = "error";
setConfig(regtest);

globalThis.Notification = {
requestPermission: jest.fn().mockResolvedValue(true),
permission: "granted",
} as unknown as jest.Mocked<typeof Notification>;

0 comments on commit e5461f7

Please sign in to comment.