From 54395d4481b266b913615a6881e47f733ec0b848 Mon Sep 17 00:00:00 2001 From: Sid Vishnoi <8426945+sidvishnoi@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:55:38 +0530 Subject: [PATCH] fix(background): don't clear overpaying on page refresh (#462) Co-authored-by: Radu-Cristian Popa --- src/background/services/background.ts | 17 +++++--- src/background/services/monetization.ts | 18 -------- src/background/services/tabEvents.ts | 58 +++++++++++++------------ src/background/services/tabState.ts | 22 +++++++++- 4 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/background/services/background.ts b/src/background/services/background.ts index 51221d62..5f81fc67 100644 --- a/src/background/services/background.ts +++ b/src/background/services/background.ts @@ -107,8 +107,8 @@ export class Background { } bindTabHandlers() { - this.browser.tabs.onRemoved.addListener(this.tabEvents.clearTabSessions) - this.browser.tabs.onUpdated.addListener(this.tabEvents.clearTabSessions) + this.browser.tabs.onRemoved.addListener(this.tabEvents.onRemovedTab) + this.browser.tabs.onUpdated.addListener(this.tabEvents.onUpdatedTab) this.browser.tabs.onCreated.addListener(this.tabEvents.onCreatedTab) this.browser.tabs.onActivated.addListener(this.tabEvents.onActivatedTab) } @@ -132,7 +132,7 @@ export class Background { case PopupToBackgroundAction.RECONNECT_WALLET: await this.openPaymentsService.reconnectWallet() await this.monetizationService.resumePaymentSessionActiveTab() - await this.tabEvents.onUpdatedTab() + await this.tabEvents.onUpdatedTabUpdatedIndicator() return success(undefined) case PopupToBackgroundAction.ADD_FUNDS: @@ -151,7 +151,7 @@ export class Background { case PopupToBackgroundAction.TOGGLE_WM: await this.monetizationService.toggleWM() - await this.tabEvents.onUpdatedTab() + await this.tabEvents.onUpdatedTabUpdatedIndicator() return case PopupToBackgroundAction.PAY_WEBSITE: @@ -191,7 +191,10 @@ export class Background { ) case ContentToBackgroundAction.IS_TAB_MONETIZED: - await this.tabEvents.onUpdatedTab(message.payload, sender) + await this.tabEvents.onUpdatedTabUpdatedIndicator( + message.payload, + sender + ) return case ContentToBackgroundAction.IS_WM_ENABLED: @@ -222,7 +225,9 @@ export class Background { this.sendToPopup.send('SET_STATE', { state, prevState }) const isCurrentTabMonetized = true // TODO get from tabState - await this.tabEvents.onUpdatedTab({ value: isCurrentTabMonetized }) + await this.tabEvents.onUpdatedTabUpdatedIndicator({ + value: isCurrentTabMonetized + }) }) this.events.on('storage.balance_update', (balance) => diff --git a/src/background/services/monetization.ts b/src/background/services/monetization.ts index 34129a4e..1c2b9922 100644 --- a/src/background/services/monetization.ts +++ b/src/background/services/monetization.ts @@ -215,24 +215,6 @@ export class MonetizationService { emitToggleWM({ enabled: !enabled }) } - clearTabSessions(tabId: number) { - this.logger.debug(`Attempting to clear sessions for tab ${tabId}.`) - const sessions = this.tabState.getSessions(tabId) - - if (!sessions.size) { - this.logger.debug(`No active sessions found for tab ${tabId}.`) - return - } - - for (const session of sessions.values()) { - session.stop() - } - - this.tabState.clearByTabId(tabId) - - this.logger.debug(`Cleared ${sessions.size} sessions for tab ${tabId}.`) - } - async pay(amount: string) { const tab = await getCurrentActiveTab(this.browser) if (!tab || !tab.id) { diff --git a/src/background/services/tabEvents.ts b/src/background/services/tabEvents.ts index cc4f1e9a..7371853e 100644 --- a/src/background/services/tabEvents.ts +++ b/src/background/services/tabEvents.ts @@ -1,14 +1,13 @@ import browser from 'webextension-polyfill' -import type { Browser, Runtime, Tabs } from 'webextension-polyfill' +import type { Browser, Runtime } from 'webextension-polyfill' import { IsTabMonetizedPayload } from '@/shared/messages' import { getTabId } from '../utils' -import { isOkState, type Translation } from '@/shared/helpers' -import type { - MonetizationService, - SendToPopup, - StorageService, - TabState -} from '.' +import { + isOkState, + removeQueryParams, + type Translation +} from '@/shared/helpers' +import type { SendToPopup, StorageService, TabState } from '.' import type { Storage, TabId } from '@/shared/types' const runtime = browser.runtime @@ -55,43 +54,48 @@ const ICONS = { } } -type CallbackTabOnActivated = Parameters< - Browser['tabs']['onActivated']['addListener'] ->[0] -type CallbackTabOnCreated = Parameters< - Browser['tabs']['onCreated']['addListener'] ->[0] +type CallbackTab> = + Parameters[0] export class TabEvents { constructor( - private monetizationService: MonetizationService, private storage: StorageService, private tabState: TabState, private sendToPopup: SendToPopup, private t: Translation, private browser: Browser ) {} - clearTabSessions = ( - tabId: TabId, - changeInfo: Tabs.OnUpdatedChangeInfoType | Tabs.OnRemovedRemoveInfoType - ) => { - if ( - ('status' in changeInfo && changeInfo.status === 'loading') || - 'isWindowClosing' in changeInfo - ) { - this.monetizationService.clearTabSessions(tabId) + + onUpdatedTab: CallbackTab<'onUpdated'> = (tabId, changeInfo, tab) => { + /** + * if loading and no url -> clear all sessions but not the overpaying state + * if loading and url -> we need to check if state keys include this url. + */ + if (changeInfo.status === 'loading') { + const url = tab.url ? removeQueryParams(tab.url) : '' + const clearOverpaying = this.tabState.shouldClearOverpaying(tabId, url) + + this.tabState.clearSessionsByTabId(tabId) + if (clearOverpaying) { + this.tabState.clearOverpayingByTabId(tabId) + } } } - onActivatedTab: CallbackTabOnActivated = async (info) => { + onRemovedTab: CallbackTab<'onRemoved'> = (tabId, _removeInfo) => { + this.tabState.clearSessionsByTabId(tabId) + this.tabState.clearOverpayingByTabId(tabId) + } + + onActivatedTab: CallbackTab<'onActivated'> = async (info) => { await this.updateVisualIndicators(info.tabId) } - onCreatedTab: CallbackTabOnCreated = async (tab) => { + onCreatedTab: CallbackTab<'onCreated'> = async (tab) => { await this.updateVisualIndicators(tab.id) } - onUpdatedTab = async ( + onUpdatedTabUpdatedIndicator = async ( payload?: IsTabMonetizedPayload | null, sender?: Runtime.MessageSender ) => { diff --git a/src/background/services/tabState.ts b/src/background/services/tabState.ts index 38f4eb2c..de24914c 100644 --- a/src/background/services/tabState.ts +++ b/src/background/services/tabState.ts @@ -1,6 +1,7 @@ import type { MonetizationEventDetails } from '@/shared/messages' import type { TabId } from '@/shared/types' import type { PaymentSession } from './paymentSession' +import type { Logger } from '@/shared/logger' type State = { monetizationEvent: MonetizationEventDetails @@ -20,12 +21,18 @@ export class TabState { private state = new Map>() private sessions = new Map>() - constructor() {} + constructor(private logger: Logger) {} private getOverpayingStateKey(url: string, walletAddressId: string): string { return `${url}:${walletAddressId}` } + shouldClearOverpaying(tabId: TabId, url: string): boolean { + const tabState = this.state.get(tabId) + if (!tabState?.size || !url) return false + return ![...tabState.keys()].some((key) => key.startsWith(`${url}:`)) + } + getOverpayingDetails( tabId: TabId, url: string, @@ -100,8 +107,19 @@ export class TabState { return [...this.sessions.keys()] } - clearByTabId(tabId: TabId) { + clearOverpayingByTabId(tabId: TabId) { this.state.delete(tabId) + this.logger.debug(`Cleared overpaying state for tab ${tabId}.`) + } + + clearSessionsByTabId(tabId: TabId) { + const sessions = this.getSessions(tabId) + if (!sessions.size) return + + for (const session of sessions.values()) { + session.stop() + } + this.logger.debug(`Cleared ${sessions.size} sessions for tab ${tabId}.`) this.sessions.delete(tabId) } }