mirror of
https://github.com/HeyPuter/puter.git
synced 2026-11-05 13:35:20 +00:00
fix(gui): a tap delivered twice routes once
The page remembers the notifications it handed over in the last ten seconds and drops a repeat, whichever way it arrives.
This commit is contained in:
1 parent
5f7ab80721
commit
7aa4d91ae9
2 files changed
+35
-1
No files matched your search
@@ -27,6 +27,8 @@ export const PUSH_TAP_PARAM = 'puter.notification';
|
||||
|
||||
const UID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
const UID_MAX = 64;
|
||||
// A tap for a uid acted on this recently is the same tap again.
|
||||
const TAP_REPEAT_MS = 10_000;
|
||||
|
||||
const nonEmpty = (value) => (typeof value === 'string' && value !== '' ? value : undefined);
|
||||
|
||||
@@ -200,6 +202,8 @@ export const storedTapHandler = ({ fetchRow, surface, found, missing }) => async
|
||||
let pushTapHandler = null;
|
||||
/** @type {PushTap[]} */
|
||||
const pendingPushTaps = [];
|
||||
/** @type {Map<string, number>} uid → when it was last handed over */
|
||||
const recentTaps = new Map();
|
||||
|
||||
/** @param {(tap: PushTap) => unknown} handler */
|
||||
export const setPushTapHandler = (handler) => {
|
||||
@@ -207,9 +211,18 @@ export const setPushTapHandler = (handler) => {
|
||||
for ( const tap of pendingPushTaps.splice(0) ) void handler(tap);
|
||||
};
|
||||
|
||||
const seenLately = (uid, now) => {
|
||||
for ( const [seen, at] of recentTaps ) {
|
||||
if ( now - at >= TAP_REPEAT_MS ) recentTaps.delete(seen);
|
||||
}
|
||||
if ( recentTaps.has(uid) ) return true;
|
||||
recentTaps.set(uid, now);
|
||||
return false;
|
||||
};
|
||||
|
||||
/** @param {PushTap|null} tap */
|
||||
const deliverPushTap = (tap) => {
|
||||
if ( ! tap ) return;
|
||||
if ( ! tap || seenLately(tap.uid, Date.now()) ) return;
|
||||
if ( pushTapHandler ) void pushTapHandler(tap);
|
||||
else pendingPushTaps.push(tap);
|
||||
};
|
||||
|
||||
@@ -282,6 +282,10 @@ describe('storedTapHandler', () => {
|
||||
});
|
||||
|
||||
describe('push tap dispatch', () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('holds taps until a handler is set, then hands them over in order', async () => {
|
||||
const { dispatchPushTap, setPushTapHandler } = await import('./notificationTap.js');
|
||||
dispatchPushTap({ uid: UID });
|
||||
@@ -305,4 +309,21 @@ describe('push tap dispatch', () => {
|
||||
expect(fetchRow).toHaveBeenCalledWith(OTHER_UID);
|
||||
expect(openApp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('hands the same notification over once within ten seconds', async () => {
|
||||
vi.useFakeTimers();
|
||||
const { dispatchPushTap, setPushTapHandler } = await import('./notificationTap.js');
|
||||
const handler = vi.fn();
|
||||
setPushTapHandler(handler);
|
||||
dispatchPushTap({ uid: UID });
|
||||
dispatchPushTap({ uid: UID });
|
||||
dispatchPushTap({ uid: OTHER_UID });
|
||||
expect(handler.mock.calls).toEqual([[{ uid: UID }], [{ uid: OTHER_UID }]]);
|
||||
vi.advanceTimersByTime(9_999);
|
||||
dispatchPushTap({ uid: UID });
|
||||
expect(handler).toHaveBeenCalledTimes(2);
|
||||
vi.advanceTimersByTime(1);
|
||||
dispatchPushTap({ uid: UID });
|
||||
expect(handler).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user