Skip to content

Commit

Permalink
feat: Update icon (#234)
Browse files Browse the repository at this point in the history
* Tag listener

* Monetization tag

* Add stop & resume

* merge and resolve conflicts

* merge conflicts

* update monetization event payload

* Monetization service progress

* Update monetization event (include frameId as well)

* Remove unused code

* Move message handler in contentScript

* Add contentScript file changes

* Clear payment session on closed tab

* Listen for tab events, resume & pause monetization

* Update icon

* remove logs

* Remove null validation

* Update icon logic

* Update bulk at start

* Fix build error

* Minor code updated

---------

Co-authored-by: Diana Fulga <diana.fulga@breakointit.eu>
Co-authored-by: Ionut Anin <ionutanin@gmail.com>
Co-authored-by: Radu-Cristian Popa <radu.popa@breakpointit.eu>
  • Loading branch information
4 people committed May 13, 2024
1 parent 2aad0ae commit a5364ef
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 41 deletions.
Binary file added src/assets/icons/icon-warning-128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/icons/icon-warning-34.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/background/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ export const sendMonetizationEvent = async ({
payload
})
}

export const emitToggleWM = async (
payload: BackgroundToContentActionPayload[BackgroundToContentAction.EMIT_TOGGLE_WM]
) => {
return await message.sendToActiveTab({
action: BackgroundToContentAction.EMIT_TOGGLE_WM,
payload
})
}
12 changes: 11 additions & 1 deletion src/background/services/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export class Background {

bindTabHandlers() {
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)
}

bindMessageHandler() {
Expand All @@ -55,6 +56,8 @@ export class Background {

case PopupToBackgroundAction.TOGGLE_WM:
await this.monetizationService.toggleWM()

this.tabEvents.onUpdatedTab()
return

case PopupToBackgroundAction.PAY_WEBSITE:
Expand Down Expand Up @@ -95,6 +98,13 @@ export class Background {
})
)

case ContentToBackgroundAction.IS_TAB_MONETIZED:
this.tabEvents.onUpdatedTab(message.payload)
return

case ContentToBackgroundAction.IS_WM_ENABLED:
return success(await this.storage.getWMState())

default:
return
}
Expand Down
2 changes: 2 additions & 0 deletions src/background/services/monetization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
StopMonetizationPayload
} from '@/shared/messages'
import { PaymentSession } from './paymentSession'
import { emitToggleWM } from '../lib/messages'
import { getCurrentActiveTab, getSender, getTabId } from '../utils'

