In this post, I'll be taking you through how I designed a simple Medium-like UI that utilizes our svelte-monetization component.
Demo: https://mediocre.now.sh
Setting up the application
I built the app using Svelte. I made use of materializecss for the responsive design and svelte-monetization for showing/hiding ads and remove image watermarks.
Open up your terminal and run the following to generate a new svelte app.
npx degit sveltejs/template medium-clone
Open App.svelte
and add the <svelte:head>
element together with your payment pointer.
<script>
...
</script>
<svelte:head>
<meta
name="monetization"
content="$coil.xrptipbot.com/701298d5-481d-40ff-9945-336671ab2c42" />
</svelte:head>
Next, install the following dependencies.
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
:
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
:
- <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.
<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.
<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.
<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.
<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.
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.
<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:
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.
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 and we can apply it to our app.
Let's import it inside App.svelte
and do the code below.
<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.
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.
Top comments (5)
Could you also build an annoying pop ups feature? It's not really a Medium clone without that
I have not experienced any of this pop ups. Could you point me to a sample?
medium.com/@nikitonsky/medium-is-a...
Thanks! To implement something like that you can use Materializecss Modals.
Something like this:
The code just display components, did you implement editor?