Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions apps/whispering/src/lib/services/notifications/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,44 @@ import { NotificationError, toBrowserNotification } from './types';
* with fallback support for extension-based notifications.
*/
export function createNotificationServiceWeb(): NotificationService {
// Cache extension detection result
let extensionChecked = false;
let hasExtension = false;
// Cache the in-flight detection promise so concurrent callers all await the
// same result rather than each racing to set up their own listener/timeout.
let extensionDetectionPromise: Promise<boolean> | null = null;

/**
* Detects if a browser extension is available for enhanced notification support.
* Results are cached to avoid repeated detection attempts.
* Uses a per-request nonce and origin check to prevent spoofing by other page scripts.
* The result is cached as a promise—concurrent calls all share the same one.
*/
const detectExtension = async (): Promise<boolean> => {
if (extensionChecked) return hasExtension;

// TODO: Implement real extension detection
// This would involve sending a ping message to the extension
// and waiting for a response with a timeout
// For now, always use browser API
hasExtension = false;
extensionChecked = true;
return hasExtension;
const detectExtension = (): Promise<boolean> => {
if (extensionDetectionPromise) return extensionDetectionPromise;

extensionDetectionPromise = new Promise<boolean>((resolve) => {
const nonce = nanoid();
const origin = window.location.origin;

const timer = setTimeout(() => {
window.removeEventListener('message', onPong);
resolve(false);
}, 200);

function onPong(event: MessageEvent) {
if (
event.origin === origin &&
event.data?.type === 'whispering-extension-pong' &&
event.data?.nonce === nonce
) {
clearTimeout(timer);
window.removeEventListener('message', onPong);
resolve(true);
}
}

window.addEventListener('message', onPong);
window.postMessage({ type: 'whispering-extension-ping', nonce }, origin);
});
Comment thread
sarthakNITT marked this conversation as resolved.

return extensionDetectionPromise;
};

return {
Expand Down
8 changes: 2 additions & 6 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.