export class MonetizationService {
Expand Down Expand Up @@ -100,6 +101,7 @@ export class MonetizationService {
async toggleWM() {
const { enabled } = await this.storage.get(['enabled'])
await this.storage.set({ enabled: !enabled })
emitToggleWM({ enabled: !enabled })
}

clearTabSessions(tabId: number) {
Expand Down
6 changes: 6 additions & 0 deletions src/background/services/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export class StorageService extends EventEmitter {
return { ...data, url }
}

async getWMState(): Promise<boolean> {
const { enabled } = await this.get(['enabled'])

return enabled
}

async keyPairExists(): Promise<boolean> {
const keys = await this.get(['privateKey', 'publicKey', 'keyId'])
if (
Expand Down
74 changes: 67 additions & 7 deletions src/background/services/tabEvents.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,76 @@
import { type Browser, runtime } from 'webextension-polyfill'
import { MonetizationService } from './monetization'
import { StorageService } from './storage'
import { IsTabMonetizedPayload } from '@/shared/messages'

export class TabEvents {
constructor(private monetizationService: MonetizationService) {}

onRemovedTab = (tabId: number) => {
this.monetizationService.clearTabSessions(tabId)
}
const icon34 = runtime.getURL('assets/icons/icon-34.png')
const icon128 = runtime.getURL('assets/icons/icon-128.png')
const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png')
const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png')
const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png')
const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png')
const iconWarning34 = runtime.getURL('assets/icons/icon-warning-34.png')
const iconWarning128 = runtime.getURL('assets/icons/icon-warning-128.png')

export class TabEvents {
constructor(
private monetizationService: MonetizationService,
private storage: StorageService,
private browser: Browser
) {}
// TODO: This is not ideal. Find a better way to clear the sessions for a specific tab.
// When closing the tab, we receive the STOP_MONETIZATION message as well.
// Maybe check if the tab is closed in the content script?
onUpdatedTab = (tabId: number) => {
onRemovedTab = (tabId: number) => {
this.monetizationService.clearTabSessions(tabId)
}

private changeIcon = async () => {
const { enabled } = await this.storage.get(['enabled'])

const iconData = {
'34': enabled ? icon34 : iconWarning34,
'128': enabled ? icon128 : iconWarning128
}

if (this.browser.action) {
await this.browser.action.setIcon({ path: iconData })
} else if (chrome.browserAction) {
chrome.browserAction.setIcon({ path: iconData })
}
}

onActivatedTab = async () => {
await this.changeIcon()
}

onCreatedTab = async () => {
await this.changeIcon()
}

onUpdatedTab = async (payload?: IsTabMonetizedPayload) => {
const { enabled } = await this.storage.get(['enabled'])

let iconData = {
'34': enabled ? icon34 : iconWarning34,
'128': enabled ? icon34 : iconWarning128
}

if (enabled) {
if (payload) {
const { value } = payload

iconData = {
'34': value ? iconActive34 : iconInactive34,
'128': value ? iconActive128 : iconInactive128
}
}
}

if (this.browser.action) {
await this.browser.action.setIcon({ path: iconData })
} else if (chrome.browserAction) {
chrome.browserAction.setIcon({ path: iconData })
}
}
}
20 changes: 1 addition & 19 deletions src/background/utils.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,8 @@
import { WalletAmount } from '@/shared/types'
import { type Browser, action, runtime, Runtime } from 'webextension-polyfill'
import { type Browser, Runtime } from 'webextension-polyfill'
import { DEFAULT_SCALE, EXCHANGE_RATES_URL } from './config'
import { notNullOrUndef } from '@/shared/helpers'

const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png')
const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png')
const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png')
const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png')

export const updateIcon = async (active: boolean) => {
const iconData = {
'34': active ? iconActive34 : iconInactive34,
'128': active ? iconActive128 : iconInactive128
}

if (action) {
await action.setIcon({ path: iconData })
} else if (chrome.browserAction) {
chrome.browserAction.setIcon({ path: iconData })
}
}

export const getCurrentActiveTab = async (browser: Browser) => {
const activeTabs = await browser.tabs.query({
active: true,
Expand Down
15 changes: 15 additions & 0 deletions src/content/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,18 @@ export const resumeMonetization = async (
payload
})
}

export const isTabMonetized = async (
payload: ContentToBackgroundActionPayload[ContentToBackgroundAction.IS_TAB_MONETIZED]
) => {
return await message.send({
action: ContentToBackgroundAction.IS_TAB_MONETIZED,
payload
})
}

export const isWMEnabled = async () => {
return await message.send<boolean>({
action: ContentToBackgroundAction.IS_WM_ENABLED
})
}
5 changes: 5 additions & 0 deletions src/content/services/contentScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export class ContentScript {
)
return

case BackgroundToContentAction.EMIT_TOGGLE_WM:
this.monetizationTagManager.toggleWM(message.payload)

return

default:
return
}
Expand Down
73 changes: 62 additions & 11 deletions src/content/services/monetizationTagManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import { WalletAddress } from '@interledger/open-payments/dist/types'
import { checkWalletAddressUrlFormat } from '../utils'
import {
checkWalletAddressUrlCall,
isTabMonetized,
isWMEnabled,
resumeMonetization,
startMonetization,
stopMonetization
} from '../lib/messages'
import { MonetizationEventPayload } from '@/shared/messages'
import {
EmitToggleWMPayload,
MonetizationEventPayload
} from '@/shared/messages'

export type MonetizationTag = HTMLLinkElement

Expand All @@ -23,6 +28,7 @@ interface FireOnMonetizationChangeIfHaveAttributeParams {
export class MonetizationTagManager extends EventEmitter {
private documentObserver: MutationObserver
private monetizationTagAttrObserver: MutationObserver
private iconUpdated: boolean

private monetizationTags = new Map<MonetizationTag, MonetizationTagDetails>()

Expand All @@ -38,10 +44,10 @@ export class MonetizationTagManager extends EventEmitter {
this.onMonetizationTagAttrsChange(records)
)

document.addEventListener('visibilitychange', () => {
document.addEventListener('visibilitychange', async () => {
document.visibilityState === 'visible'
? this.resumeAllMonetization()
: this.stopAllMonetization()
? await this.resumeAllMonetization()
: await this.stopAllMonetization()
})
}

Expand All @@ -59,11 +65,21 @@ export class MonetizationTagManager extends EventEmitter {
return
}

private resumeAllMonetization() {
this.monetizationTags.forEach((value) => {
if (value.requestId && value.walletAddress)
resumeMonetization({ requestId: value.requestId })
})
private async resumeAllMonetization() {
const response = await isWMEnabled()

if (response.success && response.payload) {
let validTagsCount = 0

this.monetizationTags.forEach((value) => {
if (value.requestId && value.walletAddress) {
resumeMonetization({ requestId: value.requestId })
++validTagsCount
}
})

isTabMonetized({ value: validTagsCount > 0 })
}
}

private stopAllMonetization() {
Expand All @@ -74,6 +90,8 @@ export class MonetizationTagManager extends EventEmitter {
}

private onWholeDocumentObserved(records: MutationRecord[]) {
this.iconUpdated = false

this.logger.info('document mutation records.length=', records.length)

for (const record of records) {
Expand All @@ -94,6 +112,8 @@ export class MonetizationTagManager extends EventEmitter {
}

async onMonetizationTagAttrsChange(records: MutationRecord[]) {
this.iconUpdated = false

const handledTags = new Set<Node>()

// Check for a non specified link with the type now specified and
Expand Down Expand Up @@ -185,7 +205,6 @@ export class MonetizationTagManager extends EventEmitter {

if (!wasDisabled) {
this.onRemovedTag(tag)
stopMonetization({ requestId })
}

this.onAddedTag(tag, requestId)
Expand Down Expand Up @@ -243,6 +262,8 @@ export class MonetizationTagManager extends EventEmitter {
}

private run() {
this.iconUpdated = false

const monetizationTags: NodeListOf<MonetizationTag> =
this.document.querySelectorAll('link')

Expand All @@ -255,6 +276,8 @@ export class MonetizationTagManager extends EventEmitter {
}
})

this.checkIsTabMonetized()

const onMonetizations: NodeListOf<HTMLElement> =
this.document.querySelectorAll('[onmonetization]')

Expand All @@ -280,6 +303,19 @@ export class MonetizationTagManager extends EventEmitter {
const { requestId } = this.getTagDetails(tag, 'onRemovedTag')
this.monetizationTags.delete(tag)
stopMonetization({ requestId })

// Check if tab still monetized
this.checkIsTabMonetized()
}

private checkIsTabMonetized() {
let validTagsCount = 0

this.monetizationTags.forEach((value) => {
if (value.requestId && value.walletAddress) ++validTagsCount
})

isTabMonetized({ value: validTagsCount > 0 })
}

// Add tag to list & start monetization
Expand All @@ -294,7 +330,14 @@ export class MonetizationTagManager extends EventEmitter {

this.monetizationTags.set(tag, details)

if (walletAddress) startMonetization({ requestId, walletAddress })
if (walletAddress) {
startMonetization({ requestId, walletAddress })

if (!this.iconUpdated) {
isTabMonetized({ value: true })
this.iconUpdated = true
}
}
}

// Check tag to be enabled and for valid wallet address
Expand Down Expand Up @@ -348,4 +391,12 @@ export class MonetizationTagManager extends EventEmitter {
return null
}
}

async toggleWM({ enabled }: EmitToggleWMPayload) {
if (enabled) {
await this.resumeAllMonetization()
} else {
await this.stopAllMonetization()
}
}
}

0 comments on commit a5364ef

Please sign in to comment.