The Interledger Community 🌱

Ciaran for Interruptor

Posted on • Edited on

Web Monetization on Interruptor is live! 🎉

It took us a little while to get our Uphold account set up and approved, but once we managed to get our payment pointer, integrating Web Monetization was pretty easy: just adding the <meta> tag with our payment pointer to each of our pages.

<meta name="monetization" content="$ilp.uphold.com/nzj2HrUbBDKa" />
Enter fullscreen mode Exit fullscreen mode

We're not looking to put Interruptor's content behind a pay wall, so we'll be working to create extra features for people supporting us through Web Monetization as a way to say thanks.

In the meantime, we've set up the site to actually say "thanks" when someone uses the site with Web Monetization activated.

Our site is made with TypeScript, React and NextJS, so we made a useWebMonetizationState hook to check when WM is activated:

import { useEffect } from 'react'
import {
  MonetizationEventMap,
  MonetizationEventType,
  MonetizationObject,
  MonetizationState,
} from '@webmonetization/types'
import { TEventListener } from '@webmonetization/types/build/genericEventListeners'
import { atom, useAtom } from 'jotai'

/**
 * Add Web Monetization to the global types
 */
declare global {
  interface Document {
    monetization?: MonetizationObject
  }
}

/**
 * Add the event listener and return a function for removing it later
 */
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)
}

/**
 * Create the atom where we'll store the current monetization state
 */
const webMonetizationStateAtom = atom<MonetizationState>('pending')

export const useWebMonetizationState = (): MonetizationState => {
  const [state, setState] = useAtom(webMonetizationStateAtom)

  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 state
}
Enter fullscreen mode Exit fullscreen mode

For now, since the only thing we're checking is whether it's started, we created a separate hook for that:

export const useHasWebMonetizationStarted = (): boolean => {
  const state = useWebMonetizationState()

  return state === 'started'
}
Enter fullscreen mode Exit fullscreen mode

We then use that hook in a component so that we can show the thank-you message when payments are being sent.

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

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

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

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

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

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

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

  return null
}
Enter fullscreen mode Exit fullscreen mode

It might get refactored a bit in the future, but for now it's working, which is the most important thing!

Top comments (0)