Build a Medium UI clone with Svelte, Materialize and Web Monetization API
Tags: gftwhackathon, webmonetization, showdev, javascript
Published: 2020-05-12 23:33:06

In this post, I'll be taking you through how I designed a simple Medium-like UI that utilizes our [svelte-monetization](https://dev.to/wobsoriano/svelte-monetization-a-minimal-and-lightweight-wrapper-for-the-web-monetization-api-1kld) component.

Demo: https://mediocre.now.sh

## Setting up the application

I built the app using Svelte. I made use of [materializecss](https://materializecss.com/) for the responsive design and [svelte-monetization](https://dev.to/wobsoriano/svelte-monetization-a-minimal-and-lightweight-wrapper-for-the-web-monetization-api-1kld) for showing/hiding ads and remove image watermarks.

Open up your terminal and run the following to generate a new svelte app.

```bash
npx degit sveltejs/template medium-clone
```

Open `App.svelte` and add the `<svelte:head>` element together with your [payment pointer](https://webmonetization.org/docs/getting-started).

```html
<script>
...
</script>

<svelte:head>
  <meta
    name="monetization"
    content="$coil.xrptipbot.com/701298d5-481d-40ff-9945-336671ab2c42" />
</svelte:head>
```

Next, install the following dependencies.

```bash
npm install materialize-css rollup-plugin-css-only svelte-monetization watermarkjs
```

### Setup materialize and rollup

In `rollup.config.js`, configure the plugin by importing it `import css from 'rollup-plugin-css-only'` and add the `css({output: "public/build/base.css"})` to the list of plugins.

Now we can import `.css` files in `src/main.js`:

```javascript
import '../node_modules/materialize-css/dist/css/materialize.css'
```

Don't forget to update `public/index.html` to include the generated `base.css` instead of `global.css`:

```html
- <link rel='stylesheet' href='/global.css'>
+ <link rel='stylesheet' href='/build/base.css'>
```

## Components

Inside the `src` folder, add a `components` folder and create 3 files - `Nav.svelte`, `Footer.svelte`, and `Advertisement.svelte`.

Our `Nav.svelte` component is a simple Navbar with a title.

```html
<nav class="white" role="navigation">
  <div class="nav-wrapper container">
    <a href="/" class="brand-logo black-text">Mediocre</a>
  </div>
</nav>
```

Next, the `Footer.svelte` component that contains dummy text.

```html
<footer class="page-footer grey darken-4">
  <div class="container">
    <div class="row">
      <div class="col l4 s12">
        <h5 class="white-text">Discover Mediocre</h5>
        <p class="grey-text text-lighten-4">
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Eius
          architecto dolores voluptatum ipsa aliquid fuga asperiores vitae
          veniam laudantium non!
        </p>
      </div>
      <div class="col l4 s12">
        <h5 class="white-text">Make Mediocre yours</h5>
        <p class="grey-text text-lighten-4">
          Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ad velit
          molestiae excepturi error est aperiam?
        </p>
      </div>
      <div class="col l4 s12">
        <h5 class="white-text">Become a member</h5>
        <p class="grey-text text-lighten-4">
          Lorem ipsum dolor sit amet consectetur, adipisicing elit. Reiciendis
          officia consequatur repellendus dolores voluptate commodi.
        </p>
      </div>
    </div>
  </div>
  <div class="footer-copyright">
    <div class="container">Mediocre</div>
  </div>
</footer>
```

And lastly, the `Advertisement` component which should appear if the user is not web monetized.

```html
<div class="card">
  <div class="card-content">
    <span class="card-title">Get unlimited access</span>
    <p>Become a member to keep reading.</p><br />
    <a href="https://coil.com/" class="waves-effect waves-light btn">
      VISIT COIL
    </a><br /><br />
    <p>Here's what you get when you upgrade to membership:</p><br />
    <p>
      <span style="font-weight: bold;">Unlimited access.</span>
      Explore Mediocre's library filled with everything you're curious about.
    </p><br />
    <p>
      <span style="font-weight: bold;">No ads.</span>
      There are zero ads on Mediocre and we don't sell your data.
    </p><br />
    <p>
      <span style="font-weight: bold;">Reward quality writing.</span>
      When you spend time reading a story, you make the author happy.
    </p><br />
    <p>
      "I love Mediocre's membership — it gives me access to the stories I love
      by the writers I love, and it allows me to help support those writers
      financially."
    </p>
    <p style="font-weight: bold;">—John Doe, Mediocre member</p>
  </div>
</div>

```

## Using `svelte-monetization`

Inside our `App.svelte`, let's import our components and add dummy content.

```html
<script>
  import Nav from "./components/Nav.svelte";
  import Footer from "./components/Footer.svelte";
  import Advertisement from "./components/Advertisement.svelte";
  let imageUrl = "https://i.imgur.com/tMxofag.png";
</script>

<!-- Nav -->
<Nav />
<!-- Main -->
<main>
  <div class="container">
    <div class="section">
      <h4>Lorem ipsum dolor sit amet, consectetur adipisicing</h4>
      <p>Oct 1, 2020 · 11 min read</p>
      <div style="text-align: center;">
        <img src={imageUrl} alt="banner" class="responsive-img" />
      </div>
     <p>
        Lorem ipsum dolor sit amet...
    </p>
    <p>
        Lorem ipsum dolor sit amet...
    </p>
    <Advertisement />
    </div>
  </div>
</main>
<!-- Footer -->
<Footer />
```

Our app then will look like this.

![Styling our page](https://i.imgur.com/TdzBEQ8.png)

Now the next thing that we want is to hide the `Advertisement` component and show full content of the article when the user is web monetized. We can now import `svelte-monetization`.

We will use Svelte's `each` block to loop over our dummy text.

```html
<script>
  import Nav from "./components/Nav.svelte";
  import Footer from "./components/Footer.svelte";
  import Advertisement from "./components/Advertisement.svelte";
  import SvelteMonetization from "svelte-monetization";
  let imageUrl = "https://i.imgur.com/tMxofag.png";
</script>

<!-- Nav -->
<Nav />
<!-- Main -->
<main>
  <div class="container">
    <div class="section">
      <h4>Lorem ipsum dolor sit amet, consectetur adipisicing</h4>
      <p>Oct 1, 2020 · 11 min read</p>
      <div style="text-align: center;">
        <img src={imageUrl} alt="banner" class="responsive-img" />
      </div>
      <SvelteMonetization let:isLoading let:isMonetized>
        {#if isLoading}
          <div>Loading...</div>
        {:else if isMonetized}
	       {#each [...Array(10).keys()] as item}
		   <p>Lorem ipsum dolor sit amet...</p>
	       {/each}
        {:else}
	       {#each [...Array(2).keys()] as item}
		   <p>Lorem ipsum dolor sit amet...</p>
	       {/each}
	       <Advertisement />
        {/if}
      </SvelteMonetization>
    </div>
  </div>
</main>
<!-- Footer -->
<Footer />
```

Output:

![Image 2](https://i.imgur.com/FeZWD9c.gif)

To test this, make sure you have the Coil extension installed and the Simulate Monetization bookmarklet added to your bookmarks. Steps can be seen [here](https://testwebmonetization.com/).

What if we want to add watermarks to our image? And remove it when user is web monetized? Good thing there's a watermarking library named [watermarkjs](https://github.com/brianium/watermarkjs) and we can apply it to our app.

Let's import it inside `App.svelte` and do the code below.

```html
<script>
  import { onMount } from "svelte";
  import Nav from "./components/Nav.svelte";
  import Footer from "./components/Footer.svelte";
  import Advertisement from "./components/Advertisement.svelte";
  import SvelteMonetization from "svelte-monetization";
  import watermark from "watermarkjs";
  const imageUrl = "https://i.imgur.com/tMxofag.png";
  let src = imageUrl;

  const addWatermark = async () => {
    const text = watermark.text.center(
      "I'm a watermark",
      "38px serif",
      "#fff",
      0.5
    );
    const options = {
      init(img) {
        img.crossOrigin = "anonymous";
      }
    };
    const img = await watermark([imageUrl], options).image(text);
    src = img.src;
  };

  onMount(() => {
    addWatermark();
  });
</script>

<!-- Nav -->
<Nav />
<!-- Main -->
<main>
  <div class="container">
    <div class="section">
      <h4>Lorem ipsum dolor sit amet, consectetur adipisicing</h4>
      <p>Oct 1, 2020 · 11 min read</p>
      <div style="text-align: center;">
        <img {src} alt="banner" class="responsive-img" />
      </div>
      <!-- Use the start event to track monetizationstart event -->
      <SvelteMonetization 
        let:isLoading 
        let:isMonetized 
        on:start={() => (src = imageUrl)}>
        <!-- Rest of our code -->
      </SvelteMonetization>
    </div>
  </div>
</main>
<!-- Footer -->
<Footer />
```

We created a function called `addWatermark` to add a watermark to our image on load. When the `start` event is dispatched, meaning the user is web monetized, the watermark is removed. Our app will finally look like this.

![Image 3](https://i.imgur.com/v8wIcH1.gif)

Full code: https://github.com/sorxrob/svelte-monetization/tree/master/example

## Conclusion

There you have it, if you have followed this tutorial to this point, you should have your own Medium-like UI with Web Monetization working. The Web Monetization API will be very helpful to content creators.

If you found this post useful, please add a ❤️ & 🦄 both here and in my entry post.