Skip to content

Commit a5ed79d

Browse files
committed
Reset idle alarm when a new Freighter surface mounts unlocked
Previously the idle auto-lock alarm only rearmed on user input events (mousedown, keydown, touchstart, wheel). When a dApp triggered a flow that opened a new popup near the end of the idle window, the user could be locked out mid-flow before they ever interacted with the new surface. Ping USER_ACTIVITY once when useActivityPing mounts in the unlocked state. Opening any Freighter surface (popup, sidebar, fullscreen) is itself an act of user intent and is enough signal to treat the user as active. The existing throttle and isUnlocked guard naturally suppress the mount ping when the wallet is locked or when an input event fires within the same throttle window. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ece5971 commit a5ed79d

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

extension/src/popup/helpers/hooks/__tests__/useActivityPing.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,34 @@ describe("useActivityPing", () => {
9292

9393
addSpy.mockRestore();
9494
});
95+
96+
it("sends a USER_ACTIVITY ping on mount when unlocked", () => {
97+
renderHook(() => useActivityPing(true));
98+
99+
expect(sendMessageToBackground).toHaveBeenCalledTimes(1);
100+
expect(sendMessageToBackground).toHaveBeenCalledWith({
101+
type: SERVICE_TYPES.USER_ACTIVITY,
102+
activePublicKey: "",
103+
});
104+
});
105+
106+
it("throttles event-driven pings against the mount ping", () => {
107+
renderHook(() => useActivityPing(true));
108+
109+
// Mount ping has already fired at t=10_000; event within the
110+
// throttle window is suppressed.
111+
window.dispatchEvent(new MouseEvent("mousedown"));
112+
expect(sendMessageToBackground).toHaveBeenCalledTimes(1);
113+
114+
// Past the throttle window, events resume firing.
115+
jest.setSystemTime(15_000);
116+
window.dispatchEvent(new MouseEvent("mousedown"));
117+
expect(sendMessageToBackground).toHaveBeenCalledTimes(2);
118+
});
119+
120+
it("does not ping on mount when locked", () => {
121+
renderHook(() => useActivityPing(false));
122+
123+
expect(sendMessageToBackground).not.toHaveBeenCalled();
124+
});
95125
});

extension/src/popup/helpers/hooks/useActivityPing.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ const ACTIVITY_EVENTS = [
2323
* Uses a leading-edge throttle so a single ping fires on the first
2424
* event in each `PING_THROTTLE_MS` window — accurate to ~8 % on the
2525
* 1-minute preset and effectively noise at higher presets.
26+
*
27+
* Also pings once on mount, so opening a new Freighter surface (popup,
28+
* sidebar, fullscreen) while the wallet is unlocked itself counts as
29+
* activity. This prevents the wallet from auto-locking mid-flow when a
30+
* dApp triggers a new popup near the end of the idle window.
2631
*/
2732
export const useActivityPing = (isUnlocked: boolean) => {
2833
useEffect(() => {
2934
if (!isUnlocked) return undefined;
3035

3136
let lastPingAt = 0;
32-
const handler = () => {
33-
const now = Date.now();
34-
if (now - lastPingAt < PING_THROTTLE_MS) return;
35-
lastPingAt = now;
37+
const ping = () => {
38+
lastPingAt = Date.now();
3639
void sendMessageToBackground({
3740
type: SERVICE_TYPES.USER_ACTIVITY,
3841
// `USER_ACTIVITY` is account-agnostic — the background only
@@ -46,6 +49,20 @@ export const useActivityPing = (isUnlocked: boolean) => {
4649
});
4750
};
4851

52+
// Ping once on mount so opening a fresh Freighter surface (popup,
53+
// sidebar, fullscreen) while the wallet is unlocked counts as
54+
// activity and rearms the idle alarm. Without this, a user who
55+
// triggers a dApp flow that opens a new popup near the end of the
56+
// idle window can be locked out mid-flow before they ever interact
57+
// with the new surface.
58+
ping();
59+
60+
const handler = () => {
61+
const now = Date.now();
62+
if (now - lastPingAt < PING_THROTTLE_MS) return;
63+
ping();
64+
};
65+
4966
for (const evt of ACTIVITY_EVENTS) {
5067
window.addEventListener(evt, handler, { passive: true });
5168
}

0 commit comments

Comments
 (0)