Interruptortecnologia
Botões de "on" e "off"
Fotografia por Justus Menke via Unsplash

Web Monetization: Parte 2 - Verificar atividade

Por Ciaran Edwards(Traduzido por Rute Correia)

Depois de configurar o apontador de pagamentos, a próxima coisa a fazer é verificar se estamos a receber pagamentos através de Web Monetization e responder a isso, usando a API de JavaScript.

Apesar de bastar algo como document.monetization === 'started' para fazer essa verificação, se estivermos a usar uma framework como React, convém que a página atualize sempre que o processo de pagamento inicie ou termine.

💡 O nosso sítio web está construído em React, pelo que as amostras de código usam React. Os princípios são bastante simples, contudo. Por isso, não deverá ser demasiado difícil de afinar o código para que encaixe em qualquer framework que esteja a ser usada.

Passo 0: Avisar o TypeScript do Web Monetization

Caso esteja a ser usado TypeScript, é preciso avisá-lo que o document agora tem uma propriedade, monetization. Ou, mais especificamente, que pode ter uma propriedade monetization. Felizmente para nós, a Coil criou um pacote com todos os tipos necessários à implementação do Web Monetization. Vamos instalá-lo:

npm install --save-dev @webmonetization/types

Assim, podemos adicionar a propriedade monetization à interface global do Document interface, deste modo:

import { MonetizationObject } from '@webmonetization/types'

declare global {
  interface Document {
    monetization?: MonetizationObject
  }
}

Agora, o TypeScript já sabe que existe Web Monetization! 🎉

Passo 1: Adicionar ouvintes de eventos

Para descobrir se há eventos de monetização a acontecer, pode-se usar document.monetization.addEventListener.

document.monetization.addEventListener(eventName, (event) => {
  doSomeStuff(event)
})

No entanto, é preciso ter em atenção duas coisas:

  1. Antes de tentarmos adicionar ouvintes de eventos, temos sempre de verificar se existe document.monetization para podermos adicioná-los.
  2. Devemos certificar-nos que removemos o ouvinte do evento, quando já não estamos a usá-lo.

Então, vamos criar uma função que resolva ambas as questões:

import {
  MonetizationEventMap,
  MonetizationEventType,
} from '@webmonetization/types'
import { TEventListener } from '@webmonetization/types/build/genericEventListeners'

/**
 * Isto adiciona um ouvinte do evento de monetização e devolve uma função
 * que podemos chamar para removê-lo mais tarde
 */
const addEventListener = <TEvent extends MonetizationEventType>(
  event: TEvent,
  eventListener: TEventListener<MonetizationEventMap[TEvent]>,
): (() => void) => {
  document.monetization?.addEventListener(event, eventListener, {
    passive: true,
  })

  return () => document.monetization?.removeEventListener(event, eventListener)
}

Isto vai dar jeito quando adicionarmos ouvintes de eventos com useEffect – podemos adicionar um ouvinte e depois removê-lo com uma função de limpeza:

useEffect(() => {
  const removeListener = addEventListener('monetizationprogress', (event) => {
    console.log('Check out all this cash money 💸', event.detail)
  })

  return () => {
    removeEventListener()
  }
}, [])

Passo 2: Armazenar o estado atual do Web Monetization num estado global

Agora que já sabemos verificar quando estamos a receber pagamentos, queremos que os componentes do nosso sítio web reajam sempre que os pagamentos comecem ou parem. Para este exemplo, vamos usar a Context API do React.

Primeiro, criamos o contexto:

import React from 'react'
import { MonetizationState } from '@webmonetization/types'

type WebMonetizationContextValue = {
  state: MonetizationState // "pending" | "started" | "stopped"
}

const WebMonetizationContext = React.createContext<
  WebMonetizationContextValue | undefined
>(undefined)

Inicializar o contexto com undefined é um pequeno truque que uso para ter a certeza que não tento aceder ao WebMonetizationContext acidentalmente, fora do Provider. Se definires um valor padrão ao usar o React Context, podes esquecer-te de encapsular a tua aplicação com o componente Provider sem nunca te aperceberes. Já me aconteceu ser apanhado em coisas assim algumas vezes, por isso permito que o valor esteja undefined, e depois crio um hook que dispara um erro se o valor do contexto estiver em falta.

export const useWebMonetization = (): WebMonetizationContextValue => {
  const webMonetizationContext = useContext(WebMonetizationContext)

  if (!webMonetizationContext) {
    throw new Error(
      `[WebMonetizationContext] You're using the context outside of the WebMonetizationProvider 😬`,
    )
  }

  return webMonetizationContext
}

O próximo passo é fazer o nosso componente personalizado WebMonetizationProvider. É aí que vamos configurar os nossos ouvintes de evento para acompanhar o estado atual de monetização, e depois definir o valor do nosso WebMonetizationContext.

export const WebMonetizationProvider: React.FC = ({ children }) => {
  const [state, setState] = useState<MonetizationState>('pending')

  useEffect(() => {
    const onStart = () => setState('started')
    const onStop = () => setState('stopped')

    const events = [
      addEventListener('monetizationstart', onStart),
      addEventListener('monetizationprogress', onStart),
      addEventListener('monetizationstop', onStop),
    ]

    return () => {
      events.forEach((removeListener) => removeListener())
    }
  }, [setState])

  return (
    <WebMonetizationContext.Provider value={{ state }}>
      {children}
    </WebMonetizationContext.Provider>
  )
}

A única coisa que falta é encapsular a aplicação com WebMonetizationProvider, e está tudo pronto para que possas começar a usar o context nos teus componentes.

Passo 3: Reagir a eventos de monetização

Agora que a nossa aplicação está configurada para acompanhar eventos de monetização, provavelmente só há mais um pormenor que nos interessa: "Está ligado ou não?"

Vamos criar mais um hook para verificar isso mesmo:

export const useIsWebMonetizationActive = (): boolean => {
  const { state } = useWebMonetization()

  return state === 'started'
}

Podes usar este hook em qualquer componente para verificar se é para mostrar conteúdo adicional, desativar anúncios ou qualquer outra coisa que queiras oferecer a visitantes que usem o protocolo Web Monetization.

No Interruptor, estamos só a começar a implementar isto. Por isso, para já, temos apenas um componente WebMonetizationToaster que mostra uma notificação a dizer "Obrigado" quando o Web Monetization está ativo.

import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'react-toastify'

import { AnalyticsEvent, trackEvent } from 'utils/analytics'
import { useIsWebMonetizationActive } from 'utils/monetization'

export const WebMonetizationToaster: React.FC = () => {
  const { t } = useTranslation('monetization')
  const isMonetizationActive = useIsWebMonetizationActive()

  useEffect(() => {
    if (!isMonetizationActive) return

    // Show the thank-you message
    toast.success(t('thanks'))
  }, [isMonetizationActive, t])

  useEffect(() => {
    if (!isMonetizationActive) return

    // Send an event to Plausible so we can see how many visitors use it
    trackEvent(AnalyticsEvent.WEB_MONETIZATION_ACTIVE)
  }, [isMonetizationActive])

  return null
}

E parece-se assim:

Screenshot of the Interruptor website showing a thank-you message

Para já, é tudo o que temos, mas vamos desenvolver novas funcionalidades nos próximos meses e escrever sobre elas aqui - por isso, não percas o próximo artigo.