Adds a "Loss thresholds" panel to the popup backed by the backend's new GET/PATCH /me/settings endpoints, converting between the wire's fractional percentages and whole-number form inputs only at that boundary. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01RTorFQXMR9ZQtCKcMTXLHp
92 lines
3.6 KiB
TypeScript
92 lines
3.6 KiB
TypeScript
import { clearSessionToken, getSessionToken, getStoredSession } from '@/background/backend-client';
|
|
import { getConnectionStatus, setConnectionStatus } from '@/background/connection-status';
|
|
import { applyLockState, getLockState } from '@/background/lock-state';
|
|
import {
|
|
handleWalletConnected,
|
|
requestAccountSwitch,
|
|
requestWalletDisconnect,
|
|
requestWalletReconnect,
|
|
} from '@/background/wallet-auth';
|
|
import { getSettings, updateSettings } from '@/background/settings-client';
|
|
import { connectWsClient } from '@/background/ws-client';
|
|
import type { NexaMessage, SettingsResult } from '@/shared/messaging';
|
|
|
|
export default defineBackground(() => {
|
|
const ws = connectWsClient({
|
|
getToken: getSessionToken,
|
|
onLockState: (state) => void applyLockState(state),
|
|
onStatusChange: (status) => void setConnectionStatus(status),
|
|
onAuthExpired: () => {
|
|
void clearSessionToken().then(() => requestWalletReconnect());
|
|
},
|
|
});
|
|
|
|
async function signOut(): Promise<void> {
|
|
await clearSessionToken();
|
|
ws.disconnect();
|
|
await requestWalletDisconnect();
|
|
}
|
|
|
|
browser.runtime.onMessage.addListener((message: NexaMessage, sender, sendResponse) => {
|
|
switch (message?.type) {
|
|
case 'nexa:get-lock-state':
|
|
getLockState().then(sendResponse);
|
|
return true; // keep the message channel open for the async response
|
|
|
|
case 'nexa:get-connection-status':
|
|
sendResponse(getConnectionStatus());
|
|
return false;
|
|
|
|
case 'nexa:open-popup':
|
|
// Best-effort: not all browsers/contexts allow programmatic popup
|
|
// opening outside a direct user gesture on the action icon.
|
|
browser.action.openPopup().catch(() => undefined);
|
|
return false;
|
|
|
|
case 'nexa:wallet-connected':
|
|
// A content script reports this on every page load (it always tries
|
|
// a silent onlyIfTrusted connect first), and after an explicit
|
|
// account switch. Only re-authenticate (which means asking Phantom
|
|
// to sign a fresh nonce — a popup every time, unlike a silent
|
|
// connect) when this isn't the wallet we're already signed in as; a
|
|
// bare "do we have a token" check can't tell those apart.
|
|
getStoredSession()
|
|
.then((session) => {
|
|
if (session?.walletAddress === message.walletAddress) return;
|
|
return handleWalletConnected(sender.tab?.id, message.walletAddress).then(() => ws.reconnectNow());
|
|
})
|
|
.catch((err) => console.debug('[nexa/background] wallet auth failed:', err)); // ws-client's own retry loop keeps trying regardless
|
|
return false;
|
|
|
|
case 'nexa:wallet-disconnected':
|
|
// Disconnected directly in Phantom's UI (not via our own sign-out
|
|
// flow, which already clears/disconnects itself) — treat the same way.
|
|
void clearSessionToken().then(() => ws.disconnect());
|
|
return false;
|
|
|
|
case 'nexa:sign-out':
|
|
void signOut();
|
|
return false;
|
|
|
|
case 'nexa:switch-account':
|
|
void requestAccountSwitch();
|
|
return false;
|
|
|
|
case 'nexa:get-settings':
|
|
getSettings()
|
|
.then((settings) => sendResponse({ settings } satisfies SettingsResult))
|
|
.catch((err) => sendResponse({ error: String(err) } satisfies SettingsResult));
|
|
return true; // keep the message channel open for the async response
|
|
|
|
case 'nexa:update-settings':
|
|
updateSettings(message.settings)
|
|
.then((settings) => sendResponse({ settings } satisfies SettingsResult))
|
|
.catch((err) => sendResponse({ error: String(err) } satisfies SettingsResult));
|
|
return true;
|
|
|
|
default:
|
|
return undefined;
|
|
}
|
|
});
|
|
});
|